Основной код загрузчика🔗

Функция main🔗

Функция main выполняет подготовительные действия, необходимые для запуска основной целевой программы, по окончании которых передаёт управление этой программе.

Код функции main и всех её зависимостей размещается в DRAM. Это сделано из-за необходимости перемещения сегментов OCM (три сегмента OCM перемещаются с адреса 0x00000000 в адрес 0xfffc0000, образуя с четвёртым сегментом, расположенным по адресу 0xffff0000 непрерывную область памяти размером 256 кбайт) – работающие код и данные не могут размещаться в перемещаемых сегментах, поэтому они помещены в DRAM.

Первое действие – настройка системы прерываний:

  • инициализация массива указателей на обработчики прерываний;
  • установка приоритетов прерываний;
  • назначение целевого процессора для прерываний от периферийных устройств;
  • разрешение прерываний.

Далее инициализируются контроллеры UART (для вывода сообщений на терминал) и QSPI флеши, в которой хранятся загрузочные образы битстрима ПЛИС и целевой программы.

Затем выполняется перемещение таблицы трансляции MMU в старшие адреса OCM – это необходимо сделать, т.к. в дальнейшем все сегменты OCM будут перемещены в старшие адреса памяти, и текущее положение таблицы трансляции окажется не валидным1.

Следующее действие: конфигурирование ПЛИС (вызов функции load_pl) с предварительным включением преобразователей уровней PS-PL (без этого доступ к PCAP не работает), после чего PL часть становится полностью работоспособной.

После этого осуществляется перемещение сегментов OCM и настройка фильтрации адресов SCU, так чтобы младшие адреса адресного пространства отображались на DRAM.

Предпоследнее действие – загрузка образа целевой программы, которая осуществляется путём вызова функции load_img.

Завершающее действие – передача управления целевой программе. Этот код запрещает прерывания, инвалидирует кэш инструкций и предсказатель ветвлений после чего передаёт управление на вектор старта целевой программы (обычно это адрес 0xfffc0000 – начало OCM в старших адресах).

Конфигурирование ПЛИС🔗

Битстрим ПЛИС хранится во внешней флешь памяти, откуда он извлекается загрузчиком и используются для конфигурирования ПЛИС посредством блока PCAP. Формат загрузочного образа описан на странице Flash Images.

Загрузка битстрима осуществляется путём вызова функции load_pl с передачей ей адреса в QSPI флеши, по которому находится образ битстрима. Функция выполняет следующие действия:

  1. извлекает размер бистрима из заголовка образа;
  2. копирует в буфер, расположенный в DRAM, данные битстрима;
  3. проверяет контрольную сумму образа;
  4. инициализирует PCAP;
  5. инициализирует PL;
  6. загружает с помощью DMA блока PCAP битстрим в PL;
  7. проверяет правильность загрузки и возвращает код успешности операции.

ВАЖНО

После копирования данных в DRAM необходимо обазятельно обеспечить когерентность кэшей и памяти! Дело в том, что копирование данных бистрима осуществляется CPU, который использует свою аппаратуру доступа в память, включая кэши данных, а загрузку бистрима в PL выполняет DMA блока PCAP, который обращается в DRAM напрямую, минуя кэши.

Когерентность кэшей и памяти осуществляется путём вызова функции Xil_DCacheFlush().

Работа загрузчика PL сопровожадется продобным логом в терминале. Пример лога:

bld: load PL bitstream from addr: 40000

load_pl: >>>> prepare bitstream <<<<
load_pl: retrieve PL bitstream size from image header: 4045564 bytes (1011391 32-bit words)
load_pl: copy bitstream image (4045572 bytes) from QSPI to DRAM
load_pl: check PL bitsream image CRC...Ok!

load_pl: >>>> setup PCAP, initialize and configure PL <<<<
load_pl:  0. wait for PL power... Ok
load_pl:  1. set up PCAP path for PL configuration
load_pl:  2. clear interrupt flags
load_pl:  3.a. PROG_B = 1
load_pl:  3.b. PROG_B = 0
load_pl:  3.c. wait for PCFG_INIT == 0... done
load_pl:  3.d. set PROG_B
load_pl:  3.e. clear PCFG_DONE interrup flag
load_pl:  4. wait for the PL is ready for programming... done: 40000A30
load_pl:  5. DMA cmd queue ready
load_pl:  6. disable PCAP loopback
load_pl:  7. program PCAP_2x clock divider for non-secure mode
load_pl:  8.a. DMA src addr: 200040
load_pl:  8.b. DMA dst addr: FFFFFFFF
load_pl:  8.c. DMA src len: 1011391
load_pl:  8.d. setup and launch DMA transfer with size: 1011391
load_pl:  9. Wait for DMA transfer done...Ok
load_pl: 10. check for transfer errors
load_pl: 11. PL configuration done
load_pl: >>>> SUCCESS <<<<

