Привет, Хабр! Меня зовут Кирилл Алексеев, я старший инженер в отделе интеграции систем на кристалле радиочастотного центра YADRO. В мире FPGA я уже больше 10 лет. 

В учебных программах разработка под FPGA (ПЛИС, программируемые логические интегральные схемы) освещается довольно мало. Может возникнуть ощущение, что это «странная» область предназначена только для радиофизиков или гиков. Но и в эту сферу уже давно пришел прогресс с системами контроля версий, таск-бордами, VS-кодом, Python-скриптами и даже с элементами объектно-ориентированного программирования при верификации цифровых схем. Этой статьей я хочу «десакрализировать» тему FPGA-разработки, обозначив крупными мазками стек используемых технологий и отразив повседневные задачи FPGA-разработчика. Для примера возьмем workflow нашего отдела. 

В начале статьи поговорим о том, зачем нам вообще нужны специализированные вычислители и почему их разработка в современном мире востребована. Потом я раскрою распределение ролей при производстве цифровой аппаратуры. А после перейдем к задачам FPGA-разработчика: обсудим разработку IP-ядер с заданной функциональностью, их верификацию, интеграцию и отладку «на железе». Посмотрим, чем интересен каждый этап разработки и на какие подводные камни можно наткнуться в процессе. 

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

Почему FPGA, а не ПК

Начну с базы: зачем вообще нужны FPGA? Почему нельзя просто везде использовать обычный ПК с процессором от Intel (AMD, Apple, кому что нравится)? 

Ответ простой: не хватает скорости

Универсальные процессоры, на базе которых построены персональные компьютеры, отлично подходят для работы с документами, но они не способны обрабатывать большой объем информации в режиме реального времени. А специализированные вычислители очень даже способны (на то они и специализированные).

Отличный пример такого «железа» — видеокарты (GPU, графические ускорители). Когда вы играете в видеоигры, центральный процессор компьютера (CPU) не успевает в реальном времени изменять значение пикселей на экране: повороты сцен, изменение освещенности, эмуляцию физических процессов — например, движение травы или блики фонарей в лужах. Поскольку каждое изображение представляет собой трехмерную матрицу пикселей, то практически любая операция с графикой предполагает сложение или умножение матриц. Это требует огромного объема вычислений.

Внутри CPU реализованы универсальные вычислительные ядра, каждое из которых может выполнять множество различных операций: арифметических (сложение, умножение и т. д.), логических (сравнение, битовые операции и т. д.), последовательных операций над массивами и не только. Но из-за универсальности ядер CPU, их число невелико и обычно измеряется двузначными числами. 

В отличие от CPU, графические ускорители состоят из тысяч небольших по размеру ядер, в которых реализованы только сумматоры и умножители: все, что нужно для эффективной работы с матрицами. Именно за счет большого числа специализированных ядер, GPU могут «быстро перемолоть» всю векторную графику.

Структура CPU процессора (слева) и графического ускорителя GPU (справа)
Структура CPU процессора (слева) и графического ускорителя GPU (справа)

Конечно, кроме графики есть множество других задач, с которыми CPU за приемлемое время не справляется. К примеру, физические модели, предсказывающие погоду с максимальной точностью, анализируют сотни взаимосвязанных параметров. Соответственно, при попытке запуска моделирования на обычном ПК результат предсказания погоды на час вперед может быть получен через день — а это, естественно, уже никому не нужно. Другой пример: цикл моделирования земной коры для поиска нефти и газа может занимать несколько месяцев даже на многопроцессорной вычислительной системе (МВС). Далеко не всегда бизнес готов ждать так долго. 

К задачам, которые эффективно решаются с помощью специализированных вычислителей, относятся:

  • обучение и инференс нейросетей: используются GPU, FPGA, ASIC (Application-Specific Integrated Circuit, интегральные схемы специального назначения), специализированные аналоговые схемы и даже фотонные микросхемы;

  • цифровая обработка сигналов (ЦОС): DSP-процессоры, FPGA, ASIC;

  • задачи шифрования, дешифрования, кодирования, декодирования, криптографии (FPGA, ASIC);

  • задачи L2 коммутации и маршрутизации пакетов в высокоскоростных сетях (FPGA, ASIC), и так далее. 

