![](https://engineer.yadro.com/wp-content/uploads/2024/07/iterative_ultimate_cover-2.png)
Итеративная сборка FPGA-проектов
с помощью нейросети
Константин Павлов, старший инженер по разработке систем на кристалле, работает в группе прототипирования в компании YADRO. Он занимается отладкой на ПЛИС исходного кода, который затем будет работать в ASIC.
В статье он расскажет об итеративной (многократной) сборке проектов ПЛИС. Зачем она нужна и какими способами — вендорскими и самописными — ее возможно реализовать. А еще на примерах из практики покажет, каких впечатляющих результатов можно добиться, используя итеративную сборку.
- что такое итеративная сборка и в чем ее польза
- какие существуют способы организации итеративной сборки
- как устроена утилита, которая подходит для любой среды проектирования
Что такое итеративная сборка
Разработчики, использующие языки программирования общего назначения, например C или Python, привыкли, что, если код написан синтаксически верно, компилятор предсказуемо качественно его соберет. Различия в производительности и эффективности работы программ, которую обеспечивают конкурирующие компиляторы, несущественны для большинства применений.
При использовании языков описания аппаратуры (HDL) дело обстоит совершенно иначе. Каждый, кто сколько-нибудь серьезно занимался разработкой на HDL, не раз упирался в ограничения выбранной аппаратуры. Или оказывался в ситуации, когда IDE вообще не может собрать проект без нарушения временных ограничений (в просторечии — «времянки не сходятся»). То есть проект собрался, но практического смысла в полученных артефактах нет: они неработоспособны.
Даже абсолютно корректный HDL-код не всегда успешно заработает на целевой аппаратной платформе и с необходимой производительностью. Рассмотрим факторы, которые могут влиять на успешность сборки.
- Архитектура выбранного семейства ПЛИС.
Серии ПЛИС отличаются по производительности, емкости, доступным вариантам корпуса. Даже в рамках одной серии от одного производителя существуют чипы с различным спидгрейдом и различными достижимыми максимальными частотами. Количество тактовых ресурсов, линий IO, специализированных аппаратных блоков всегда конечно и может вдруг оказаться недостаточным для выполнения поставленной задачи.
- Среда разработки (IDE).
Выбрав модель ПЛИС, разработчик зачастую вынужден использовать определенную безальтернативную среду разработки, предоставленную вендором. IDE разных производителей по-разному реализуют процесс сборки, имеют различный функционал и дают разные возможности для анализа дизайна. Необходимость уметь пользоваться всевозможными наборами инструментов требует от разработчика дополнительных усилий и кругозора.
- Параметры модулей проекта.
HDL позволяют описывать модули параметрически. Например, условный двоичный счетчик может иметь параметр N, определяющий его ширину. Такой счетчик без проблем имплементируется в проекте ПЛИС, если параметр N равен 8. Но, если решаемая задача требует реализовать счетчик шириной N равной 1024, этот абсолютно корректный код можно будет воплотить в жизнь только при условии снижения тактовых частот. А это часто недопустимо исходя из требований решаемой задачи.
- Констрейны.
Файлы ограничений в формате SDC/XDC — стандарт в индустрии. Их необходимо причислять к исходным файлам проекта ПЛИС наряду с HDL-кодом, поскольку они существенно влияют на то, как IDE будет интерпретировать HDL, на каких низкоуровневых примитивах и в каком месте на кристалле реализует логические функции. В крупных проектах ПЛИС работа по наполнению и оптимизации файлов констрейнов является наиболее трудным и творческим этапом во всем маршруте разработки.
Это только основные факторы, влияющие на успешность сборки проекта ПЛИС. Мы видим, что «голые» HDL-исходники — это далеко не все, что необходимо для успешной сборки. FPGA-разработчик сталкивается с огромной неопределенностью, вызванной десятками факторов, на всех этапах развития проекта.
Задачи, которые помогает решить И. С.:
- Выбор электронной компонентной базы.
Такая задача возникает на раннем этапе разработки проекта, когда мы только прорабатываем его архитектуру и состав аппаратных средств. И. С. может помочь с выбором оптимальных по характеристикам моделей ПЛИС для использования в целевом проекте. Запустив сборку заготовки будущего проекта под несколько моделей ПЛИС, можно проанализировать, какая из них будет наилучшим образом удовлетворять требованиям.
- Проверка синтезируемости кода во всем диапазоне перестройки параметров.
В зависимости от значения параметра может меняться внутренний алгоритм реализации модуля. И. С. дает возможность автоматизированно проверить все варианты реализации, уменьшив количество ошибок и сократив время на отладку.
- Контроль отсутствия временных нарушений.
В зависимости от значений параметров HDL-модулей меняется сложность имплементации в ПЛИС. В ходе разработки HDL-кода разработчик не всегда осознает, какова на практике область допустимых значений для перестройки параметров, какие комбинации параметров вызовут нарушения временных ограничений. И. С. дает возможность проверить, в каком диапазоне значений параметров реализованный HDL-код будет пригодным к использованию. Исключается ситуация, когда минимальные изменения в проекте приводят к неожиданной деградации характеристик и/или увеличению сроков разработки.
- Выбор наилучшей стратегии синтеза и имплементации.
Такая задача обычно возникает на завершающем этапе разработки проекта, когда все необходимые функции реализованы и отлажены по отдельности. Разработчику необходимо собрать проект целиком, поэтому заполненность ПЛИС близка к 100%. Возникает потребность не просто «как-то» собирать проект с дефолтными настройками, а делать это эффективно, за минимальное время, при этом без ухудшения качества. И. С. на данном этапе может помочь проварьировать стратегии имплементации, доступные в IDE, и выбрать оптимальную для конкретного проекта.
Если сформулировать коротко, И. С. дает разработчикам дополнительную информацию о поведении разрабатываемого кода и снижает риски получить некачественный, неустойчивый к изменениям проект.
Способы организации итеративной сборки
Разобравшись в терминологии и преимуществах, которые дает многократная сборка проектов ПЛИС, предлагаю рассмотреть, какими средствами мы можем организовать И. С. в своих проектах. Начнем с вариантов, которые я считаю недостаточно хорошими, а далее перейдем к более рациональным решениям.
Самый наивный и трудоемкий способ реализации И. С. — это ручная сборка и компиляция нескольких проектов подряд. Мы вручную делаем копии «базового» проекта, вносим изменения в код, затем вручную запускаем каждый вариант на сборку. После того, как все компиляции завершатся, нужно будет пройтись по директориям, прочитать отчеты и попробовать из них что-то понять. Я считаю, что выявить таким образом какие-то закономерности будет сложно, если вообще возможно.
Второй способ, который можно предложить, — это использование подхода Design runs. Он позволяет скомпилировать проект с разными стратегиями сборки или под разные модели ПЛИС, не выходя за пределы единственного открытого дизайна в окне IDE. Недостаток этого подхода в том, что он доступен только в среде AMD/Xilinx Vivado. Design runs — встроенная стандартная функция Vivado, но способ по-прежнему полуручной, поскольку детальный анализ и сравнение результатов сборки не автоматизированы.
В среде Intel/Altera Quartus есть похожая, но не полностью аналогичная функция, которая называется SEED. Это числовой параметр проекта, при изменении которого меняется и расстановка примитивов проекта на кристалле. Меняя SEED, можно собрать «другой вариант» проекта с другими максимальными рабочими частотами (Fmax). Собрав несколько аналогичных проектов, можно выбрать тот, который обеспечивает наилучшие характеристики. Аналогично Design runs, это полуручной способ. Детальный анализ и сравнение результатов сборки тоже не автоматизированы.
Следующий способ — это инкрементальная компиляция. С ней мы можем переиспользовать для сборки результаты предыдущих запусков. Если от запуска к запуску изменения проекта незначительны, IDE будет собирать заново только изменившиеся части дизайна. Так мы можем на основе одной «удачной» сборки получать ее доработанные варианты с улучшенными характеристиками. Инкрементальная компиляция доступна во всех известных IDE для разработки ПЛИС, однако реализована везде по-разному. Способ нельзя назвать удобным для реализации И. С. Да и детальный анализ и сравнение результатов сборки здесь тоже никак не автоматизированы.
Утилита Quartus Design Space Explorer
От простых и неэффективных методов многократной сборки FPGA перейдем к тем, которые заслуживают большего внимания. В частности — к утилите dse.exe
из пакета Intel/Altera Quartus. Ее окно показано на рисунке.
![Quartus Prime Design Space Explorer II](https://engineer.yadro.com/wp-content/uploads/2024/07/incremental_ultimate_1-1024x590.png)
Здесь речь уже идет о качественном, автоматизированном решении для И.С. Суть работы с утилитой проста. Мы задаем папку с базовым проектом и настраиваем, с какими параметрами необходимо собирать проект, какие параметры проварьировать и в каких пределах. Можно пробовать собирать проект с различными значениями SEED или менять стратегии сборки.
После настройки и запуска утилита самостоятельно сделает локальные копии базового проекта, внесет изменения в каждую копию, в параллель проведет сборку и покажет агрегированный отчет. Каждый из собранных проектов доступен на диске для дальнейшего анализа.
Пример использования dse.exe
Однажды я разрабатывал среднего размера FPGA-проект (~10 килоLUT), в котором была критически важна производительность преобразования потока данных. Я считал, что алгоритм обработки сделан уже достаточно хорошо, поэтому увеличивать производительность планировал за счет повышения тактовой частоты. Я задался вопросом, а можно ли получить бо́льшие частоты — Fmax в терминах IDE Quartus — просто изменяя внешние условия, например, операционную систему и версию среды. Здесь-то мне и пригодилась утилита dse.exe
.
Я сделал несколько виртуальных машин с Windows 7 и Linux Manjaro, установил на них различные версии Quartus Lite Edition и воспользовался Design Space Explorer, чтобы перебирать значения SEED. Это позволило получить статистику по сборке одного и того же FPGA-проекта для каждой из комбинаций «операционная система/версия IDE». Чтобы среда собирала проект с максимальными возможными частотами Fmax, в констрейнах была назначена заведомо недостижимая тактовая частота дизайна в 1 ГГц.
Полученные значения Fmax приведены в таблице и на графиках ниже.
Это совсем не очевидно, но, оказывается, достижимая рабочая частота собранного проекта ПЛИС существенно отличается в зависимости от внешних, совершенно искусственных факторов.
Худший результат сборки — проект работает максимум на 207 МГц. Лучший из полученных результатов — проект работает на частоте 374 МГц. Из раза в раз на Quartus версии 13.1 на Windows получаются плохие результаты. А если взять Linux и собирать проект в Quartus 20.1 с агрессивными настройками, тот же самый код компилируется с Fmax практически в два раза выше!
Это реальный пример, какую пользу можно получить от итеративной сборки. Сделав несколько запусков компиляции в различных условиях, мы выявили закономерности, позволяющие оптимизировать параметры дизайна. В данном случае мы получили почти двукратный прирост в производительности прошивки ПЛИС, просто поменяв машину, на которой собираем релиз!
Мой подход к итеративной сборке
Design Space Explorer — отличная утилита, но она работает только с проектами для ПЛИС Intel/Altera. Прямых аналогов от других производителей я не знаю.
Мне захотелось создать инструмент, который бы работал похожим образом, но был более универсален. Он должен предоставлять возможность делать копии базового проекта ПЛИС, менять параметры запуска компиляции и итеративно собирать проекты. А также иметь средства для автоматизированного анализа результатов.
Идею я реализовал в виде шаблона для итеративной сборки FPGA-проектов. Он состоит из нескольких HDL-исходников и вспомогательных скриптов. Любой более сложный проект можно будет сделать на основе шаблона и поэкспериментировать с И. С. Я разработал это решение с расчетом, чтобы его можно было легко адаптировать под любую IDE для разработки ПЛИС и получить максимальную гибкость в применении.
Рассмотрим вариант шаблонного проекта, предназначенный для итеративной сборки ПЛИС AMD/Xilinx. Структура файлов проекта следующая:
Проект состоит из головного, или «внешнего», makefile и директории базового проекта. Базовый проект base
содержит директорию src/
с исходными файлами HDL, директорию scripts/
с вспомогательными TCL-скриптами, и «внутренний» makefile.
«Внешний» makefile
«Внешний» makefile нужен, чтобы скопировать основной проект ПЛИС необходимое количество раз. В нем также определяется переменная VAR,
которая будет определять различия дочерних проектов друг от друга. В шаблоне реализован последовательный перебор натуральных чисел от VAR_MIN
до VAR_MAX,
однако ничто не мешает реализовать любой, сколь угодно сложный закон перестройки параметров. Переменная внедряется в дочерние проекты через автогенерируемый файл define.vh
. Для каждого значения переменной VAR
будет создан и независимо собран дочерний проект.
Для удобства анализа результатов сборки множества проектов, во внешнем makefile предусмотрен отдельный job под названием report.
Мы в цикле проходим по сгенерированным дочерним проектам и достаем из отчетов только ту информацию, которая нам интересна в данный момент.
Листинг «внешнего» makefile представлен ниже:
«Внутренний» makefile
«Внутренний» makefile предназначен для сборки конкретного дочернего проекта. В нем указывается имя проекта, модель ПЛИС и состав исходных файлов. Этот файл необходимо заполнить один раз при подготовке базового проекта.
Листинг «внутреннего» makefile:
Вспомогательные TCL-скрипты
В среду Vivado встроен скриптовый язык TCL. Поэтому для сборки проектов потребовалось подготовить несколько вспомогательных TCL-скриптов.
В скрипте set_prj_vivado.tcl
мы считываем параметры проекта, прокинутые из внутреннего makefile, создаем проект Vivado под выбранную модель ПЛИС, добавляем исходники в проект.
Скрипт сompile_vivado.tcl
нужен, чтобы запустить синтез и имплементацию. Затем нам нужно просто дождаться завершения процесса.
Запуск итеративной сборки нужно производить из корня проекта командой make -j
. Компиляция дочерних проектов происходит параллельно. Утилита make контролирует, сколько независимых процессов компиляции будут выполняться одновременно. Как только один из процессов сборки завершится, make запустит на сборку очередной дочерний проект.
Преимущества предложенного решения
- Компактный код, который легко адаптировать под задачу. При необходимости шаблон легко корректировать и поддерживать. Например, сделать итерации не по одному, а по двум параметрам в дочерних проектах.
- Простота. Мы не применяем никаких изощренных техник, нам не нужно устанавливать или изучать дополнительные технологии. Утилита make широко известна и активно используется в индустрии, работает везде.
- Предложенный подход актуален при работе с любой средой проектирования, с ПЛИС любого производителя. В статье описан вариант шаблона для Vivado, но можно поменять запуск Vivado на запуск Quartus, Gowin EDA, IceCube и любого другого САПР или тулчейна. Мы получили универсальный инструмент для И.С., не заточенный на определенного вендора или конкретную серию ПЛИC.
- Встроенные возможности по анализу отчетов. Команды, которые мы можем использовать в make-файле, универсальны. В нашем распоряжении весь арсенал текстовых утилит типа sed, awk и прочих. У нас нет так называемого vendor lock: мы используем технологии, которые одинаково работают с любым проектом и на любой аппаратной платформе.
Всем удачи! Исследуйте свои проекты ПЛИС с помощью итеративной сборки.