Загрузка целевой программы🔗

Загрузочный образ целевой программы так же хранится во внешней флешь памяти. В отличие от образа битстрима PL этот образ состоит из блоков, каждый из которых имеет заголовок, в котором указаны адрес загрузки блока, длина блока и флаги. Блоки загружаются последовательно до тех пор, пока не обнаружится последний блок (на это указывает флаг в заголовке блока). В процессе загрузки осуществляется подсчёт контрольной суммы CRC32, которая по завершении загрузки проверяется.

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

Логирование работы загрузчика🔗

Все важные действия отмечаются сообщениями в лог, выводимый на терминал. Типовой вывод работы программы загрузчика и передачи управления целевой программе:

------------------------------------------------
bld: start!

bld: relocate and remap MMU translation table... done

bld: bootloader DRAM stuff copy errors:
       +--------+--------+----------+
       |  code  |  data  |  rodata  |
       +--------+--------+----------+
       |   0    |    0   |    0     |
       +--------+--------+----------+

bld: load PL bitstream from addr: 40000

load_pl: >>>> prepare bitstream <<<<
load_pl: retrieve PL bitstream size from image header: 4045564 bytes (1011391 32-bit words)
load_pl: copy bitstream image (4045572 bytes) from QSPI to DRAM
load_pl: check PL bitsream image CRC... Ok!

load_pl: >>>> setup PCAP, initialize and configure PL <<<<
load_pl:  0. wait for PL power... Ok
load_pl:  1. set up PCAP path for PL configuration
load_pl:  2. clear interrupt flags
load_pl:  3.a. PROG_B = 1
load_pl:  3.b. PROG_B = 0
load_pl:  3.c. wait for PCFG_INIT == 0... done
load_pl:  3.d. set PROG_B
load_pl:  3.e. clear PCFG_DONE interrup flag
load_pl:  4. wait for the PL is ready for programming... done: 40000A30
load_pl:  5. DMA cmd queue ready
load_pl:  6. disable PCAP loopback
load_pl:  7. program PCAP_2x clock divider for non-secure mode
load_pl:  8.a. DMA src addr: 200C60
load_pl:  8.b. DMA dst addr: FFFFFFFF
load_pl:  8.c. DMA src len: 1011391
load_pl:  8.d. setup and launch DMA transfer with size: 1011391
load_pl:  9. Wait for DMA transfer done...Ok
load_pl: 10. check for transfer errors
load_pl: 11. PL configuration done
load_pl: >>>> SUCCESS <<<<

bld: relocate OCM segments to upper memory... done!

load_img: >>>> load image from QSPI address: 440000 <<<<
load_img: load section block > addr: FFFC0000, size:  17980, attr: 0
load_img: load section block > addr: FFFE0000, size:   1772, attr: 0
load_img: load section block > addr: FFFE06EC, size:      8, attr: 0
load_img: load section block > addr: FFFE06F4, size:    144, attr: 0
load_img: load section block > addr: FFFE0784, size:     24, attr: 1
load_img: CRC Ok!
bld: loading cam program from QSPI done!
bld: >>>> handoff bld -> cam <<<<

------------------------------------------------
cam: start!

  1. Следует отметить, что таблица трансляции MMU не загружается из образа целевой программы, а помещается по указанному адресу путём копирования таблицы трансляции MMU самого загрузчика – это возможно благодаря тому, что содержимое таблицы трансляции MMU загрузчика и целевой программы идентичны. Основная причина отказа от загрузки таблицы трансляции MMU средствами BootROM состоит в том, что в этом случае размер образа получается чрезмерно большим – утилита bootgen, с помощью которой генерируется образ загрузчика, по какой-то причине стремится сформировать образ, охватывающий все адреса программы, и получается, что если таблицу трансляции разместить в старших адресах (четвёртый сегмент OCM, куда таблица в конечном итоге и помещается), то размер образа начинает превышать адресное пространство процессора, что неприемлемо.