Операционная система ЗОСРВ «Нейтрино» > Руководство разработчика > Основные принципы системной разработки > Запуск системы, быстрая активация устройств > Построение встраиваемых систем > Статьи руководства > Руководство по разработке начального загрузчика (IPL)



Руководство по разработке начального загрузчика (IPL)

Разработка кода, подготавливающего систему к передаче управления загрузочному образу

В этой статье:

Начальный загрузчик (IPL)
Функции начального загрузчика
Образ в линейной памяти
Образ на устройстве с переключением банков
Процессоры и конфигурации
Загрузка с устройства с переключением банков
Загрузка с устройства с линейной памятью
«Горячий» и «холодный» запуск
IPL для горячего запуска
IPL для холодного запуска
Загрузка образа
Линейное устройство
Устройство с переключением банков
ПЗУ
Загрузка по сети
Использование сервера BOOTP
Последовательный порт
Стандартный диск
Другие методы загрузки
Передача управления программе запуска (startup)
Модификация IPL
Инициализация оборудования
Загрузка образа в оперативную память
Структура загрузочного заголовка
Взаимосвязь между полями struct startup_header
Линейный загрузочный образ, выполняемый по месту хранения (XIP) в ПЗУ
Сжатый линейный загрузочный образ в ПЗУ
Образ в ПЗУ, не выполняемый по месту хранения (non-XIP)
Загрузка образа с диска или по сети (x86 BIOS)
Загрузка сжатого образа с диска / по сети
Структура начального загрузчика
Структура дерева каталогов с исходным кодом
Структура кода IPL
Пример
Разработка нового IPL

Начальный загрузчик (IPL)

В этом разделе мы подробно рассмотрим устройство начального загрузчика и его модификацию для конкретного оборудования.

Функции начального загрузчика

Первая задача начального загрузчика — минимальная настройка оборудования и создание среды, в которой будет выполняться сначала программа запуска (например, startup-bios, startup-elbrus и др.), а затем микроядро ЗОСРВ «Нейтрино». Эта задача включает в себя как минимум следующие действия:

  1. запуск выполнения с вектора сброса
  2. настройка контроллера памяти (например, линий выбора микросхем и/или контроллера PCI)
  3. настройка часов
  4. настройка стека, с помощью которого библиотека начального загрузчика верифицирует и конфигурирует ОС (скачивает, сканирует, настраивает образ ОС и передает ему управление)

Часть начального загрузчика, которая выполняет инициализацию, полностью реализована на языке ассемблера, поскольку выполняется в ПЗУ без участия контроллера памяти. После инициализации оборудования начальный загрузчик вызывает функцию main() для инициализации среды языка C.

После ее подготовки начальный загрузчик может выполнять различные задачи в зависимости от типа устройства, с которого загружается ОС (линейного или с переключением банков).

Линейное устройство
Образ целиком находится в линейном адресном пространстве процессора.
Устройство с переключением банков
Образ не полностью доступен процессору (например, он размещен на устройстве с переключением банков, диске, в сети и др.)

Под термином «устройство» обычно понимается ПЗУ, в котором хранится образ (флеш-память, EPROM, статическое ОЗУ с питанием от батареи и др.)

Образ в линейной памяти

Образ может располагаться на следующих линейных устройствах:

Образ на устройстве с переключением банков

Образ может располагаться на следующих устройствах с переключением банков:

Процессоры и конфигурации

Помимо вышеперечисленных устройств, во встраиваемой системе могут использоваться следующие процессоры и конфигурации:

Загрузка с устройства с переключением банков

