Введение


Для некоторых людей FPGA SoC является чем-то недоступным пониманию и данная статья должна исправить это недоразумение. Разберем создание программы с нуля, от пустого проекта, до горящего светодиода. Для начала скажу, что проект выполнялся на отладочной плате DE1-SoC, и вы можете с легкостью адаптировать его для других плат с плисами фирмы Аltera, если разберетесь с данным руководством. Начнем!

Создание прошивки FPGA


Для создания прошивки нам, очевидно, потребуется проект Quartus. Но создадим этот проект не стандартным способом (через project wizard), а через утилиту, которая шла в комплекте с платой DE1-SoC



Эта утилита генерирует top-level файл написанный на Verilog с объявлениями выбранных элементов. Нам потребуется CLOCK, HPS (SoC), кнопки, светодиоды. Нажимая Generate получаем проект Quartus. Главное преимущество создания проекта этим способом, это то, что нам не придется назначать пины FPGA в Pin Planner, потому что утилита сделала это за нас, тем самым сэкономила уйму времени.



Добавим генерированный файл к проекту.

Следующим шагом будет создание системы в QSYS. На всякий случай кратко поясню суть происходящего. Cyclone V SoC не просто FPGA, внутри ее структуры находится двухъядерный процессор Cortex-A9 с разными модулями, такими как USB порты, Ethernet, SPI, SD/MMC и т.д. По-простому, можно представить это как микроконтроллер внутри FPGA. Создавая систему в QSYS мы связываем HPS (hard processor system) систему с элементами, синтезируемыми в FPGA, используя готовые ядра (IP cores). QSYS позволяет без лишних забот соединить разные ядра через шины Avalon-MM или AMBA AXI, нам не нужно в ручную писать код, QSYS генерирует его за нас. Заходим в QSYS и выбираем во вкладке IP cores нашу HPS систему. В настройках системы нам потребуется только Lightweight H2F Bridge во вкладке FPGA interfaces.



Во вкладке Peripheral Pins выберем SD/MMC (с карточки мы будем загружать программу) и UART. Позже расскажем об этом подробнее.



Во вкладке HPS clocks оставляем все по умолчанию. Во вкладке SDRAM необходимо заполнить все поля значениями skew и так далее. На сколько я понимаю, они зависят от трассировки линий от микросхемы до SDRAM. У меня не получилось найти какого-либо документа, в котором есть эти данные для DE1-SoC, поэтому взял их из готового проекта для моей платы (это был SOC-Computer в примерах Altera University Program). Я сохранил эти настройки как персет, чтоб больше не заполнять их, вы можете видеть его в правой колонке. Далее в IP cores найдем PIO, это будут наши светодиоды и кнопки.



Для настроек кнопок выберем Input и ширину 4 бита так как 4 кнопки всего



Для светодиодов соответственно Output и ширина 10.



Соединяем полученную систему как показано на рисунке. Можем изменить имена PIO чтоб было понятнее и красивей. LWH2F bridge является связующим звеном между HPS и FPGA (так же назначает мастером HPS), так как PIO — это элементы выполняющиеся в «ткани» FPGA. Кликнем два раза на надпись «Double click for export» напротив external connection, что-бы вывести из системы выводы PIO светодиодов и кнопок. Позже вы увидите в коде top-level файла генерированной системы эти выводы. Поскольку доступ к PIO, да и ко всем остальным ядрам, в HPS осуществляется по адресам, необходимо назначить их. Это можно сделать автоматически, нажав Assign base addresses.

После этого можно генерировать код системы.





Указываем путь и желаемый язык кода. Я предпочитаю Verilog. После этого QSYS можно закрыть. В окне Quartus появляется следующее сообщение:



Давайте сделаем то, что нас просят.



В окне Files видим наш hps_system.qip. Раскроем его и видим top-level файл системы.



Все, что мы выбрали в системе QSYS теперь в этом файле. Теперь нужно просто вставить этот модуль в изначальный файл. Это и будет top-level файлом нашей прошивки FPGA.



В нем мы приписываем выводам HPS системы изначальные I/O пины этого файла. Напоминаю, что ничего не нужно назначать в Pin Planner, все уже сделано при создании проекта утилитой. Но необходимо назначить пины HPS, это делается следующим образом:





Как только это сделано можно компилировать прошивку. Важно заметить, что если были допущены какие-либо ошибки (я, например, забывал поставить точку в объявлении hps_system возле сlk_clk и hps_io_hps_io_… видно из рисунка) необходимо заново выполнять tcl скрипты. Даже если все написано правильно, и вы запустили tcl скрипты, но после запуска компиляции вы получаете ошибку, стоит попробовать еще раз запустить скрипты, ничего не меняя в коде. Мне помогало, не знаю, чем объяснить данную особенность.



И так, с прошивкой закончили! Теперь приступаем к созданию Preloader.

Создание Preloader


Процесс загрузки HPS имеет несколько стадий, попробуем разобраться в них. Стоит заметить, что Cortex-A9 является процессором для приложений, буква «А» в названии означает Application, и в первую очередь предназначен для работы с использованием ОС, например Linux. Поэтому, строго говоря, идея запуска Bare-Metal программ может показаться странной, но в некоторых случаях это необходимо. К тому же, естественно, такая возможность есть, но разработчику надо понимать процесс загрузки хотя бы на базовом уровне.

Сразу после включения выполняется код расположенный прямо на Flash памяти Cortex-A9 называемый BootRom. Вы не можете изменить его или даже посмотреть его содержание. Он служит для первичной инициализации и в следующем этапе передает процесс загрузки в SSBL (Second Stage Boot Loader называемый коротко Preloader). Что необходимо знать для понимания процесса — это то, что код BootRom, в первую очередь, выбирает источник загрузки Preloader, ориентируюсь на внешние физические пины BSEL. В DE1-SoC изначально выбрана конфигурация пинов для загрузки с SD карточки, и изменить это на, например, QSPI или NAND flash, не припаивая дополнительно переключателя и пары резисторов, невозможно. Поэтому в QSYS мы выбирали во вкладке Peripheral Pins пины SD карты. Есть так же вариант загрузки не из внешних источников, а из памяти, созданной в FPGA, с предварительно загруженным туда кодом.

И так после выполнения кода BootRom начинает загружаться Preloader, необходимый для настройки Clock, SDRAM и прочего. После начинает выполняться программа.

Для создания Preloader потребуется SoC EDS, уверен вы уже скачали его с сайта Intel FPGA.



Программа работает из командной строки. Для начала создадим BSP написав соответствующую команду «bsp-editor».



В этом окне нажмем New HPS BSP.



Необходимо указать путь к {Project directory}/hps_isw_handoff/ и нажать ОК, остальные параметры менять не нужно.



Выбираем настройки spl.boot в котором указываем источник откуда будет загружаться Preloader. В нашем случае это SD карта, поэтому выберем BOOT_FROM_SDMMC. Будем использовать вариант загрузки Preloader и нашей программы при котором на флешке как минимум 2 раздела – раздел не отформатированный с id=A2, для Preloader, и раздел с системой FAT32, для программы. Есть другой вариант без разделения флешки на разделы, так называемый RAW format, но наш вариант на мой взгляд проще. Отформатировать таким образом флешку можно любой программой (я использовал Mini partition tools 9.2). Или можно использовать уже собранный образ в папке ...\embedded\embeddedsw\socfpga\prebuilt_images\sd_card_linux_boot_image.tar.gz и записать его на флешку через Win32DiskImager.



Выбирем FAT_SUPPORT, FAT_BOOT_PARTITION 1, FAT_LOAD_PAYLOAD_NAME .img. Не будем использовать WATCHDOG_ENABLE и EXE_ON_FPGA (мы же не собираемся загружать Preloader с FPGA).



Здесь очень тонкий момент, на котором я попался и целый месяц искал проблему, когда только знакомился с SoC. Serial Support означает, что уже Preloader будет использовать UART модуль для вывода диагностических сообщений прямо во время загрузки. Semihosting означает, что во время вывода этих диагностических сообщений, они будут автоматически выводиться в окне debugger при отладке. Использование в самой программе этой функции очень удобно, она позволяет выводить в окно debugger все, что написано в функции printf без дополнительного написания кода. Если в настройках QSYS в HPS не указать использование UART, но поставить галочку Serial Support в BSP — такой Preloader работать не будет. Если убрать Serial Support, при этом оставив Semihosting, также ничего работать не будет, по крайней мере у меня так было. Так что можете поэкспериментировать или просто оставьте галочки, если хотите, чтоб все точно работало. Нажимаем Generate, затем Exit.