Большинство перечисленных задач может быть решено с максимальной скоростью и энергоэффективностью на довольно дешевых в массовом производстве заказных микросхемах ASIC. Альтернатива заказным кристаллам — FPGA. Хотя последние и проигрывают в эффективности ASIC-микросхемам, процесс FPGA-разработки значительно проще. Это позволяет быстрее выйти на рынок с MVP (Minimal Viable Product, минимально жизнеспособный продукт) и постепенно его совершенствовать без необходимости изменения «железа».

С введением закончили, поехали дальше.

Какие есть роли в производстве цифровой аппаратуры и где там FPGA-разработчик

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

  • Алгоритмисты-математики разрабатывают основной подход к решению задачи, сравнивая возможные методы их решения по критерию наилучшей точности и качества результата, подкрепляя выводы математическими выкладками. Их наработками пользуются и архитекторы, и программисты, и разработчики IP-ядер.

  • Программисты («физическое» моделирование) реализуют функциональные модели, основанные на выбранных алгоритмах и математических методах решения задач. Функциональные модели призваны проверить корректность работы системы в целом. Тут могут использоваться как языки программирования, так и специализированные среды разработки — например, Matlab.

  • Архитекторы системы превращают математику и алгоритмы в эффективную цифровую схему, которая при заданной функциональности займет минимум аппаратных ресурсов ПЛИС. Результат работы архитектора — технические требования и спецификации, с укрупненными (неподробными) схемами IP-ядер. Если сравнивать разработку программно-аппаратных продуктов со сферой продуктового программирования, в последней роль архитекторов играют системные аналитики.

  • Разработчики IP-ядер работают с архитектурной спецификацией или техническим заданием над описанием предложенной схемы IP-ядра на HDL (Hardware Description Language, язык описания аппаратуры) или используют для этих целей схемные редакторы вроде AMD-Xilinx ISE и Intel-Altera Block Design. Еще есть ряд САПР (Система автоматизированного проектирования) для генерации HDL кода: HLS фреймворки, Matlab Simulink, PeakRDL и другие. В последнее время нейросети LLM тоже могут генерировать HDL код, но у них много ошибок. Не стоит слепо верить LLM: семь раз проверь, один коммить!

  • Верификаторы проверяют корректность схем, описанных на HDL путем моделирования в таких САПР как Vivado, ModelSim, QuestaSim, Verilator и так далее. Обычно под корректностью имеется в виду совпадение выходных данных IP-ядра с некими эталонными данными, получаемыми из программной модели. Верифицировать можно как вручную, отсматривая данные на временной диаграмме, так и в автоматическом режиме в командной строке САПР.

  • Программисты (моделирование поведения «железа»), помимо функциональных моделей, реализуют так называемые битовые модели, выход которых должен в точности совпадать с выходом «железа». «Битовые» модели могут сильно отличаться от функциональных из-за особенностей используемой аппаратной базы и специфики решаемой задачи. Например, при решении задач ЦОС функциональная модель может использовать формат данных с плавающей запятой floating point, тогда как в ПЛИС рационально использовать формат с фиксированной запятой fixed point, поскольку последний обеспечивает нужную точность вычислений, а его реализация использует значительно меньше аппаратных ресурсов. Еще в «битовых» моделях может значительно отличаться последовательность вычислений из-за различных вариантов распараллеливания. Например, в ПЛИС можно реализовать суммирование по схеме «пирамида».

  • Системные программисты настраивают soft или hard-процессорные ядра в ПЛИС и собирают под них специализированные операционные системы, например, Petalinux. Они же реализуют драйверы для доступа к разработанным IP-ядрам.

  • Интеграторы собирают все разработанные IP-ядра в единую систему под управлением внешнего процессора или soft / hard процессорных ядер, реализованных в ПЛИС. Результат их работы — целевая программно-аппаратная функциональность, включающая как «прошивку» ПЛИС, так и необходимое ПО для работы с процессорными ядрами. Иногда интеграторам требуется поместить в ПЛИС очень много вычислительных блоков, и, помимо прочего, обеспечить высокую тактовую частоту. Чтобы в этих условиях получить рабочую «прошивку» без ошибок по Slack и Hold, приходится использовать «черную магию» physical and timing constraints.

  • Тестировщики запускают операционную систему с «прошивкой» ПЛИС на «железе»: отладочных платах, макетах и боевых устройствах. При тестировании может использоваться контрольно-измерительное оборудование, такое как осциллографы, генераторы сигнала, векторные анализаторы и другие. Тестировать можно как вручную, запуская работу прошивки простенькими скриптами и анализируя результат визуально, так в автоматическом режиме через консольные скрипты и API к функциям дополнительного оборудования.