Предположим, что встраиваемая система загружается с устройства с переключением банков или страничной структурой (например, со страничного флеш-накопителя, дискового/сетевого устройства и др.) и образ не сжат. Начальный загрузчик должен выполнить три основных задачи:

  1. Подключиться к устройству с помощью C-функции. Для примера мы рассмотрим скачивание образа через последовательный порт. Начальный загрузчик использует функцию image_download_8250(), которая настраивает контроллеры 8250 и управляет ими.

    После настройки контроллера функция копирует образ в оперативную память через последовательный интерфейс.

  2. Затем начальный загрузчик вызывает функцию image_scan(), которая принимает начальный и конечный адреса в качестве аргументов и возвращает адрес, по которому расположен образ:

    unsigned long image_scan( unsigned long start,
    unsigned long end );

    Функция image_scan():

  3. После обнаружения и проверки образа ОС начальный загрузчик вызывает функцию image_setup(), которая принимает адрес образа в качестве аргумента и всегда возвращает значение 0:

    int image_setup( unsigned long address );

    Функция image_setup():

    На этом этапе программа запуска находится в ОЗУ (и должна всегда выполняться в нем), а в ее заголовке указан адрес образа ОС.


    Note: Поскольку программа запуска отвечает за копирование файловой системы образа в конечную область ОЗУ, начальный загрузчик должен помещать образ в место, где программа запуска, которая не знает о страничных устройствах (последовательных, дисковых, параллельных, сетевых и др.), имеет линейный доступ к нему.

    Следует иметь в виду, что если образ сжат, начальный загрузчик может копировать сжатый образ в область, которая не пересекается с конечным местоположением образа в ОЗУ после распаковки программой запуска. Если образ находится во флеш-памяти, ПЗУ или на другом линейном накопителе, это не представляет проблему, но если образ хранится на страничном устройстве, разработчику необходимо позаботиться о том, чтобы образ в оперативной памяти не пересекался с образом, распакованным программой запуска. Действуют следующие правила:

    Несжатый образ
    Если образ не сжат, начальный загрузчик может копировать его из страничного устройства непосредственно в целевую область. Программа запуска сравнивает адреса и обнаруживает, что не требуется копировать образ.
    Сжатый образ
    Если образ сжат, программа запуска должна копировать и распаковывать его в область, не совпадающую с конечной областью образа в ОЗУ.

  4. На последнем этапе управление передается точке входа в программу запуска с помощью функции image_start():

    int image_start( unsigned long address );

    Функция image_start() не возвращает значение при успешном выполнении и возвращает -1 в случае ошибки. Она передает управление по адресу startup_vaddr заголовка программы запуска.

Загрузка с устройства с линейной памятью

В системе, которая загружается с линейного устройства (например, линейного флеш-накопителя, ПЗУ и т.д.) начальный загрузчик выполняет те же задачи, что и при загрузке со страничного устройства, с одним важным исключением: ему не требуется целиком копировать образ ОС с устройства в оперативную память.

«Горячий» и «холодный» запуск

Сложность кода начального загрузчика зависит от конфигурации встраиваемой системы. Чтобы описать различные типы начальных загрузчиков, мы воспользуемся терминами «горячий запуск» и «холодный запуск»:

IPL для горячего запуска
Если BIOS или ПЗУ-монитор уже размещены на векторе сброса, начальный загрузчик лишь расширяет их функции.
IPL для холодного запуска
В системе отсутствует (или не используется) BIOS или ПЗУ-монитор. Начальный загрузчик должен находиться на векторе сброса.

IPL для горячего запуска

Этот начальный загрузчик не начинает выполняться сразу после сброса, а принимает управление от BIOS или ПЗУ-монитора.

BIOS систем с архитектурой x86 и многие ПЗУ-мониторы поддерживают расширения. При сканировании памяти после включения питания BIOS или ПЗУ-монитор пытается обнаружить расширения в адресном пространстве. Признаком расширения является сигнатура; например, сигнатура расширения PC BIOS состоит из двух байт 0x55 и 0xAA, которые находятся в начале ПЗУ. Расширение должно быть готово принимать управление по адресу, который определяется смещением точки входа (например, точка входа в расширение PC BIOS имеет смещение 0x0003).

Следует иметь в виду, что этот метод применяется в различных ПЗУ-расширениях PC BOOTP. После того, как код ПЗУ с сигнатурой расширения получает управление, он загружает образ по сети и помещает его в оперативную память.

IPL для холодного запуска

Одно из преимуществ ЗОСРВ «Нейтрино», особенно с точки зрения себестоимости встраиваемых систем, заключается в том, что в них не является обязательным наличие BIOS или ROM-монитора. Эта статья предназначена в первую очередь для разработчиков, которые создают собственного начального загрузчика или по какой-либо причине не желают пользоваться штатным начальным загрузчиком BIOS/монитора.

Рассмотрим задачи начального загрузчика для холодного запуска.

При включении питания или сбросе процессора некоторые его регистры находятся в известном состоянии, а управление передается по определенному адресу памяти (вектору сброса).