Теперь поменяем рабочую папку SoC EDS командой cd "<указываем полный путь до генерированных файлов BSP (по дефолту это …software/spl-bsp)>" Для сборки Preloader выполним команду make. Ожидаем. В конце процесса в паке получаем нужный файл preloader-mkpimage.bin.

Это собранный файл, в котором находятся одинаковых 4 образа Preloader. Командой mkpimage, выполненной в SoC EDS, можно разобрать этот файл на составляющие (отдельные образы), а затем собрать уже из других конфигураций (с разными образами Preloader). Другие конфигурации могут быть совершенно разными (разные системы в QSYS), то есть получаем отдельные файлы preloader-mkpimage.bin с 4 одинаковыми образами (по 64кб каждый), разобрали их на составляющие и теперь можно собрать новый preloader-mkpimage.bin с разными образами (например, с разными источниками загрузки). Это делается для надежности. Допустим так случилось, что какая-то сила не позволила загрузиться с первой попытки, с первого образа. Тогда начинается загрузка второго образа, а он, например, у нас немного другой, для аварийной ситуации. Если и этот не удалось загрузить, то переходим к третьему и так далее. Но это уже не предмет нашей задачи, скорее лирическое отступление от темы, так что продолжаем!

Вставим флешку с уже подготовленным разделом A2 и запишем на нее наш preloader-mkpimage.bin командой «alt-boot-disk-util -p -a write –d ». SoC EDS должен указывать на путь к папке где лежит файл Preloader. Все почти готово для написания программы! Лишь создадим header файлы с дефайнами QSYS элементов для удобства. Поменяем путь SoC EDS, указав к файлу прошивки, и выполним команду sopc-create-header-files .sopcinfo. На выходе получаем несколько файлов, посмотрев содержание которых станет понятно зачем они.



Программа


Для написания и отладки программы производитель рекомендует использовать среду DS-5 Eclipse. Рекомендуется запускать Eclipse через SoC EDS командой «eclipse&» (знак "&" в конце команды ставится для того, чтобы окно SoC EDS было активное после открытия Eclipse).
Для тех, кто уже знаком с ARM и когда-либо писал для такой архитектуры программы, сложности заканчиваются. Для незнакомых с ARM сложности продолжаются.

Создадим пустой «C» проект. Там будет окно выбора компилятора, тут каждый волен выбирать и разбираться с тем, что ему больше подходит. Мне, как новичку в ARM больше пришелся по вкусу Arm Compiler 5, главным образом из-за относительно простого синтакиса scatter файлов, используемых для размещение написанной программы в разных частях программы (один только вид синтаксиса linker script у GCC меня пугает). В настройках проекта все выглядит тривиально. Укажу лишь на данную команду.



Сделайте тоже самое. Это конвертирует axf формат в формат bin. Нам потребуется это позже для записи программы на флешку. Напишем уже наконец ее.



Тут даже stdio.h не потребуется. Эта программа просто присваивает содержимое в памяти по адресу KEYS_BASE для адреса LEDS_BASE. Нажав кнопку на плате, светодиод тот час погаснет.



Содержание scatter файла.
Для отладки в debugger необходимо написать скрипт. Как вы помните процесс загрузки не простой. Скрипт останавливает выполнение Preloader на этапе загрузки приложения из источника и передает эту задачу компьютеру.



Не забудьте прошить плату перед загрузкой программы и отладкой!



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







Теперь после компиляции вы можете отлаживать программу, смотреть содержание регистров и проделывать массу интересных вещей.



После того, как вы убедились, что программа работает корректно, можно создать образ программы для самостоятельной загрузки. Для этого из папки Debug с проектом DS-5 возмем файл <prj_name>.bin и через SoC EDS конвертируем его в .img формат. Это делается командой «mkimage -A arm -O u-boot -T standalone -C none -a 0x00100000 -e 0x00100000 -n „baremetal image“ -d .bin .img.» где "-a" адрес куда загружать, а "-e" точка входа программы.

Точка входа также может задаваться в самом проекте, если используются вектора прерывания, у нас они не используются, поэтому не задаем. Я ориентируюсь на то, какие адреса я задавал в scatter файле, и пишу такие же в этой команде. Перед выполнением не забудьте указать путь к bin файлу в SoC EDS командой cd "<папка с файлом>". Название img файла должно совпадать с тем, что вы указали в BSP editor в FAT_LOAD_PAYLOAD_NAME.