Также другие два вида программистов разрабатывают софт управления «железом», реализующий основную логику работы устройства, и пользовательский интерфейс. Еще в цикле разработки устройства участвуют конструкторы, разработчики печатных плат, наладчики, технические писатели и так далее. 

Где в этом списке FPGA-разработчики? Обычно они объединяют в себе сразу несколько ролей. В нашем отделе мы разрабатываем IP-ядра, верифицируем их, интегрируем в конечную систему и отлаживаем на «железе». Подробно об этом расскажу дальше.

Прямо сейчас у нас открыты вакансии:

Присоединяйтесь к команде!

Workflow FPGA-разработки

Наша команда разрабатывает радиомодули сотовой связи (RU, Radio Unit), отвечающие за преобразование информации в цифровой сигнал (модуляция) и обратное преобразование (демодуляция). При обработке в том числе используются алгоритмы ЦОС — например, QPSK и QAM модуляция/демодуляция, OFDM модуляция/демодуляция, КИХ-фильтрация, интерполяция и децимация.

Разработка таких систем требует от алгоритмистов и архитекторов глубокого понимания алгоритмов ЦОС, принципов функционирования сотовых сетей и систем связи в общем. В РЧЦ YADRO эти специалисты не занимаются непосредственно разработкой, а формируют пул требований и конструкторскую документацию для FPGA-разработчиков и программистов, что обусловлено сложностью и мультикомпонентностью разрабатываемых систем. Но в других компаниях роль архитекторов могут выполнять иные люди. Например, это могут быть программисты, которые прислали тебе код и просят выполнить данные вычисления на ПЛИС. Иногда — заказчики, которые хорошо оформили ТЗ (это бывает реже), или твой непосредственный начальник. Конечно, идеально, когда FPGA-разработчик тоже может выполнять функцию архитектора, что облегчает и ускоряет процесс разработки, но это тема уже для другой статьи.

Если рассматривать в общем, в нашей команде процесс разработки выглядит так: 

Пробежимся по каждому этапу.

  1. Разработка микроархитектуры IP-ядра осуществляется на основании описания целевых алгоритмов и системных требований, полученных от алгоритмистов и архитекторов. Микроархитектура предполагает детальную проработку цифровой схемы, в том числе ее оптимизацию под архитектуру выбранной ПЛИС. Тут требуется учитывать число встроенных арифметических блоков DSP и блоков памяти BRAM / URAM, число и размер логических ячеек LUT, число встроенных процессорных ядер, контроллеров цифровых интерфейсов и памяти и т.п. Также на этом этапе обычно формируется заготовка документации на IP-ядро.

  2. Реализация «битовой» модели для работы с нужной разрядностью и форматом представления данных. Обычно разрабатывается вместе с функциональной моделю или же на ее основе. В зависимости от назначения разрабатываемого блока, функциональная модель может использоваться как эталон относительно точности вычислений. Сравнивая точность функциональной и «битовой» моделей можно подобрать оптимальную разрядность арифметических операций при переходе от float point арифметики к fixed point. Лично я использую для этих целей Python, но многие коллеги программируют в Matlab или на C/C++.

  3. Разработка IP-ядер — итеративный процесс описания схемы на HDL языке SystemVerilog по его архитектурной спецификации и в соответствии с разработанной микроархитектурой. Тут отдельно можно выделить разработку вычислительного ядра, подсистемы управления и регистрового пространства. 

  4. Верификация IP-ядра с использованием эталонных данных «битовой» модели.

  5. Интеграция IP-ядра в тестовый или боевой проект. На этом этапе мы используем IP-интегратор Vivado для его упаковки и Block Design для сбора верхнего уровня проекта. Тут же зачастую выполняется установка и настройка внутрисхемного анализатора (ChipScope, ILA, SignalTAP), который позволяет выгружать состояние схемы прямо из работающей ПЛИС.
    Дальше нужно получить конфигурационный файл ПЛИС, в котором реализуемая схема будет работать на целевой тактовой частоте. Также на этапе интеграции выполняется сборка Petalinux с необходимым набором вспомогательных приложений, а также разработанным bash-скриптом для инициализации IP-ядра.

  6. Тестирование проекта на отладочной плате с использованием Vivado HW Manager, скриптов инициализации и контрольно-измерительного оборудования.