IPL должен располагаться на векторе сброса и выполнять следующие задачи:

  1. настраивать процессор
  2. обнаруживать образ ОС
  3. копировать программу запуска в оперативную память
  4. передавать управление программе запуска

Например, в системе x86 вектор сброса расположен по адресу 0xFFFFFFF0. Устройство с начальным загрузчиком должно находиться в этом диапазоне адресов. Как правило, в системах x86 PC BIOS вектор сброса содержит инструкцию JMP c последующим переходом на код диагностики, настройки и начальной загрузки.

Загрузка образа

Независимо от используемого процессора начальный загрузчик должен загружать образ в соответствии с требованиями микроядра ЗОСРВ «Нейтрино», как описано выше. Иногда в коде начального загрузчика необходимо предусматривать возможность загрузки резервного образа (например, .altboot при загрузке с дискеты/жесткого диска) или автоматического отката при повреждении образа.

Следует иметь в виду, что трудоемкость начальной загрузки в значительной степени зависит от местоположения образа.

Еще раз обратимся к двум типам устройств хранения образов.

Линейное устройство

Это наиболее простой вариант. Образ целиком хранится в ПЗУ или на устройстве PC-Card, адресное пространство которого полностью отображено в адресное пространство процессора. Единственное, что должен сделать начальный загрузчик — скопировать код запуска в оперативную память. Этот способ идеально подходит для компактных или глубоко встраиваемых систем.

Следует обратить внимание, что в архитектуре x86 отсутствует требование адресуемости устройства в первом мегабайте памяти; программа запуска также может располагаться за его пределами.

Обратите внимание, что для полного отображения адресного пространства устройства PC-Card в адресное пространство процессора может потребоваться настройка, которую должна быть выполнена начальным загрузчиком (мы предоставляем библиотечные функции для нескольких стандартных интерфейсных микросхем PC-Card).

dirmap.png
Рисунок 1. Устройство с линейной адресацией памяти

Устройство с переключением банков

Образ хранится на устройстве, которое не отображается непосредственно в линейную память. В этом случае разработчику необходимо решить, как код начального загрузчика будет получать доступ к образу, который хранится на устройстве.

Возможны различные варианты:

Рассмотрим общие характеристики этих способов. В таких системах код начального загрузчика знает, как считывать данные из аппаратных компонентов. Начальный загрузчик выполняет следующие действия:

  1. принимает управление
  2. переносит образ из устройства в оперативную память
  3. передает управление загруженному образу

nomap.png
Рисунок 2. Устройство с переключением банков памяти

ПЗУ

Образ хранится на твердотельном накопителе (ПЗУ, EPROM, флеш-память), но процессор видит только небольшую часть содержимого устройства. Это устройство имеет небольшое окно, которое отображается в адресное пространство процессора (например, 32 Кбайт), а дополнительные аппаратные регистры определяют, какая часть устройства отображена в этом окне.

smallipl.png
Рисунок 3. Накопитель с переключаемыми банками, отображаемыми в окне

Чтобы загрузить образ, начальный загрузчик должен знать, как управлять блоком отображения окон. Затем начальный загрузчик копирует образ из окна в оперативную память и передает ему управление.


Note: По возможности следует избегать применения блоков отображения памяти (как собственной разработки, так и «стандартных»), поскольку они усложняют программно-аппаратную структуру встраиваемой системы. Мы настоятельно рекомендуем пользоваться линейными устройствами (дополнительную информацию см. в статье Рекомендации по проектированию).

Загрузка по сети

В зависимости от особенностей встраиваемой системы или процесса ее разработки можно загружать образ по сети через интерфейс Ethernet. ПЗУ-монитор некоторых встраиваемых плат содержит в себе код BOOTP. На ПК с сетевой платой ISA или PCI загрузочное ПЗУ помещается в адресное пространство процессора, где BIOS компьютера передает ему управление. Код BOOTP взаимодействует с сетевым оборудованием и принимает образ от удаленной системы.

Использование сервера BOOTP

Чтобы загрузить ЗОСРВ «Нейтрино» с помощью BOOTP, необходимы ПЗУ с BOOTP для клиентской части (ОС) и сервер BOOTP (например, bootpd). Поскольку протокол TFTP используется для передачи образа от сервера клиенту, также потребуется сервер TFTP, который поставляется вместе с сервером BOOTP в большинстве операционных систем («Нейтрино», UNIX, Windows 95/98/NT/...).

Последовательный порт

