Привет, Хабр! Меня зовут Павел Панкратов, я ведущий инженер-программист в дивизионе искусственного интеллекта YADRO. Этим текстом я запускаю цикл статей — экскурс в особенности работы с SoC, комбинирующей в себе реализованные в «железе» аппаратные блоки (Hard IP’s) и программируемую логику (Soft IP’s). Основная задача, которая объединит все три статьи, — параллельный запуск встраиваемой операционной системы на двух различных по архитектуре процессорах, представленных в виде Hard и Soft IP-блоков.
Производители подобных систем, как правило, предоставляют окружение для разработки и документацию с примерами реализации универсальных решений. Но масса важных деталей от пользователя все же скрывается, что приводит к долгим исследованиям при попытках нетривиальной модификации. Эта и остальные статьи цикла — попытка раскрыть завуалированные тонкости, сделать их доступными и понятными.
В первой части цикла вы узнаете про разработку проекта программируемой логики. В ней мы:
Кратко познакомимся с аппаратной платформой.
Рассмотрим инструменты для разработки.
Узнаем про архитектуру проекта.
Сконфигурируем и настроим Hard-подсистемы (CPU, память, периферия).
Сконфигурируем и настроим Soft-подсистемы (CPU, память, периферия).
Организуем каналы взаимодействия.
Проведем синтез проекта.
Вторая часть будет посвящена кросс-компиляции Embedded Linux, а третья — запуску и верификации проекта. Подписывайтесь, чтобы не пропустить.
Дисклеймер: Статья рассчитана на подготовленного читателя. Основной упор я сделал на освещение нюансов и подводных камней при реализации схожих проектов. В статье намеренно не описывал базовые понятия, информацию о которых легко найти в интернете. Постарался охватить весь цикл разработки с различными вариациями используемых подходов и инструментов.
Знакомство с аппаратной платформой
В основе проекта — плата для разработчика от компании MYIR (Make Your Idea Real): FZ5 EdgeBoard AI Box | Xilinx Zynq UltraScale+ ZU5EV. Она базируется на SoC от компании AMD (Xilinx) — Zynq UltraScale+ XCZU5EV. Документацию и сопроводительное ПО можно найти по ссылкам: MYIR и Zynq UltraScale+ MPSoCs.
Результаты этого проекта, хоть это и не было доказано экспериментально, должны быть воспроизводимы и на других платах класса Zynq. А общая концепция и подходы к реализации переносимы на широкий спектр встраиваемых устройств различных производителей.
На верхнем уровне эта система на кристалле содержит два крупных блока: Processing System (PS, процессорная система) и Programmable Logic (PL, программируемая логика).
Сердце PS-части — четырехъядерный ARM Cortex-A53, вокруг которого организована необходимая периферия в виде подсистем ввода-вывода, контроллера памяти, графического процессора и прочих сопроцессоров, отвечающих за конфигурирование, функции безопасности и другое.
PL-часть представляет собой конфигурируемую пользователем логику. В ней в рамках проекта мы планируем разместить ряд IP-блоков, реализующих функциональность 32-битного процессора с периферией, достаточной для запуска Embedded Linux. Так, в качестве CPU IP используем MicroBlaze, а в качестве периферии — набор стандартных блоков, доступных в интегрированной среде разработки.
Коммуникация между PS- и PL-частями осуществляется через набор AXI-шин. Подробнее — в документации и по ходу статьи.
Инструменты для разработки
Производитель предлагает целый набор инструментов для разработки:
Vivado — основная среда для разработки проектов конфигурируемой логики.
Vitis Unified Software Platform — среда для разработки гетерогенных проектов. Предназначена для связи компонентов FPGA, CPU, AI, предоставляет более высокий уровень абстракции.
PetaLinux Tools — набор инструментов командной строки для упрощения разработки Linux-based продуктов.
По сути, Vivado и Vitis — это графические оболочки над инструментами командной строки, среди которых tcl-скрипты и xsct (Xilinx Software Command Line Tool). Это буквально означает, что всe, что выполняется в графической оболочке, может быть сделано средствами командной строки и скриптов.
PetaLinux — оболочка над Yocto Project, упрощающая настройку, сборку, верификацию, подготовку загрузочных образов и запуск Linux-based проектов. Сводит все этапы до вызова нескольких команд.
Отмечу также несколько альтернативных вариантов для сборки Embedded Linux:
Yocto Project — фреймворк, являющийся самостоятельным проектом для сборки Embedded Linux, а также бэкэндом для Petalinux.
Buildroot — еще один популярный фреймворк для сборки Embedded Linux.
Кросс-компиляция из исходных кодов с помощью совместимого тулчейна. Один из самых «прямых» и понятных методов сборки ядра.
Исходные коды ядра Linux, а также необходимых загрузчиков и прочих компонентов доступны в открытых репозиториях:
Также доступна обширная Wiki
В ходе работы над проектом мы использовали следующие версии ПО:
Как их скачивать и устанавливать, описывать в статье я не буду. Вся необходимая информация доступна в руководствах пользователя.
Архитектура проекта
Итак, наша цель — запустить Embedded Linux на PS- и PL-частях SoC XCZU5EV. Ключевой момент: обе ОС должны работать параллельно.
Что будем делать с Processing System
Здесь ситуация более-менее понятна. Существуют прямые инструкции по развертыванию Embedded Linux стандартными средствами для разработки. У платы есть вся необходимая периферия, включая коммуникационные каналы (UART, USB, Ethernet), контроллер DDR-памяти, соединенный с 8 ГБ DDR4, четырехъядерный ARM Cortex A53 и другие компоненты.
План по PS-части для платы FZ5BOX следующий:
Создать проект в Vivado IDE и выполнить конфигурацию Processing System IP-блока.
Синтезировать проект Vivado и получить файл описания аппаратного обеспечения.
На базе файла описания аппаратного обеспечения создать и построить проект с помощью PetaLinux tools. Результатом будет загрузочный образ, который необходимо поместить на SD-карту.
Верифицировать работоспособность решения выполнив загрузку с SD-карты.
Общий план для любой другой платы подобного класса будет аналогичным, различие лишь в сопроводительной документации и деталях конфигурации.
Что будем делать с Programmable Logic
С PL-частью ситуация не столь тривиальна, рассказать о ней мне придется куда более подробно — наберитесь терпения.
В сети множество инструкций по интеграции и настройке Soft-CPU, развертыванию Embedded Linux на платах без Processing System. К сожалению, такого не скажешь о противоположном, то есть о платах, содержащих как программируемую логику, так и аппаратно реализованную Processing System. Конечно, некоторые материалы есть. Но они либо сводятся к запуску Bare-Metal приложений на Soft-CPU, либо абстрактно говорят о том, что успешный опыт запуска Embedded Linux на PS и на Soft-CPU в PL-частях плат класса Zynq существует, но без деталей.
Как правило, если PS части нет, то микросхемы оперативной памяти, коммуникационные порты и прочая периферия напрямую подключаются к программируемой логике. Для успешного запуска Linux в проекте Vivado размещаются соответствующие IP-блоки (Soft-CPU, контроллер памяти и прочие вспомогательные системы). Затем назначаются внешние порты в соответствии с документацией на плату или Board-файлом. Последний, кстати, сильно упрощает процесс: Vivado заранее знает, какие пины за что отвечают, и позволяет простым перетаскиванием из перечня доступных компонентов подключать требуемую периферию.
После успешного синтеза проекта генерируется файл описания аппаратного обеспечения. Он импортируется в проект PetaLinux, и буквально спустя несколько команд мы получаем загрузочный образ, который можно запустить как на QEMU, так и отправить по JTAG на саму плату. UART-консоль также можно организовать по JTAG.
Вуаля, мы получили работоспособный Linux, развернутый на Soft-CPU! Подход прост и понятен. Ключевой минус: от нас скрыта масса нюансов, которые сыграют важную роль при попытке развернуть Embedded Linux на плате класса Zynq, то есть, включающую в себя аппаратную Processing System.
Все же азарт пройти малоизведанный путь есть. Давайте попробуем сформировать план действий для запуска Embedded Linux на Soft-CPU в PL-части FZ5BOX и детально посмотрим на моменты, где наш путь будет сильно отличаться от описанного для плат без PS.
Организуем доступ к памяти
Платы класса Zynq — это в первую очередь процессор и только во вторую — программируемая логика. Выражается это в том, что большая часть периферии, включая микросхемы оперативной памяти, не подключается напрямую к программируемой логике, а ориентирована на обеспечение нужд PS. C точки зрения последней, вся эта периферия отражена на физическую память (Memory Mapped IO). Что это означает для нас?
Linux — это большая программа, которая загружается в оперативную память и затем исполняется процессором. Для ее работы нужен ряд аппаратных компонентов, таких как центральный процессор (CPU), контроллер прерываний (Interrupt Controller), менеджер памяти (MMU, Memory Management Unit), системный таймер (System timer) и непосредственно оперативная память (RAM).
Некоторые из этих компонентов уже реализованы в таком Soft-CPU, как MicroBlaze. Некоторые можно реализовать внешними IP-блоками, размещенными в PL. У MicroBlaze, среди прочего, есть поддержка LMB (Local Memory Bus), которая реализуется посредством блочных ячеек памяти, расположенных в PL-части. У этой памяти высокая скорость доступа на чтение и запись, но еe объема может быть недостаточно для загрузки полноценной ОС. Поэтому MicroBlaze предлагает реализацию кэшированного доступа во внешнюю оперативную память, связь с которой более медленная.
Так как на платах класса Zynq оперативная память — в ведении PS-части и не подключена напрямую к PL, мы не сможем разместить IP-блок контроллера памяти в PL-часть с последующим назначением соответствующих I/O-портов. Микросхемы памяти попросту подключены к аппаратно-реализованному в SoC контроллеру DDR.
Наш план — использовать доступ к оперативной памяти PS-части через AXI-шину. Так оба процессора (обе ОС) будут использовать одну физическую память. Важно отметить, что использование одной памяти может иметь неприятные последствия при одновременном доступе. В связи с этим, с точки зрения ОС, запущенной на PS, нам следует зарезервировать регион памяти, используемый Soft-CPU. Такой подход решит вопрос с получением доступа к нужному объему памяти и упростит загрузку всех необходимых образов, но создаст другую проблему, о которой я расскажу позже.
Добавляем коммуникационные интерфейсы
Помимо памяти и основных аппаратных компонентов, нам нужны коммуникационные интерфейсы для взаимодействия с ОС Soft-CPU. Здесь есть несколько вариантов:
Вариант первый. Как и в случае с памятью, используя AXI-шину, можно получить доступ к периферии платы. Это позволит, например, пробросить UART-консоль на внешние порты. FZ5BOX располагает двумя UART-интерфейсами, выведенными на корпус. У одного из них есть встроенный конвертер USB-UART, который по умолчанию используется операционной системой, запущенной на PS. Второй выведен в виде пинов и подразумевает использование внешнего USB-UART.
Вариант второй. Можно разместить два IP-блока, реализующих функциональность UART, и соединить их Tx- и Rx-линии между собой. Отдав контроль над первым UART-IP Soft-CPU, а над вторым — PS, мы сможем организовать коммуникацию между ними, не выводя сигналы во внешний мир. Консоль одного будет доступна через /dev/tty-устройство внутри другого. Вызвав консольную утилиту screen c указанием нужного нам устройства в рамках PS, мы получим доступ к консоли MicroBlaze.
Второй вариант, на мой взгляд, более простой, поэтому предпочтение отдадим ему.
Загружаем ОС в оперативную память для исполнения Soft-CPU
Остается открытым вопрос о построении и загрузке ОС в оперативную память для исполнения Soft-CPU. Ему мы посвятим две отдельные части. Пока отмечу, что сделать это стандартными инструментами поставщика (PetaLinux) если не невозможно, то как минимум очень проблематично.
Опираясь на альтернативные варианты для сборки Embedded Linux, я сделал выбор в пользу самого «прямого» и понятного пути сборки ядра — кросс-компиляции из исходного кода. (Для интересующихся, вариант с использованием Buildroot также рабочий, проверено).
Что касается загрузки и исполнения, вариант загрузки по JTAG отпал, так как цель была сделать портативную систему, способную самостоятельно запускаться и выполнять требуемые задачи. Помимо этого, у FZ5BOX нет выведенного на внешний корпус JTAG-разъема. Для подключения последнего необходимо разбирать плату на части, что мне показалось излишним.
В связи с этим самым доступным способом доставки загрузочного образа на плату остался вариант с копированием его на SD-карту вместе с образом для PS-части. О процессе размещения образа в RAM и запуске исполнения на Soft-CPU поговорим отдельно.
Суммируем планы
Кажется, всего перечисленного должно быть достаточно. Давайте объединим планы действий для PS- и PL-частей:
Собираем проект в Vivado IDE, включающий в себя Processing System (Hard-CPU), MicroBlaze (Soft-CPU), необходимую периферию.
Организуем каналы взаимодействия между подсистемами.
Синтезируем проект и получаем файл описания аппаратного обеспечения.
Собираем Embedded Linux и получаем загрузочный образ для Hard-CPU с помощью PetaLinux.
Собираем Embedded Linux для Soft-CPU путем кросс-компиляции ядра из исходных кодов.
Создаем загрузочную SD-карту и помещаем полученные образы в соответствующие разделы.
Загружаем систему и проверяем результат.
План кажется довольно простым, но большая часть шагов таит в себе целый ряд нюансов и подводных камней. Однако не буду лукавить, статью я пишу уже после завершения проекта, а значит, успех неизбежен. В путь!
Лирическое отступление. Хочу добавить, что отладка подобных проектов сама по себе — нетривиальная задача, особенно если нет доступа по JTAG или альтернативным каналам. Выполняешь все необходимые шаги к запуску, а в ответ — тишина… :)
Конфигурирование и настройка Hard-подсистемы
Конфигурация PS-части специфична для разных платформ и серий SoC. На разных платах компоненты подключены к различным выводам. IP-блоки Processing System могут иметь разные названия.
Самый надежный способ — обратиться к руководству пользователя конкретной платы для разработчика. В случае с FZ5BOX документ называется MYS-ZU5EV_FPGA Development Manual, который доступен в сопроводительной документации и ПО по ссылке. Так, разделы 2 и 3 содержат подробное описание необходимых шагов по установке IDE, созданию проекта и конфигурации PS. Я не буду повторять их в статье, но отмечу дополнительные шаги, не описанные в документации.
Согласно плану, нам необходимо организовать два дополнительных канала взаимодействия между PS- и PL-частями:
Из PL в PS (канал для доступа к оперативной памяти).
Из PS в PL (канал последовательной консоли посредством IP-блока UART).
Для организации этих каналов необходимо перейти в раздел PS-PL Configuration основного окна настройки IP-блока Zynq UltraScale+ MPSoC.
Далее в разделе PS-PL Interfaces необходимо включить AXI HPM0 FPD мастер-интерфейс и AXI HPD0 FPD слейв-интерфейс. Помимо этого, так как UART генерирует прерывания, нам нужно также включить в разделе General → Interrupts → PL to PS линию прерываний IRQ0.
Настроенный блок PS будет выглядеть так:
Размещение и настройку периферийных IP-блоков выполним после конфигурирования Soft-CPU части.
Конфигурирование и настройка Soft-подсистемы
Конфигурацию Soft-подсистемы начнем с размещения основного компонента — Soft-CPU. Для этого из каталога IP выбираем блок под названием MicroBlaze. После его перетаскивания на рабочее поле проекта в верхней части последнего появляется надпись Run Block Automation. Щелкнув на нее, всплывает окно с предварительной конфигурацией нашего Soft-CPU.
Предлагаю выполнить настройки, как показано на картинке ниже:
После выполнения настроек нажимаем ОK.
В зависимости от версии среды для разработки в «шапке» рабочей зоны может вновь появиться надпись Run Block Automation. Дело в том, что в более свежих версиях Vivado блок MicroBlaze может быть использован как в классическом исполнении, так и в исполнении RISC-V. В этом проекте я использовал классическую версию.
Пара слов о пункте Debug Module. В нем можно сконфигурировать модуль отладки в базовом и расширенном вариантах, а также с дополнительной UART-консолью. Он позволяет отлаживать работу процессора по JTAG или через AXI-шину и развертывание Debug-сервера в PS-части. Из-за того, что подключение JTAG к плате FZ5BOX весьма неудобно, а вера в собственные силы была велика, я попросту отключил данный модуль как ненужный. Для ваших проектов настраивайте его по потребности.
Базовая конфигурация MicroBlaze разместила в рабочей зоне ряд дополнительных блоков:
rst_ps8_0_99 M — Processor System Reset — вспомогательный блок для корректной организации каналов сброса компонентов системы.
microblaze_0_axi_periph — AXI Interconnect — вспомогательный блок для согласования и объединения AXI-шин. На стороне MicroBlaze он подключен к каналу M_AXI_DP (Data Peripheral). Этот канал отвечает за обмен данными с периферийными устройствами. Для справки, MicroBlaze — это процессор с «гарвардской» архитектурой, то есть с раздельными шинами данных и команд (инструкций). Помимо этого, MicroBlaze разделяет шины для кешированного доступа к памяти M_AXI_DC (Data Cache), M_AXI_IC (Instruction Cache) от шин доступа к периферии. Также есть возможность включить шину инструкций для периферии M_AXI_IP (Instruction Peripheral). Это, я полагаю, может быть полезным при загрузке инструкций напрямую из периферийной памяти — например, QSPI. В нашем случае в этом нет необходимости.
microblaze_0_axi_intc — AXI Interrupt Controller — контроллер прерываний. Я уже упоминал, что он является одним из аппаратных компонентов, необходимых для успешного запуска Embedded Linux. Да и не только для этого.
microblaze_0_xlconcat — Concat — блок служит для объединения различных источников прерываний в единую шину, для передачи в контроллер прерываний.
microblaze_0_local_memory — локальная память, базирующаяся на BRAM. Быстрая, но с весьма скудным максимальным объемом в 128 КБ. Она еще не раз станет героем моего повествования.
Углубляемся в настройку Soft-подсистемы
После базовой конфигурации перейдем к более тонкой настройке. Для этого дважды щелкнем на IP-блок Soft-CPU. Здесь нас ждет два возможных режима настройки: базовый и расширенный, который включается нажатием кнопки Advanced в левой верхней части окна конфигурации. Давайте детальнее посмотрим, что предлагают эти режимы.
Базовый режим
Базовый режим предлагает шесть страниц с конфигурацией различных параметров. Ниже — скриншоты с необходимыми пояснениями.
На первой странице выбираем предустановленную конфигурацию Linux with MMU. Это загрузит пресет минимально необходимых настроек для запуска Embedded Linux. Разрядность процессора — 32 бита.
Вероятно, у вас возникнет вопрос: «Почему бы не выбрать 64 бита?». На момент написания статьи я встречал ряд комментариев от представителей AMD (Xilinx), что поддержка Embedded Linux на 64-битной версии MicroBlaze не входит в их текущие планы.
Конечно, можно подискутировать и о целесообразности использования 64-битного Soft-CPU, его производительности, максимальной частоте работы, балансе использованных ресурсов программируемой логики и так далее. Но давайте оставим попытки запуска Embedded Linux на 64-битной версии особо искушенному читателю, а пока продолжим.
В разделе General Settings мы отключим Debug Module Interface, используем кэш данных и инструкций для доступа во внешнюю оперативную память, а также используем исключения, так как Linux поддерживает их обработку.
MMU — это еще одно звено успешного запуска Embedded Linux. В теории можно запустить Linux и без него, но это уже совсем другая история. Особого внимания заслуживает последняя галочка настроек — Enable Discrete Ports. Она активирует внешние порты управления и получения текущего состояния процессора. Эти порты сыграют ключевую роль в корректном запуске Soft-CPU, но об этом чуть позднее.
На второй странице — конфигурация различных аппаратных компонентов в составе процессора.
Настройки блока Instructions способны менять фактический набор инструкций, которые может исполнять процессор. При конфигурации ядра Linux нам потребуется в точности указать, что именно было включено в этом разделе. Иначе процессор может «случайно» свалиться в исключение по неподдерживаемой инструкции во время исполнения.
Также здесь можно настроить еще ряд параметров, которые нам пока не так интересны. Они отвечают за:
оптимизацию синтеза проекта для максимальной производительности или частоты,
настройку коррекции ошибок в BRAM,
подключение кэша для предсказанных соответствующим модулем целей инструкций условного перехода.
Ранее мы включили поддержку обработки исключений. А эта страница позволяет сконфигурировать перечень исключений, которые процессор может генерировать. Оставляем, как есть, и идем дальше.
Четвертая страница настроек позволяет сконфигурировать внутренние детали организации кэша данных и команд, который используется для доступа во внешнюю оперативную память. Выбрав пресет Linux with MMU на первой странице настроек, мы уже получили базовые значения для указанных здесь параметров.
Единственное, что нужно поменять, — это начальный адрес и адрес верхней границы. Они формируют диапазон памяти, к которой будет осуществляться кэшированный доступ. Доступ к памяти вне этого диапазона кэшироваться не будет. В нашем случае регион выделенной оперативной памяти будет начинаться с адреса 0x4000_0000, объем — 512 МБ. То есть до адреса 0x5FFF_FFFF и ко всему данному участку предполагается кэшированный доступ.
Тут может возникнуть очередная порция резонных вопросов: «А что это за магические цифры? Откуда они взялись?».
Что ж, I’m glad you asked! У нас 32-битный процессор, а значит, он может адресовать всего 4 ГБ памяти. Заранее отвечая на упрек внимательного и знающего читателя — да, MicroBlaze позволяет включить режим Extended Addressing на второй странице настроек, который добавляет в арсенал инструкции для адресации большего диапазона. Наша плата снабжена 8 ГБ памяти, но бывают версии и с 4 ГБ — и тогда расширяться просто некуда. Так что мы данную опцию использовать не будем.
Так вот, 4 ГБ памяти — это диапазон адресов от 0x0000_0000 до 0xFFFF_FFFF. Еще раз взглянем на карту памяти:
В диапазоне первых 4 ГБ у нас есть участок от 0x0000_0000 до 0x7FFF_FFFF, выделенный под нужны DDR-контроллера, — он составляет 2 ГБ. Оставшиеся 6 ГБ ОЗУ на плате находятся за пределами адресуемой 32 битами памяти и для MicroBlaze недоступны. Использование крайних адресов 2 ГБ диапазона — плохая идея: зачастую близкие к началу или концу участки памяти используются загрузчиками. Поэтому мы целимся в середину.
Слегка забегая вперед, согласно загрузочному скрипту U-Boot, образ Linux для PS-части грузится по адресу 0x1000_0000 по умолчанию, что находится в первой половине доступного диапазона. Не будем мешать этому процессу и выберем участок во второй половине, который как раз начинается с адреса 0x4000_0000. Размер участка памяти в 512 МБ достаточен для MicroBlaze и займет ровно четверть от 2 ГБ памяти, оставляя свободными еще 512 МБ в конце под нужды загрузчиков. Таким образом и получился наш выбранный диапазон от 0x4000_0000 до 0x5FFF_FFFF.
Пятая страница настроек отвечает за конфигурацию менеджера памяти. Базовые настройки также подгружены пресетом Linux with MMU. Менять ничего не будем.
Заключительная страница настроек отвечает за конфигурацию коммуникационных шин. Два первых раздела содержат уже знакомые нам интерфейсы локальной памяти и кэшированного доступа к внешней памяти. Раздел потоковых интерфейсов позволяет подключать несколько парных (мастер-слейв) однонаправленных каналов. В основном это используется для связи с различными аппаратными ускорителями, реализованными в PL-части для поддержания высокой скорости передачи данных. Trace Bus Interface может использоваться для отладки и сбора статистики. А Lockstep Interface полезен для синхронизации работы между несколькими экземплярами MicroBlaze в мультипроцессорной системе.
Расширенный режим
Расширенный режим настройки во многом повторяет базовый, но есть несколько дополнений. Расскажу о работе с одним из них, вероятно, кому-то будет полезно.
Активировав расширенный режим, перейдем на вкладку Interrup & Reset. В поле Vectors (скрыто всплывающей подсказкой) можно задать пользовательский адрес таблицы векторов прерываний. В руководстве MicroBLaze написано, что при включении питания процессор начинает выполнение инструкций, стартуя с данного адреса. Опция может быть интересена в случае прямого исполнения кода из оперативной памяти, без запуска из зоны LMB. На это намекают и два бита в регистре MSR — DCE, ICE.
О чем говорит всплывающая подсказка? В случае установки вышеупомянутых битов в единицу сразу после подачи питания кэшированный доступ к внешней оперативной памяти будет активирован. И это позволит реализовать систему без LMB. На первый взгляд, это как раз наш случай. Нам необходимо загрузить образ Linux в общую оперативную память и начать его исполнение.
Однако, после ряда экспериментов мне так и не удалось увидеть ощутимой разницы с включенными или отключенными битами DCE, ICE. Вероятно, она заключается только в кэшировании доступа, а не в его ограничении, на что намекает подсказка. Тем не менее, в обоих случаях процессор уверенно стартовал исполнение инструкций из внешней памяти. Тестировал я это как на Bare Metal-приложениях, так и на весьма комплексных программах — например, загрузчике U-Boot. К сожалению, ядро Linux, предоставленное вендором, таким образом не запустится, поэтому мы не исключили LMB из нашего проекта. О причинах мы поговорим в третьей части статьи.
На этом этапе мы закончим настройку MicroBlaze. Практически все необходимое у нас уже есть. Еще один важный элемент, который мы пока не включили в проект, — это таймер/счетчик. Многие компоненты Linux нуждаются в точном отсчете времени. К сожалению, внутренности MicroBlaze не обеспечивают ОС всем необходимым для этой задачи. Поэтому мы вынуждены использовать внешний блок. В меню выбора IP находим AXI Timer и перетаскиваем его на рабочее поле. Настройки выполняем как на скрине:
Теперь точно все. Необходимые компоненты Soft-подсистемы в сборе. Переходим к налаживанию соединений между блоками и организации каналов взаимодействия.
Организация каналов взаимодействия
Для начала выполним соединения между всеми размещенными компонентами. Для этого можно воспользоваться опцией Run Connection Automation в верхней части рабочего поля или просто соединить все вручную.
Подключаем интерфейсы кэшированного доступа M_AXI_DC, M_AXI_IC к порту S_AXI_HP0_FPD через AXI SmartConnect. Таймер подключаем как еще одно периферийное устройство к каналу M_AXI_DP Soft-CPU через существующий microblaze_0_axi_periph. Вывод прерываний от блока таймера заводим на порт блока Concat, чтобы те были доступны для использования MicroBlaze. Остается подключить все каналы тактового сигнала и сброса. В качестве промежуточного результата получаем примерно следующее:
Далее займемся организацией канала связи с ОС, запущенной на MicroBlaze. Напомню, что для этого мы планировали использовать два IP-блока UART, линии Tx и Rx которых соединены между собой. Вновь обращаемся к каталогу IP и находим блок AXI Uartlite. Нам нужны два, размещаем! Настройки обоих блоков должны совпадать. Пример на скрине ниже:
Шину S_AXI одного подключаем как периферийное устройство MicroBlaze через microblaze_0_axi_periph, а другого — к порту M_AXI_HPM0_FPD процессорной системы через отдельный AXI Interconnect. Каналы прерываний также соответственно подключаем к блоку Concat, уходящему в MicroBlaze через контроллер прерываний, и к порту pl_ps_irq0 процессорной системы.
На этом хотелось бы сказать, что проект готов к синтезу, но, увы, это не так. В главе, посвященной архитектуре, я отметил, что решение с использованием оперативной памяти, находящейся в ведении PS, создает дополнительную проблему. О ней поговорим сейчас.
Проблема с использованием ОЗУ, находящейся в ведении PS
Дело в том, что при подаче питания MicroBlaze начинает исполнение инструкций с адреса, установленного как базовый для таблицы векторов прерываний. В случае загрузки ОС ее точка входа будет находиться в ОЗУ. На момент первого доступа Soft-CPU во внешнюю память последняя уже должна быть сконфигурирована как сама по себе, так и в плане коммуникационных интерфейсов (в нашем случае это AXI-шины). Этой конфигурацией занимается первичный загрузчик.
Естественно, происходит это не мгновенно. И MicroBlaze, получив питание, столкнется с проблемой доступа ко все еще не настроенной памяти — это приведет к сваливанию в исключение. Но проблему можно решить. На этапе конфигурации мы включили использование дискретных портов, отвечающих за контроль и состояние процессора.
В особенности нас интересует шина Reset_Mode.
Конфигурация шины в состояние 01 позволяет сразу после запуска переводить Soft-CPU в состояние сна. Пробуждение осуществляется подачей сигнала на любой из каналов шины Wakeup. Таким образом, все, что нам необходимо сделать, — это разместить блок Constant из IP-каталога, задать ему значение 01 и подключить к шине Reset Mode.
С подачей сигнала на шину Wakeup есть ряд возможных решений. Я предпочту использовать IP-блок AXI GPIO. Помимо шины Wakeup, у процессора еще есть ряд выводов состояния, таких как Sleep, Hibernate, Suspend, Pause Ack. Пожалуй, неплохо будет получать их значения в целях отладки.
Добавление в проект и подключение IP-блока AXI-GPIO принципиально ничем не отличается от других блоков. В двух словах данный блок предоставляет возможность использования GPIO-портов в PL-части путем доступа к ним по AXI-шине из PS-части. Конфигурация блока представлена ниже:
Здесь я настроил восемь GPIO-портов. Два первых канала на выход, остальные — на вход. Используя блоки Concat и Slice, подключил их соответственно к шине Wakeup и портам состояния процессора. Порт S_AXI подключил к периферийному AXI Interconnect процессорной системы. Сигналы тактовой частоты и сброса соответственно. Давайте посмотрим на итоговый проект:
Синтез проекта
Вот мы и перешли к завершающему этапу. Перед синтезом осталось сделать всего одну вещь: настроить карту памяти. В проекте мы использовали различные блоки, которые участвуют во взаимодействии через AXI-шины. Последние в нашем случае отображены на память.
Не буду ходить вокруг да около — редактирование адресов карты памяти проекта вызывается щелчком на вкладку Address Editor над верхней частью рабочего поля. Сконфигурированные адреса можно увидеть ниже:
Здесь мы видим уже знакомые нам 128 КБ LMB, доступные по адресу 0x0, 512 МБ DDR, доступные в диапазоне от 0x4000_0000 до 0x5FFF_FFFF, и всю вспомогательную периферию для Soft- и Hard-подсистем.
Настроив все должным образом, переходим к синтезу. Синтез проекта состоит из трех шагов:
Синтез.
Имплементация.
Запись битстрима.
Все шаги доступны на панели Flow Navigator Vivado IDE. Чтобы выполнить все три стадии разом, можно просто вызвать исполнение последней, а первые две вызовутся автоматически.
После успешного прохождения этапов нам необходимо экспортировать файл описания аппаратного обеспечения. Он понадобится нам позднее. Для этого переходим в меню File → Export → Export Hardware…. Проходя по шагам помощника, выбираем Include Bitstream, чтобы включить файл прошивки программируемой логики в файл описания аппаратного обеспечения, далее указываем путь и название файла. После нажатия кнопки Finish по указанному пути появится файл с расширением .xsa. Сохраняем его и готовимся ко второй части нашего путешествия…
Как я уже писал в начале статьи, вторая часть цикла будет посвящена сборке Embedded Linuх. Что мы сделаем в следующий раз:
Узнаем, какие минимально необходимые компоненты понадобятся для запуска Embedded Linux.
Осуществим сборку под ARM стандартными инструментами производителя.
Осуществим сборку под Soft-CPU «вручную».
Подготовим загрузочный носитель.