Если обобщать, FPGA-разработчик далеко не всегда выполняет самостоятельно всю описанную выше работу. Например, за реализацию функциональной и «битовой» модели зачастую отвечает отдельный программист. А вот работа по реализации IP-ядер, их частичной верификации, интеграции и тестированию в «железе» как раз и является той самой рутиной в буднях FPGA-разработчика. Подробнее об этом — дальше.

Разработка IP-ядра: HDL-кодирование и не только

Схема (слева) и ее описание на HDL-языке SystemVerilog (справа)
Схема (слева) и ее описание на HDL-языке SystemVerilog (справа)

На этом этапе FPGA-разработчик описывает цифровую схему IP-ядра на известном ему (и используемом в компании) HDL-языке. Я приверженец именно схемотехнического подхода: считаю его самым простым и понятным. Если схемы нет, сначала стоит ее нарисовать, а уже потом приступать к реализации. Обычно на схемах отражают вычислительное ядро, памяти и fifo, а также основные элементы схемы управления — мультиплексоры, приоритетные шифраторы и так далее. Если реализуется процедурная схема, в ней обязательно будет присутствовать подсистема управления, которую часто рационально описывать в виде управляющего автомата (FSM, Finite State Machine). О том, что это такое и как их описывать, можно почитать в книге Харрисов «Цифровая схемотехника и архитектура компьютера».

Разрабатываемые блоки часто содержат регистровое пространство (регпространство): набор регистров, к которым имеет доступ процессор и которые могут быть записаны/прочитаны извне по системной шине. Не могу не поделиться open source тулом PeakRDL, который позволяет сгенерировать SystemVerilog-описание регистровой карты из описания на языке SystemRDL. К тому же с PeakRDL можно автоматически сгенерировать документацию на регистровое пространство. Суммарно это сильно ускоряет и упрощает процесс разработки IP-ядра.  Спецификация на язык SystemRDL приведена тут.

Когда HDL-код IP-ядра готов, нам необходимо с помощью САПР получить конфигурационный файл ПЛИС. Для этого САПР выполняет три операции: синтез, размещение и трассировку (Synthesis, Place and Route). На этапе синтеза описанная схема IP-ядра отображается на примитивы используемой ПЛИС: LUT, триггеры, блоки памяти и арифметические DSP-блоки. На этапе размещения выполняется расстановка элементов синтезированной схемы на координатной плоскости кристалла, а на этапе трассировки выполняется прокладка связей между ними, для чего конфигурируется коммутационная матрица ПЛИС. После выполнения этих этапов САПР предоставляет отчеты об используемых ресурсах FPGA и максимальной тактовой частоте.

Elaborate, Synthesis, Place and Route 
Elaborate, Synthesis, Place and Route 

Чтобы ответить требованиям тактовой частоты, на этапе трассировки САПР выполняет статический анализ временных характеристик схемы (STA, Static Timing Analysis). Важно, чтобы результаты STA не содержали ошибок по времени установки и удержания сигнала (Setup & Hold timing errors). Понятие максимальной тактовой частоты присутствует в САПР Quartus, тогда как в САПР Vivado предоставляется отчет, удовлетворяет ли схема заданной тактовой частоте. Косвенно оценить максимальную тактовую частоту можно и в Vivado, пересчитав используемую тактовую частоту с запасом по Setup из Vivado Design Timing Report.

Vivado Design Timing Report 
Vivado Design Timing Report 

Если полученные результаты устраивают, обычно переходят к следующему этапу. Но что делать, если после STA-анализа есть ошибки? В самом начале полезно проанализировать RQS (Report Quality Summary) рекомендации Vivado, где дается некоторое описание присутствующих в схеме проблем и предложения по их решению. В общем, есть множество вариантов, как улучшить схему и избежать ошибок STA. Начиная от перебора различных стратегий синтеза, размещения и трассировки и заканчивая «расстановкой» примитивов DSP / BRAM / URAM с помощью placement constraint: закрепление конкретных вычислительных узлов схемы в конкретных физических координатах микросхемы. Последнее позволяет упростить задачу алгоритмам размещения и трассировки САПР. Почитать об этом можно тут, стр. 75.