Последовательный порт может быть полезен в процессе разработки целевой системы для скачивания ее образа или обеспечения отказоустойчивости (например, можно повторно скачивать образ через последовательный порт при несовпадении контрольной суммы).

Можно встраивать код загрузки образа через последовательный интерфейс в начального загрузчика, чтобы он мог скачивать образ с внешнего устройства. Как правило, это не приводит к заметному увеличению себестоимости встраиваемой системы; в большинстве случаев можно исключать последовательный порт из окончательной конфигурации. Большинство отладочных плат, изготавливаемых производителями микросхем, оснащены последовательными портами. Мы предоставляем исходный код встраиваемого загрузчика через последовательный порт с микросхемой 8250.

Процесс начальной загрузки системы почти идентичен описанному выше процессу загрузки по сети, однако для скачивания образа используется последовательный порт.

Стандартный диск

Для загрузки традиционной встраиваемой системы, которая схожа с ПК и оснащена BIOS, легче всего использовать диск. BIOS берет на себя всю необходимую работу — считывает образ с диска, помещает его в ОЗУ и передает ему управление.

С другой стороны, при отсутствии BIOS самостоятельно реализовать этот метод загрузки наиболее сложно, поскольку требуется драйвер для доступа к диску (стандартному жесткому диску с вращающимся накопителем или твердотельному диску). Начальный загрузчик должен считать таблицу разделов устройства, определить, где находится содержимое образа, а затем отобразить его фрагменты в окно и передать байты в ОЗУ (если используется твердотельный диск) либо считать байты данных с дискового носителя.

Другие методы загрузки

Если ни один из вышеперечисленных методов загрузки не подходит для разрабатываемой встраиваемой системы, необходимо написать начального загрузчика, который выполняет рассмотренные выше основные действия — обрабатывает вектор сброса, считывает образ с какого-либо накопителя и передает управление программе запуска.

Передача управления программе запуска (startup)

Когда образ загружен в оперативную память или готов к выполнению в ПЗУ, необходимо передавать управление коду запуска (который скопирован из образа в ОЗУ).

Подробную информацию о различных типах программ запуска см. в статье Руководство по разработке модуля startup.

После передачи управления коду запуска начальный загрузчик завершается.

Модификация IPL

В этом разделе подробно рассматриваются этапы создания начального загрузчика встраиваемой системы, который загружается из ПЗУ или флеш-памяти.

Системы, которые загружаются с диска или по сети, обычно оснащены BIOS или ПЗУ-монитором, в которых уже реализована значительная часть функций начального загрузчика. Если разрабатываемая встраиваемая система относится к этой категории, можно пропустить эту главу и перейти к статье Руководство по разработке модуля startup.

IPL получает управление в момент сброса и выполняет следующие основные функции:

  1. инициализация оборудования (посредством ассемблерного кода)
  2. загрузка образа в ОЗУ (например, через последовательный порт с помощью функции image_download_8250())
  3. обнаружение образа ОС (с помощью функции image_scan())
  4. копирование программы запуска (с помощью функции image_setup())
  5. передача управления загруженному образу (с помощью функции image_start())

Инициализация оборудования

На этом этапе выполняется базовая инициализация оборудования, в том числе получение доступа к оперативной памяти системы, которая может быть недоступной после сброса. Трудоемкость инициализации зависит от действий, которые были выполнены до передачи управления загрузчику. В одних системах этот код активизируется после сброса и решает весь объем задач, а в других — вызывается более компактным загрузчиком, который выполняет часть необходимой работы.

Следует иметь в виду, что на этом этапе не обязательно инициализировать стандартные периферийные устройства (например, скорость работы последовательных портов). Эту задачу выполняют драйверы ОС, которые запускаются позже. Достаточно инициализировать только оборудование, которое позволяет передавать управление программе запуска в образе.

Программа запуска написана на языке C и доступна вместе со всеми исходными кодами. Структура кода запуска позволяет с легкостью модифицировать его и добавлять в него новые инициализации (например, настраивать структуру системной страницы в оперативной памяти).

Загрузка образа в оперативную память

Код начального загрузчика должен обнаруживать загрузочный образ, который сгенерирован утилитой mkifs, и частично или полностью копировать его в память.

Загрузчик использует информацию в заголовке образа для копирования заголовка и модуля startup-* в оперативную память. Если образ не находится в линейно адресуемой памяти целиком, загрузчик должен помещать его в ОЗУ.