Теперь просто скопируем файл на флешку в fat раздел, как обычный файл. Вставив флешку в DE1-SoC можно видеть выполнение программы.

Заключение


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

Список литературы


  1. Cyclone V Hard Processor System Technical Reference Manual
  2. Altera SoC Embedded Design Suite User Guide
  3. HPS SoC Boot Guide — Cyclone V SoC Development Kit
  4. Bare Metal User Guide
  5. SoC-FPGA Design Guide
Поделиться с друзьями
-->

Комментарии (18)


  1. VBKesha
    15.06.2017 17:36

    Помнится я тоже промучался с BareMetal. В итоге перешел решил что удобней использовать ос например QNX.

    Сразу после включения выполняется код расположенный прямо на Flash памяти Cortex-A9 называемый BootRom. Вы не можете изменить его или даже посмотреть его содержание.

    Вроде бы его содержимое можно прочитать по адресу 0xfffd0000 (https://www.altera.com/hps/en_us/cyclone-v/hps.html#topic/sfo1410068186638.html)


    1. pinchazer
      15.06.2017 21:39

      Да, действительно, содержание BootRom находится по этому адресу. Но я имел ввиду что вы не найдете ни ассемблерный код, ни, конечно же, С код BootRom. Можно только в дебаггере посмотреть машинный код, но какой в этом смысл?


  1. Fox_exe
    15.06.2017 17:43
    -7

    «Почему FPGA такие сложные?» — потому что чтобы поморгать светодиодом придётся потратить пол-часа на чтение документации по процу и IDE.
    «Почему Arduino такой популярный?» — потому что чтобы поморгать светодиодом достаточно ткнуть «Run» в IDE (т.к. код уже есть написан, а вся механика рабоыт с железом уже реализована в стандартных либах и достаточно одной команды для управления пином.)


    1. Nice_Vladi
      15.06.2017 17:48
      +3

      Да ну нет же. Здесь человек рассказывает о серьезной связке ПЛИС+процессор, программирование софтового микроконтроллера и т.д. Это и правда не просто. А мигать светодиодом на ПЛИС — вполне просто. Проект marsohod.org — там куча примеров для простых камней. Не слишком сложнее ардуин. Все-таки стоит ознакомиться с предметом, а потом ругаться)) В целом, эта самая моргалка диодом — ну 5 строк, максимум. Не так уж много и сложно. Даже как-то обидно стало за плисины)


      1. Fox_exe
        15.06.2017 19:56
        -2

        Согласен — FPGA — это сложно. Но статья преподноситься, будто «Легко».
        Отсюда и сравнение — простецкая ардуинка, у которой 90% «железных» функций сокрыты за простыми командами и иниализация (почти?)всего железа уже реализована в стандартной библиотеке.

        В случае с FPGA — придётся всё выставлять ручками (Через редактор или сразу в коде).
        Да и сам fpga — это не готовый проц, а набор логики, которая может работать как угодно. Отдельные модели позволяют манипулировать связями своих транзисторов, что позволяет чипу прикинуться обычным процом (Причем любым. Максимум — скорость работы будет отличаться)


        1. VBKesha
          15.06.2017 22:23
          +3

          Зачем сравнивать платформу(Arduino) и технологию FPGA?
          Сравните голый ATMega и FPGA. Или Cyclone V SoC+Linux и Arduino.
          И в этой статье идёт речь вообще о SoC.


    1. VBKesha
      15.06.2017 17:51
      +2

      Не стоит забывать про места применения. Когда понадобится запустить какой нибудь экран с LVDS на с клоком даже в 50 мегагерц, Arduino не поможет никак. А если надо моргать светодиодами то ставить FPGA смысла нет.

      Да и к тому же это SoC а не FPGA…


    1. ef_end_y
      15.06.2017 18:34
      +2

      Ардуино — попсовый вариант реализации микроконтроллера и имеет много недостатков в плане возможностей и производительности, поэтому нет смысла сравнивать с ПЛИС. По своему опыту: несколько лет назад меня пригласили в проект покодить MSP-430, так я с нуля изучал его неделю. На ПЛИС же я потратил гораздо меньше времени.

      Ну, и в который раз — ПЛИС и микроконтроллеры имеют зачастую совершенно разные назначения, поэтому их сравнивать равносильно сравнению электропилы и перфоратора


    1. lingvo
      16.06.2017 13:27

      В FPGA мигание светодиодом делается за 5 минут, причем вообще без программирования и знания Verilog/VHDL. Причем нужно знать только название пина, на котором сидит светодиод и пина входа для клока.


  1. tronix286
    15.06.2017 19:04
    +2

    Для того, чтобы поморгать светодиодом нужен резистор килоома на полтора, конденсатор в микрофарад 300 и КТ315Б:
    image


    1. tronix286
      15.06.2017 19:12
      +1

      И светодиод, конечно. Я уж не упоминаю в суе К155ЛА3, с помощью которой можно построить эмулятор циклона 5, а потом на нем улететь в костмас. Но это, если конечно ракета позволит несколько сотен тонн ЛА3 вывезти с земли, а солнечные панели обеспечат питалово соизмеримо кол-ву ЛА3.


  1. RomanArzumanyan
    16.06.2017 12:26
    +1

    Не критики ради, а удобства для: можно быстро сделать скриншот окна, если запустить «Ножницы» (Snipping Tool), и потом выбрать New->Windows Snip. Получается аккуратный скриншот окна, без необходимости обведения рамкой.


  1. lexxsu
    16.06.2017 20:56
    -1

    А по мне так наоборот все слишком сложно описано. Очень много привязки проприетарному софту и предопределенному железу. На примере открытого RISC процессора необходимо только расположить прошивку на нулевом адресе DDR и сделать reset. Процессор сам грузится с нулевого адреса по умолчанию. Все установки делаются опять через прошивку процессора.
    Также непонятно зачем разбираться с памятью, она же в fpga bitimage определена, а не через процессор.


  1. antonsosnitzkij
    20.06.2017 22:23
    +1

    Статья класс, недавно в коллективе хотели исать такую же, только все делая с нуля не используя GUI для создания стартового проекта, но Вы опередили! Столкнулись с тем же вопросом о BSEL, потому что в CycloneV DevKit они хотя бы есть, но плата на порядок дороже. Пока не находили с ходу информацию о «припаять» для изменения конфигурации не находили, не подскажите ли этот вопрос подробнее?
    Интересны подробности содержимого spl, использование Neon и второго ядра, превратится ли это в цикл статей по данной тематике?


    1. pinchazer
      20.06.2017 23:18
      +1

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


      1. antonsosnitzkij
        21.06.2017 18:56

        Коллега проделал такую же работу как и Вы, только с GCC вместо ARM compiler, выявили ряд косяков, но когда решили попробовать перейти на ARM — вышло, что он очень строгий и ругался на библиотеки, поэтому вернулись на gcc.
        Еще такой вопрос: ARM DS-5 требует нахождение всех библиотек в рабочем пространстве проекта, иначе начинает ругаться, встречалась ли Вам такая проблема? Если да, то как решили этот вопрос?


        1. pinchazer
          21.06.2017 22:48

          Не считаю это за проблему, просто скопировал все исходники из hwlib (стандартные библиотеки для всей переферии HPS) в папку проекта и все. Ведь при компиляции собственно компилируются только используемые в коде исходники, а остальные не задействованы. Мне ARM compiler больше понравился чем GCC из-за относительно простого по сравнению с linker script синтаксисом scatter файла. linker script у GCC вообще какой-то через чур сложный показался, а размещение проги в памяти играет очень важную роль в Baremetal. Каким образом так получилось, что ARM compiller ругается, а GCC нет? Что значит строже?


          1. antonsosnitzkij
            22.06.2017 12:36

            Это скорее не проблема, а лишние затраты времени и места. Да, библиотеки не весят много, но когда проект за проектом вы копируете набор файлов — согласитесь, это странно. А если еще в ходе разработки пользователь начинает изменять параметры компиляции (например, ставит флаг оптимизации), то библиотеки заново перекомпилируются и это дополнительное время. Мы еще до конца не разбирались, но вроде бы можно просто руками скомпилировать и затем создать -.a файлы, с названием типа lib_bridges.a, далее в настройках линковщика добавить их — и готово, копировать папку ненужно.
            По поводу строгости — думаю, логично, что компилятор созданный под конкретную платформу будет оптимизировать код лучше, чем компилятор, созданный без учета платформы. У нас была одна проблема — в одном месте gcc выдавал предупреждение, а arm compiler ошибку. Правда, сейчас вот открыл DS-5 и не смог с ходу воспроизвести эту ошибку, но как получится — Вам расскажу.)