Статьи на эту тему:

Стоит упомянуть и про Vivado Incremental Implementation — функцию, появившуюся в Vivado ML Edition 2021.1, которая позволяет в автоматическом режиме перезапускать Place и Route с использованием предыдущих результатов. Еще в Vivado ML Edition можно использовать кнопку «сделай хорошо»: САПР автоматически запустит сразу несколько стратегий синтеза, Place & Route, выберет самый лучший результат и автоматически запустит Incremental Implementation. Фактически эта функциональность «выжимает максимум» из возможностей САПР, и разработчику остается только ждать «битника» с требуемыми характеристиками.

Верификация RTL — важно и долго

Разрабатываемое IP-ядро (DUT, Design Under Test) и верификационное окружение
Разрабатываемое IP-ядро (DUT, Design Under Test) и верификационное окружение

На практике этот этап идет параллельно с разработкой HDL-кода, поскольку разработчику важно понимать, работает ли то, что он написал, или нет. Отладка «в железе» намного сложнее верификации в симуляторе, поэтому не стоит пренебрегать выполнением этого этапа. Если мы живем в идеальном мире, здесь за нас все, конечно, делают инженеры-верификаторы. Но даже в этом случае FPGA-разработчик обычно моделирует собственный блок хотя бы на нескольких базовых тестовых сценариях, перед его передачей для полного и всестороннего тестирования инженером-верификатором.

Нужно учитывать, что этап верификации самый длительный. Предположу даже, что верификация подчиняется закону Парето (правило 80/20). То есть 20% времени ведется непосредственное написание 80% HDL-кода IP-ядра, тогда как остальные 20% кода — это правки на этапе верификации (и немного отладки), которая требует 80% времени.

В первую очередь верификатор разрабатывает тестовый план — по сути, список тестов, полнота которых согласуется с разработчиком, алгоритмистом и архитекторами блока. Только после их выполнения мы сможем однозначно утверждать, что блок работает корректно. С четко формализованным списком легче верифицировать блок. К тому же так меньше шансов, что вы упустили что-то важное.

Дальше собираем тестовое окружение. Для этого есть достаточно много техник от совсем простых до продвинутых — например, UVM. При этом сам подход к верификации остается неизменным: 