Структура загрузочного заголовка

Структура загрузочного заголовка struct startup_header определена в файле <sys/startup.h>. Она имеет размер 256 байт и содержит следующие поля, которые считываются начальным загрузчиком и/или программой запуска:

Корректность загрузочного образа проверяется путем вычисления контрольной суммы всего образа с помощью функции checksum():

checksum( image_paddr, startup_size );
checksum( image_paddr + startup_size, stored_size - startup_size );

Взаимосвязь между полями struct startup_header

В этом разделе описаны некоторые поля, которые используются начальным загрузчиком и программой запуска при различных типах загрузки ЗОСРВ «Нейтрино». Эти поля заполняются утилитой mkifs.

Ранее мы рассмотрели, какие действия выполняются начальным загрузчиком и программой запуска.

Линейный загрузочный образ, выполняемый по месту хранения (XIP) в ПЗУ

На этом рисунке показан образ, выполняемый по месту хранения (XIP, eXecute In Place):

lromxip.png
Рисунок 4. Линейный XIP-образ


Note: В следующих примерах псевдокода поле image_paddr соответствует исходному местоположению образа в линейном ПЗУ, а поле ram_paddr — целевому местоположению образа в ОЗУ.

Ниже перечислены действия начального загрузчика:

checksum( image_paddr, startup_size );
checksum( image_paddr + startup_size, stored_size - startup_size );
copy( image_paddr, ram_paddr, startup_size );
jump( startup_vaddr );

Сжатый линейный загрузочный образ в ПЗУ

Этот сценарий аналогичен предыдущему, но со сжатым образом:

lromcomp.png
Рисунок 5. Линейный сжатый образ

Ниже перечислены действия начального загрузчика:

checksum( image_paddr, startup_size );
checksum( image_paddr + startup_size, stored_size - startup_size );
copy( image_paddr, ram_paddr, startup_size );
jump( startup_vaddr );

В программе запуска необходимо выполнить следующее действие:

uncompress( ram_paddr + startup_size, image_paddr + startup_size,
stored_size - startup_size );

Образ в ПЗУ, не выполняемый по месту хранения (non-XIP)

В этом сценарии образ не выполняется по месту хранения:

romnxip.png
Рисунок 6. non-XIP-образ

Ниже перечислены действия IPL:

checksum( image_paddr, startup_size );
checksum( image_paddr + startup_size, stored_size - startup_size );
copy( image_paddr, ram_paddr, startup_size );
jump( startup_vaddr );

В программе запуска необходимо выполнить следующее действие:

copy( ram_paddr + startup_size, image_paddr + startup_size,
stored_size - startup_size );

Загрузка образа с диска или по сети (x86 BIOS)

В этом сценарии мы не создаем полнофункционального начального загрузчика. Начальный загрузчик BIOS помещает образ в память и передает управление нашему начальному загрузчику. Поскольку существующий начальный загрузчик не знает, где располагается программа запуска, он всегда передает управление в начало образа. Мы помещаем в начало образа небольшого начального загрузчика, который передает управление по адресу startup_vaddr:

disknet.png
Рисунок 7. Сетевой/дисковый образ

Начальный загрузчик выполняет следующее действие:

jump( startup_vaddr );

Загрузка сжатого образа с диска / по сети

Этот сценарий аналогичен предыдущему, но нам необходимо распаковать образ в программе запуска:

disknetc.png
Рисунок 8. Сжатый сетевой/дисковый образ

Программа запуска выполняет следующее действие:

uncompress( ram_paddr + startup_size, image_paddr + startup_size,
stored_size - startup_size );

Загрузка с устройства с переключением банков очень похожа на загрузку с диска / по сети, но в начальном загрузчике необходимо написать код, который копирует образ в ОЗУ:

bankcopy( image_paddr, ram_paddr, startup_size );
checksum( image_paddr, startup_size );
checksum( image_paddr + startup_size, stored_size - startup_size );
jump( startup_vaddr );

Дальнейшие действия сводятся к описанному ранее сценарию загрузки с диска / по сети сжатого или несжатого образа.

При работе с устройством с переключением банков необходимо отображать физические адреса и размеры соответствующим образом. Желаем успехов и больше не занимайтесь переключением банков ПЗУ! Работайте в линейном адресном пространстве.

Структура начального загрузчика

