В моем недавнем проекте обработки звука на Hackster.io я создал простой IP-блок High Level Synthesis (HLS), к которому можно было добавить фильтрацию и эффекты. Чтобы гарантировать, что это IP-ядро будет взаимодействовать с IP-ядрами I2S TX и RX от Xilinx, мне нужно было создать потоковые интерфейсы AXI в IP-блоке HLS.

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

Мы собираемся начать с самого начала с простого IP-блока HLS, который выполняет простое добавление. Для всех этих примеров мы будем ориентироваться на Zedboard.

В HLS интерфейсы синтезируемого блока определяются аргументами, которые мы передаем нашей функции C / C ++ вместе с любыми возвращаемыми параметрами. Направление зависит от того, как используются в функции; хотя, если мы используем указатели или массивы, их можно использовать как ввод-вывод.

Помимо входов и выходов, в синтезированном IP-блоке HLS также будет создан интерфейс ap_cntrl. Этот интерфейс управления обеспечивает синхронизацию, сброс и квитирование, например, позволяя запускать блок (ap_start), сообщая, когда результат готов (ap_done), когда блок готов принять новый ввод (ap_ready), и если блок простаивает (ap_idle).

Однако даже поведение интерфейса ap_cntrl зависит от настроек нашего решения и самого кода.

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

Таким образом, тактовый период, определенный в настройках решения, будет влиять на синтез и взаимодействие. Приведенный ниже очень простой код при синтезе с тактовым периодом 100 нс приведет к комбинаторной реализации.

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

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

Таким образом, этот простой порт ap_cntrl дает нам возможность работать с IP-блоком HLS в нашем проекте и по этой причине называется протоколом уровня блока.

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

Здесь на помощь приходят протоколы уровня порта, и для них важен тип конструкции C, используемой для переменной. Протоколы уровня порта позволяют нам определять для каждого порта в блоке HLS определенный протокол интерфейса, такой как AXI Lite, AXI, FIFO, BRAM и т. Д.

Обновив простой пример выше для использования массивов, мы можем изменить интерфейсы для использования интерфейсов типа FIFO. Мы определяем этот тип интерфейса с помощью прагмы:

#pragma HLS interface ap_fifo depth = ‹depth› port = ‹port›

Чтобы определить тип интерфейса, мы можем либо ввести их вручную, как указано выше, либо использовать окно директив в правой части окна HLS.

Поскольку это интерфейс уровня порта, нам необходимо определить его для каждого из интерфейсов в блоке HLS.

Когда мы запустим это через HLS, мы увидим сгенерированный сводный отчет, который определяет реализованные интерфейсы. Как видите, реализованы интерфейсы FIFO.

Интерфейсы FIFO интересны, когда мы хотим передавать данные между модулями HLS.

Однако часто мы хотим реализовать интерфейсы, которые используют разновидность AXI (s_axilite, axis, s_axi, m_axi). Мы можем использовать тот же подход для реализации интерфейсов главного и подчиненного устройств AXIS и AXI.

Если мы работаем с гетерогенной SoC, такой как Zynq или Zynq MPSoC, мы можем захотеть использовать AXI lite для управления IP-блоком HLS, а не интерфейсом дискретных блоков. Мы можем сделать это, используя прагму, как показано ниже:

#pragma s_axilite port = return bundle = cmd

Это не только создаст интерфейс AXI lite, который позволяет нам управлять IP-ядром HLS, но и сгенерирует программные драйверы, необходимые для работы с IP-ядром HLS в SDK.

Если нет необходимости останавливать и запускать IP-ядро HLS, мы можем использовать прагму:

#pragma HLS interface ap_ctrl_none port = return

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

См. Мои проекты FPGA / SoC: Адам Тейлор на Hackster.io

Получить код: ATaylorCEngFIET (Адам Тейлор)

Получите доступ к архивам MicroZed Chronicles с более чем 280 статьями о Zynq / Zynq MpSoC, обновляемыми еженедельно в MicroZed Chronicles.