Имеется два массива данных (исходный и результирующий (эталонный). Исходный мы подаем на вход тестируемого блока (DUT, Design Under Test), а дальше сравниваем выходной результат с эталоном. Если результат совпал, все работает корректно. Если нет, ищем причину ошибки и устраняем ее путем изменения HDL-кода.

Возникает вопрос: где взять данные для сравнения? Обычно их предоставляют программисты, получая после запуска «битовой» модели. Но есть и другие варианты — например, написать модель вычислений прямо в тестовом файле на HDL-языке или использовать сторонние IP-ядра в качестве генератора эталонных данных. Суть от этого не меняется: эталон необходим, без него мы не поймем, работает наш блок или нет.

Интеграция IP в проект: работа с системной шиной и периферией

Интеграция разрабатываемого IP-ядра в систему
Интеграция разрабатываемого IP-ядра в систему

Итак, блок отлажен, занимает аппаратного ресурса не больше требуемого и работает на целевой тактовой частоте. Самое время проверить его на реальном железе! 

Но перед этим мы сначала интегрируем блок в проект, у которого есть системный интерфейс для конфигурации регистрового пространства, а также имеется возможность подать входные данные и снять результат. Если нет реального проекта, мы используем минимальный рабочий с дополнительным тестовым окружением: памятью на входе блока, играющей роль входного генератора данных и внутрисхемного анализатора на выходе (ChipScope, ILA, SignalTAP).

Регистровое пространство можно предварительно инициализировать в HDL-коде, но это не всегда удобно, ведь для тестирования работы блока в различных режимах придется получать несколько различных прошивок. Чтобы решить эту проблему, можно использовать так называемые виртуальные пины (Vivado — VIO ILA, Quartus — Virtual Jtag).

Если в финальном проекте планируется использовать процессор для управления вычислениями, то рационально интегрировать все необходимые системные элементы и сразу подключить IP-ядро к системной шине. Это даст не только максимальную гибкость при отладке, но и позволит программистам начать работу над драйвером и управляющим приложением.

Часто для управления вычислениями в FPGA используется обычный ПК. В этом случае на этапе интеграции будут задействованы контроллеры высокоскоростных интерфейсов сопряжения FPGA с системной шиной, таких как PCIe. Для сопряжения нескольких ПЛИС может использоваться, например, стандарт LVDS (Low-Voltage Differential Signaling). При этом все эти контроллеры будут использовать встроенные высокоскоростные последовательные приемо-передатчики Transievers. FPGA-инженер обязан уметь настроить все указанные блоки (или знать, у кого спросить, как они настраиваются).

Другой вариант — использование SoC AMD (Xilinx) Zynq или Intel (Altera) Cyclone — FPGA. Кроме логики, у них «на борту» есть аппаратно-реализованные процессорные ядра (hard cores). Если в итоговом, «боевом» проекте будет использован встроенный процессор, то стоит сразу интегрировать в минимальный тестовый проект ядро процессора с реальными настройками (или близкими к реальным). Если предполагается, что на этом ядре будет запущен Petalinux или аналогичная операционная система, стоит сразу ее сконфигурировать и работать с ней.

Например, при использовании Zynq процессорного ядра нужно экспортировать файл конфигурации в формат, который можно подгрузить в Petalinux. После этого выполнить компиляцию Petalinux и получить linux image, который содержит и конфигурационный файл FPGA, и информацию об адресном пространстве IP-ядер, подключенных к процессорной системе.

Отладка на железе: разбираемся, почему схема работает не как надо

Работа схемы при симуляции (слева) и «в железе» (справа)
Работа схемы при симуляции (слева) и «в железе» (справа)

Получив linux image или конфигурационный файл FPGA, мы переходим к работе с отладочной платой или другим специализированным железом, под которое ведется разработка. На этом этапе для начала стоит убедиться, что ваш модуль действительно отрабатывает как задумано. Обычно для этого используют внутрисхемные логические анализаторы, но также можно программно выгружать и анализировать результаты промежуточных вычислений. 

Само слово «отладка» подразумевает, что в схеме есть ошибки. Даже после окончания этапа верификации. 

На практике бывало, что «в железе» работа схемы отличалась от результатов верификации при одних и тех же входных данных. Так, некорректно описанный триггер или мультиплексор может синтезироваться в защелку (latch), использование которой нарушает STA-анализ. Из-за этого САПР перестает анализировать часть связей между элементами и не выводит их на требуемую тактовую частоту. Причем в этой ситуации Vivado не выдает ошибок и может даже не выдать критических предупреждений. Однако, обычные предупреждения о подобном поведении все-таки будут. Их анализ и расширенный анализ отчетов STA помогают отловить данную проблему.

Часто для выведения схемы на целевую тактовую частоту используют так называемые timing constraints — это дополнительные ограничения, позволяющие упростить выполнение STA-анализа в тех местах, где это возможно по логике работы схемы. Примером могут служить регистры, значения в которых используются схемой только через некоторое время после их загрузки по системной шине. Для облегчения STA-анализа, можно указать САПР не выводить на целевую тактовую частоту связи от данных регистров к схеме.

На этапе отладки важно отсматривать все пути, на которые накладываются timing constraints set_multicycle_path, set_max_delay и set_false_path. При неверно описанных ограничениях, схема может или некорректно работать, или не работать вообще. Иногда при установке внутрисхемного логического анализатора, ошибки в работе схемы перестают возникать. Это верный признак проблем с timing constraints (а иногда — с асинхронными элементами в схеме).

Сопряжение нескольких сложных IP-ядер тоже может привести к проблемам. Тест одного IP-ядра может занимать сутки, так что верификацию нескольких объединенных блоков проводят редко. Из-за этого один блок может, например, передавать данные в момент, когда другой блок занят. Или будет не совпадать формат передачи. Причин может быть множество, но результат один: схема не работает. Хорошая новость: детектировать такие ошибки легко с помощью внутрисхемного логического анализатора.

Не жалейте места для внутрисхемного логического анализатора!

Отдельно хочу сказать о самом неочевидном типе ошибок — «плавающем». Такие ошибки не повторяются от запуска к запуску, а возникают один раз за долгое время работы. Бывало, что они были связаны со сбоями в работе оперативной памяти DDR и FLASH памяти. Иногда к сбою приводил перегрев ПЛИС или, наоборот, просадка напряжения питания. А бывало, даже находили некорректности в работе Vivado и получали подтверждение о существующей проблеме от техподдержки Xilinx. Но, несмотря на это, чаще всего ошибки возникали из-за проблем в архитектуре реализуемой схемы и некоторых неучтенных состояниях логики управления или передачи данных. 

Для отлова любых ошибок советую задавать себе вопрос: «Как такая ситуация вообще могла произойти?». Дальше стройте предположения и проверяйте их корректность либо с помощью внутрисхемного логического анализатора, либо воссоздавая причины ошибок при симуляции схемы. Так вы сможете сравнительно быстро купировать проблему. Еще полезно разматывать «клубок» ошибок с конца: «Почему данные не сошлись? В предыдущем блоке проблем быть не может, а вот в блоке перед ним могла произойти ситуация_1, что в теории может быть причиной проблемы».

Отдельно хочется отметить, что на этапе отладки мы часто используем дополнительное контрольно-измерительное оборудование. Когда тестируют системы связи, специализированный генератор входного сигнала генерирует пакеты трафика, которые через специализированное ПО загружается в ПЛИС. Пройдя все стадии обработки, данные на выходе ПЛИС (макетного стенда) анализируются осциллографами или же векторными анализаторами, которые отражают мощность сигнала в большом диапазоне частот уже после его преобразования из «цифры» в «аналог». Запуск оборудования, трафика и снятие контрольных показаний в разных режимах работы может отнимать довольно много времени при тестировании вручную. Поэтому при использовании контрольно-измерительного оборудования мы по возможности стараемся автоматизировать тестирование, так как программный доступ к функциям оборудования позволяет выполнить те же задачи за минуты.

Сухой остаток

Итого, FPGA-разработка сильно отличается от обычного программирования. Основные отличия:  

  • HDL код представляет собой не последовательность инструкций, а описание цифровой схемы: арифметико-логических элементов и связей между ними. Примерно так же, веб-разработчики описывают разметку страницы на HTML.

  • Многообразие вариантов реализации одной и той же схемы. Хотя тут я юлю: в программировании тоже есть множество вариантов, но в цифровых схемах их больше из-за огромного числа вариантов распараллеливания.

  • Длительность и трудоемкость этапов верификации, отладки и тестирования. 

Если вы всегда мечтали работать с холодным и жужжащим железом, смотреть на его немигающие (или, наоборот, мигающие) светодиоды и несказанно радоваться, когда понимаете, как избавиться от очередной «плавающей» ошибки, то эта профессия вам понравится.  

Резюмируя вышесказанное, можно провести аналогию между FPGA-разработчиком, совмещающим в себе несколько ролей одновременно, и многоруким Шивой. Зачастую FPGA-разработчик и разрабатывает IP-ядра, и верифицирует, и интегрирует их в конечную систему, и отлаживает систему на «железе». А еще он немного маг, когда дело касается отрицательных значений Slack.

Если у вас есть разделение труда (или деление на программистов всех мастей, верификаторов, разработчиков, интеграторов, тестировщиков и так далее, как это устроено в команде РЧЦ YADRO) — это идеальный мир. Конечно, это не значит, что вы никогда не будете заниматься всем этим понемногу, но вы точно сможете сконцентрировать ваше внимание на главной задаче, а не распылять его на несколько высокоприоритетных. Тут всегда будет выигрыш, если не в качестве решения, то в качестве жизни из-за отсутствия стресса. 


Это была обзорная статья, в которой мы вскользь пробежались по верхам FPGA-разработки. В следующий раз планирую подробно и наглядно рассказать про этап разработки IP-ядра: будут микроархитектурные решения и примеры оптимизации схем различного рода. Ну и очень хочется поделиться некоторыми хитростями организации HDL-кода (спойлер: они такие же, как и у программистов, но другие).

Если вы хотите попробовать свои силы в FPGA и ASIC-разработке, приходите на соревнования по разработке СнК. Хакатон ежегодно проходит в Зеленограде в МИЭТ при поддержке YADRO. В этом году он состоится 24–26 апреля, но регистрация уже завершилась, так что ждем вас в следующий раз!

Заинтересованным студентам старших курсов предлагаю прийти на летнюю стажировку «Импульс». В этом году двое ребят осваивали «курс молодого бойца» в нашем отделе: отрабатывали навык разработки IP-ядер, их верификации и отладки.

На любые вопросы буду рад ответить в комментариях. Всем положительных Slack!