Запуск Bare-metal програми на Cyclone V SoC

Введення

Для деяких людей FPGA SoC є чимось недоступним розумінню і ця стаття повинна виправити це непорозуміння. Розберемо створення програми з нуля, від порожнього проекту, до палаючого світлодіоду. Для початку скажу, що проект виконувався на налагоджувальній платі DE1-SoC, і ви можете з легкістю адаптувати його для інших плат з плісами фірми Altera, якщо розберетеся з даним керівництвом. Почнемо!

Створення 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 біля clk_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/і натиснути OK, інші параметри змінювати не потрібно.

Вибираємо параметри spl.boot в якому вказуємо джерело звідки буде завантажуватися Preloader. У нашому випадку це SD карта, тому виберемо BOOT_FROM_SDMMC. Будемо використовувати варіант завантаження Preloader і нашої програми при якому на флешці як мінімум 2 розділу - розділ не відформатований з id = A2, для Preloader, і розділ з системою FAT32, для програми. Є інший варіант без поділу флешки на розділи, так званий RAW 1916 at, але наш варіант на мій погляд простіше. Відформатувати таким чином флешку можна будь-якою програмою (я використовував 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 можна бачити виконання програми.

Ув'язнення

У цій статті багато моментів можна було б розглянути докладніше, оскільки описаний процес має безліч етапів, а на кожному є альтернативні варіанти виконання і свої особливості. Але я думаю, що на всі інші питання за мене чудово відповість список літератури. Читайте наступну статтю Запуск AMP програм на Cyclone V 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