В этом разделе мы рассмотрим структуру дерева каталогов исходного кода начального загрузчика, а также типового файла с исходным кодом.

Структура дерева каталогов с исходным кодом

Дерево каталогов с исходным кодом IPL имеет следующую структуру:

рабочий_каталог_bsp/src/hardware/ |--> ipl/ | `--> boards/ | |--> xzynq/ | |--> rockchip/ | |--> imx6x/ | |--> imx8x/ | |--> orangepi/ | |--> p3041/ | |--> p5040/ | `--> ... | |--> startup/ `--> flash/

В каталоге рабочий_каталог_bsp/src/hardware/ipl/boards хранятся исходные коды начальных загрузчиков для конкретных плат (например, каталог рабочий_каталог_bsp/src/hardware/ipl/boards/xzynq содержит исходный код для материнской платы Xilinx Zynq UltraScale+ MPSoC на процессоре ARMv8 Cortex-A53 с архитектурой AArch6)).

Структура кода IPL

Код начального загрузчика состоит из двух уровней. Первый уровень написан на языке ассемблера и предназначен для подготовки среды выполнения кода второго уровня, написанного на языке C. Обычно на первом уровне настраиваются контроллеры DRAM, инициализируются различные регистры и линии выбора микросхем для обращения к периферийным устройствам.

Обычно ассемблерный код начального загрузчика находится в файле, имя которого начинается с init (например, init8xx.s для платы MPC8xxFADS); файл C всегда называется main.c.

После того, как ассемблерная программа завершает минимальный набор подготовительный действий для передачи управления C-программе, программа main() вызывает следующие функции в указанном порядке:

image_download_8250()
Эта функция скачивает образ из места его хранения. Ее можно не вызывать, если образ находится в линейной памяти (поскольку он уже «скачан»).

Если образ скачивается с нестандартного устройства, следует вызвать функцию image_download_hw(), где вместо hw указывается описательное имя устройства (например, image_download_x25()).
image_scan()
В эту функцию передаются начальный и конечный адреса области памяти, в котором выполняется поиск загрузочного образа. Если поиск успешен, функция возвращает указатель на начало образа. Область памяти, в которой выполняется поиск, может содержать несколько образов. Если один из образов имеет некорректную контрольную сумму, используется следующий образ. Если все образы имеют корректные контрольные суммы, функция анализирует заголовок программы запуска и выбирает образ с максимальным номером версии. Следует иметь в виду, что сканирование выполняется только в указанном диапазоне адресов.
image_setup()
Эта функция копирует необходимые компоненты образа в ОЗУ.
image_start()
Эта функция переходит в начало образа, который загружен в ОЗУ; затем образ передает управление программе запуска.

Пример

Рассмотрим программу main.c для платы FADS8xx:

#include "ipl.h"
unsigned int image;
int main( void )
{
/* Поскольку образ находится по адресу 0x2840000, нам не нужно вызывать функцию image_download_8250 */
image = image_scan( 0x2840000, 0x2841000 );
/* Копирование в ОЗУ программы запуска, которая выполняет все необходимые действия с образом */
image_setup( image );
/* Настройка регистра связи и переход к начальному адресу программы запуска */
image_start( image );
return (0);
}

Поскольку в этом примере мы работаем с линейно адресуемым устройством флеш-памяти, на котором находится образ, нам не нужно вызывать функцию image_download_8250().

Далее вызывается функция image_scan(), которой мы передаем очень узкий диапазон адресов для поиска образа, поскольку знаем, где он находится.

Затем вызывается функция image_setup() с адресом, полученным от функции image_scan(). Функция image_setup() копирует код запуска в ОЗУ.

В конце вызывается функция image_start(), которая передает управление программе запуска. Мы не ждем возврата из нее и используем оператор

return (0);

только для того, чтобы избежать ошибки компилятора из-за отсутствия возвращаемого значения в функции main().

Разработка нового IPL

Рекомендуется начинать разработку нового IPL с изучения уже готового загрузчика для процессора и платы, которые максимально схожи с целевыми.

Выполните следующие действия:

  1. создайте новый подкаталог в каталоге рабочий_каталог_bsp/src/hardware/ipl/boards и присвойте ему имя платы
  2. скопируйте в новый подкаталог все файлы и подкаталоги из комплекта поддержки аналогичной платы
  3. измените файлы нужным образом




Предыдущий раздел: перейти