_______________
__________________
_____
_______________________
_______________________
____________________
____________________
_______________
__________________
_____
_______________________
_______________________
____________________
____________________

Engee: Генерация Verilog без рутины

Главная / Мероприятия / Engee: Генерация Verilog без рутины
24 сентября 2025 10:00
Применение кодогенерации для проектов систем связи и цифровой обработки сигналов
Зарегистрироваться
Онлайн
Вебинар
Юрий Юрьев
Спикер
Инженер ЦИТМ Экспонента в сфере цифровой обработки сигналов
Смотреть запись вебинара
Вебинар демонстрирует возможности платформы Engee. Ознакомьтесь с ней заранее!
О вебинаре
Устали тратить месяцы на ручное RTL проектирование в FPGA и ASIC проектах? Ошибки на этапе написания кода, долгая отладка и бесконечные правки — знакомо?
Engee избавляет вас от рутины, автоматизируя генерацию чистого и оптимизированного Verilog. Пишите математические модели — а код создастся сам.
На вебинаре разберём опыт одного из наших клиентов и на его основе посмотрим, как быстро создать приёмник и передатчик, минуя ручное RTL-проектирование.
  • Сократите сроки разработки в разы — вместо недель кодирования получайте готовый RTL за часы.
  • Избежите скрытых ошибок — встроенные инструменты проверяют код до синтеза.
  • Оптимизируйте ресурсы — Engee улучшает логику, чтобы ваш дизайн работал эффективнее.
  • Работайте на уровне алгоритмов, а не триггеров — сосредоточьтесь на идее, а не на рутинном коде.
Почему это важно для ваших проектов?
Генерация Verilog
Примеры
Типы данных с фиксированной точкой
Общие понятия, генерации кода
Автоматическая оптимизация кода
Отладка и тесты кода
Итоговый пример проекта
План вебинара
Пользовательские
шаблоны
Fixed-Point
Генератор кода Engee
Помимо этого сама Engee поддерживает верификацию сгенерированного кода как при помощи симулятора, так и при помощи генерации исполняемого файла, который мы можем подключить к исходной модели.
  • быстрый,
  • компактный,
  • человекочитаемый,
  • переносимый,
  • независимый от Engee,
  • трассируемый к модели,
  • пригодный для промышленного использования.
Сгенерированный код:
Спикер
  • Работает с Engee и MATLAB с 2016 года.
  • В 2022 году получил степень магистра по специальности «Прикладная информатика», в 2020 году получил степень бакалавра по специальности «Информатика и вычислительная техника».
Инженер ЦИТМ Экспонента в сфере цифровой обработки сигналов, разработчик под ПЛИС, занимался обработкой изображений, видео, аудио, машинным обучением.
Юрий Юрьев
Запись вебинара
Регистрация в Engee
Скачать материалы в сообществе
Текстовая версия вебинара
Вступление
Сегодня мы с вами рассмотрим генерацию кода Verilog в Engee.

Какие вопросы будут затронуты:
  • Реализация фиксированной точки в Engee, типы данных, которые мы можем применять под генерацию кода.
  • Как устроен сам генератор кода. Из каких частей он состоит, что в себя включает, как реализован этот подход, преобразование модели в итоговый код, который мы можем запустить на ПЛИС.
  • Оптимизация кода в модели. Методы, применяемые к анализу модели на этапе до выполнения компиляции кода. Мы часть логики отбрасываем и разберем, как раз по какому принципу эта логика отбрасывается.
  • Пользовательские шаблоны Рассмотрим возможности реализации своих алгоритмов под генерацию кода, то есть подход с точки зрения того, что мы можем взять готовый блок и написать под него свою реализацию, как под уже имеющиеся блоки, которые поддерживают генерацию кода, так и под блоки, которые на данный момент не поддерживают генерацию кода.
  • Несколько методов верификации кода.
  • Выводы.

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

В чем преимущество модельно-ориентированного подхода?
Во-первых, преимущество — это прототипирование системы в модели. Мы на этапе набрасывания алгоритма уже понимаем насколько он рабочий, насколько он имеет место быть. По сути, в процессе создания модели уже большую часть багов логических и так далее отлавливаем. При этом мы не затрачиваем время на описание какого-то бесполезного рутинного кода, а просто используем готовые блоки, готовые шаблоны.
Во-вторых, в таком подходе, понятное дело, что код мы не пишем, а генерируем из модели.
В-третьих, за счет именно генератора код получается оптимизированным и вдобавок будет полностью соответствовать тому, что мы реализовали в модели. В принципе, такой подход убирает большую часть ненужного тестирования и ненужных проверок.
Реализация фиксированной точки в Engee
Давайте теперь перейдем к следующей части, а именно к самой Engee, и рассмотрим возможности фиксированной точки.

Итак, первый пример касается одной команды, то есть команды fi (Value, Sign, Total_bits, Fractional_bits). Она позволяет нам привести число с плавающей запятой к числу с фиксированной точкой. Данная команда, наверняка, многим, кто работал с MATLAB, очень хорошо знакома.
  • Value — это значение самого числа. Либо оно задается как число без дробной части, либо число с дробной частью. 
  • Sign — это выделяем ли мы бит на знак. То есть, если 1, значит число со знаком, соответственно, может быть как положительным, так и отрицательным. 
  • Total_bits — это суммарное количество бит на все число.
  • Fractional_bits — это количество бит на дробную часть.
Соответственно, из этой команды мы можем понять, что количество бит, выделяемое на целую часть рассчитывается автоматически исходя из общего количества бит минус бит на знак минус количество бит на дробную часть.
Ниже приведен простой пример.  Есть число 128,9, я выделяю 1 бит на знак, 16 бит суммарно все число и 7 бит на дробную часть.
Давайте разберем несколько примеров более подробных.
Первый пример — это 0,8 беззнаковая, 7 бит суммарно и 7 бит на дробную часть. На целую часть в данном числе мы ничего не выделяем. Мы получаем число, максимально приближенное по шагу квантования к исходному значению.
Если мы берем и к этому числу прибавляем число с другим количеством бит на целую часть, на дробную часть, то в результате автоматически будет перерассчитан тип данных. Как мы видим, в данном случае уже на целую часть выделено большее количество бит, а во-вторых, на дробную часть относительно второго числа выделено тоже большее количество бит, то есть оно унаследовано из первого числа.
Более интересный момент, если мы умножаем на просто целое число. И как мы видим, здесь очень интересная магия произошла. Мы получаем 72 бита. Откуда такие большие значения? Здесь это связано с тем, что по умолчанию в Engee числа без дробной части имеют тип данных int64. Как мы все знаем Engee написана на Julia, а Julia — это компилируемый язык, он очень чувствителен к типам данных и старается максимально оптимизировать свои ресурсы. Отсюда и такой интересный результат.
Если мы хотим получить более аккуратный результат, то нам надо с вами использовать тройку тоже с фиксированным количеством бит. Если мы берем 3, то здесь нам не нужен ни бит на знак, ни бит на дробную часть, нам нужно всего 2 бита на целую часть. Соответственно, мы могли получить здесь значение в разы меньше, чем мы получили по количеству бит.
В еще одном примере мы уже берем изначально число больше по битам и по знаку, и по общему количеству бит, чем исходный наш x. Получаем здесь по битам прирост в 1 бит. Откуда он берется? По умолчанию, дабы точно ничего не потерять, реализован такой алгоритм. Он точно так же реализован и в MATLAB, то есть каждый раз, когда мы не явно указываем тип, в данном случае мы просто берем 2 числа с фиксированной точкой. Выход мы не определяем как новый тип, поэтому 1 бит добавляется к числу.
И еще один пример — это конвертация типов данных. Мы точно так же можем переназначить тип данных. Мы можем всю конструкцию обернуть в новый тип данных и получить новое значение по количеству бит на символ.
Здесь мы увидим один интересный нюанс. Во-первых, как мы видим, модель у нас в фиксированной точке, часть ее касающаяся ПИД-регулятора, и данные логируются в CSV-файл, то есть логируются фиксированные точки в CSV. После этого по результатам выполнения умножения на 1 мы опять возвращаем в float64, то есть плавающую запятую, и точно так же логируем. Какой интересный момент я хотелось бы показать.
Давайте далее разберем пример модели, где применяется тот же подход.
Почему так? Давайте рассмотрим этот пример более детально, параметры мы уже разобрали. Во-первых, мы проверяем, входит ли наша целая часть в диапазон выделенных под нее бит. И да, действительно, наше число находится в этом диапазоне, на целую часть нам хватает бит. Если мы берем дробную часть, то, соответственно, она рассчитывается относительно шага квантования. После этого мы рассчитываем, сколько шагов квантования мы можем выполнить для того, чтобы максимально приблизиться к нашему значению. И формируем итоговое битовое представление нашего числа. Отсюда и такой результат.
Мы по результатам данных преобразований сохраняем все в CSV, и если мы посмотрим, после вычитывания данных из CSV-файла, который касается fix-point-а, мы получаем все тоже в float64. Здесь такой момент, то что внутри модели эти типы жестко задаются, и во время решения они применяются. Но когда мы начинаем их логировать в какие-то файлы, они обратно конвертируются в float64 и так и записываются.

Для чего это сделано? Это сделано для того, чтобы такие файлы было легко читать. То есть не добавляется никакая ненужная обвязка — сколько бит, на какую часть и так далее. При таком примере мы получаем идентичные результаты что для числа с плавающей запятой, что для числа с фиксированной точкой. В этом подходе между ними нулевая ошибка.
Как устроен генератор кода
Здесь взят просто какой-то блочок, описаны его входы-выходы и несколько действий, которые мы применяем к этому блочку. Слева код на CHISEL, справа код уже сконвертированного в Verilog. Видно явно, как происходит конвертация, что и откуда берется. Через командное управление вы точно так же можете сгенерировать не Verilog код сразу, а, например, код CHISEL и посмотреть, как у вас вообще собирается модель, и насколько ваш шаблон или ваша модель корректно генерирует код.
Далее давайте мы с вами разберем, как устроен сам генератор Verilog кода и что он из себя представляет. Генератор Verilog кода состоит из трех основных частей. Самой Engee, то есть модели, которую мы с вами создали. Дальше она конвертируется в CHISEL. И только потом идет этап конвертации Verilog.

Зачем нужна вот эта вот прослойка из CHISEL? Во-первых, легче код Julia конвертировать именно в более высокоуровневый язык, а после этого уже реализовать его переход в Verilog, потому что он как раз для этого и предназначен.
Что дает такой подход? Такой подход дает несколько ключевых преимуществ.
  1. Мощные абстракции для высокоуровневого описания. Это преимущество касается больше разработчиков, либо тех, кто будет затрагивать именно разработку пользовательских шаблонов. Код легче написать. Мы просто тратим меньше времени на написание кода.
  2. Все функциональные возможности Scala поддерживаются CHISEL. Когда мы генерируем код, мы можем через командное управление сгенерировать код CHISEL и увидеть, что файлик имеет расширение Scala. Те, кто сталкивался с этим, знают, что CHISEL — это ответвление от Scala.
  3. Удобная объектно-ориентированная модель проектирования. Каждый объект можно представить впоследствии в виде модуля. Легко описать входные и выходные порты, легко описать логику внутри модуля и так далее.
  4. Это гибкость от высокоуровневых описаний до работы с битами. Мы можем опускаться при необходимости на максимально низкий уровень.
  5. Есть множество уже готовых шаблонов, готовых встроенных примитивов, которые мы можем внедрять. Например, такой примитив, как Mux.
  6. Готовая поддержка Fixed Point. Легко перевести модель чисел, а чисел — Verilog, потому что уже реализована конвертация по типам данных.
  7. Автоматическая оптимизация результирующего кода. Когда мы конвертируем код чисел в Verilog, мы еще отсеиваем некоторую лишнюю часть касаемо ресурсов, касаемо логики. Это происходит автоматически. Соответственно, наш код будет более ресурсоемкий.

Вот небольшой пример того, как выглядит генерация.
Оптимизация кода в модели
Следующий вариант, который мы рассмотрим, — зададим один из входных портов. Я поставил на выходе терминатор, то есть заглушку.
Теперь давайте перейдем к примерам и разберем конкретные возможности и оптимизацию моделей. Модель, которую мы теперь будем рассматривать, это модель, в которой достаточно простая логика.
Вот мы сгенерировали код. Что у нас внутри? А внутри у нас, по сути, ничего. У нас есть описание модуля, описание Clock-ов, Reset’ов и одного единственного выхода. Причем выход изначально жестко задан как 1, то есть true. Почему так? Потому что вся наша логика определяется однократно внутри блока, и больше ничто, никакие входы и выходы на нее не влияют.
Типы данных — посмотреть. Как мы уже убедились, далеко не все типы данных позволяют генерировать Verilog, работать на ПЛИС. Первый тип данных — это фиксированная точка. Второй тип данных — это булевые значения. И третий тип данных — это INT-овые или UINT-значения. В принципе, мы можем работать либо с целыми числами, либо с бинарной логикой, либо с фиксированной точкой. Если мы где-то оставим плавающую запятую, этот код мы не сможем из модели сгенерировать в код.
Второй момент — размерность сигналов. При генерации кода под ПЛИС у нас конвейерная обработка, и мы должны понимать, что, во-первых, либо мы используем посимвольные вычисления, либо, если мы берем вектора, то это параллельные вычисления, и, логику надо формировать именно под это.
И третий момент — это частоты дискретизации. Во-первых, они должны быть везде одинаковые, то есть генерация кода не поддерживается под разные частоты дискретизации. А во-вторых, они должны соответствовать шагу решателя. Если у нас будет расхождение между шагом решателя модели и частотой дискретизации на конкретных блоках, мы тоже получим ошибку.

Давайте начнем с первого примера. Сгенерируем код и посмотрим, что мы получим.
3 константы. В таком подходе мы уже получим всю нашу логику, ничего не потеряв. Как мы видим, теперь у нас есть 3 входа, помимо Reset и Clock. И есть логика получения выхода. Мы берем первый вход и второй вход, или второй вход и третий вход. Как раз полное соответствия нашей логики.

Надеюсь, теперь вам понятно, как работает оптимизация в самих моделях.
И следующий момент, который мы рассмотрим, это нормально созданный код.
Как мы видим, результат идентичен первому. С чем это связано? Это связано с тем, что мы используем на выходе заглушку. Он, в принципе, в таком подходе не анализирует логику. Он просто задает пустой модуль.
Что это значит? То, что у этой логики нет выхода. Давайте запустим и тоже посмотрим результат.
Здесь просто есть два логических «И» и логическое «ИЛИ», то есть какая-то логическая операция над входными данными. Первая подсистема внутри себя имеет константы. Что это нам дает? Мы внутри задали значение. По результатам всей этой логики, как мы видим, верхний AND даст false, нижний AND даст true, OR даст true. На выходе мы будем получать значение true. Давайте посмотрим параметры генерации.
У нас стоит target Verilog. Мы можем задать параметры оптимизации генерируемого кода, можем включить-выключить комментарии. Плюсом, мы можем при генерации создать C-функцию. Об этом я поговорю попозже. Это один из вариантов верификации кода. Я объясню, как он работает и что он из себя представляет. В плане отладки для таких моделей стоит включать вот эти все параметры:
Шаблоны генерации кода
Здесь взят очень простой пример. Его главная задача — показать принцип, что в себя включает вообще шаблон. Шаблон — это смесь кода Julia и CHISEL, то есть, это такое метапрограммирование CHISEL. Мы описываем текстом код, который должны сгенерировать и делаем в него вставки различных параметров из нашей модели, из описания блока. В данном случае все очень просто. Эта реализация не учитывает типы данных. У нас всегда берется WIND8. Есть оператор сравнения и есть два входа. На выходе мы получаем результат оператора сравнения.

Как мы видим, слева у нас представлен код CHISEL. В нем как раз описаны входные порты и выходной порт, булевое значение, результат сравнения. Дальше описана сама логика и выход порта.

В Verilog коде мы видим все то же самое. Мы видим то, что у нас заданы типы для входных портов, задан тип для выхода. Добавляется только clock reset и наша логика сравнения. Первый ход меньше либо равен второму. На выходе мы получаем значение true/false.
Следующий этап, который мы рассмотрим, это шаблоны генерации кода. Есть возможность реализовать свои пользовательские шаблоны для блоков и применять их в своих проектах.
Несколько методов верификации кода
Начнем с того, что сам подход модельно-ориентированного проектирования позволяет на этапе модели уже определить, насколько корректно работает наша логика. Дальше, когда мы генерируем код, у нас есть несколько вариантов.

Первый вариант — это сгенерировав код Verilog, воспользоваться и Icarus Verilog и написать свой testbench. Сейчас в разработке находится генерация testbench из модели. Со временем его и писать не придется. Просто запустить симуляцию, посмотреть на то, насколько там результаты совпадают с моделью. Построить графики или просто вывести какие-то числовые значения, посмотреть, сравнить Verilog и нашу модель.

Второй вариант — это воспользоваться Verilator. Это тоже программное обеспечение, позволяющее преобразовать Verilog в C. C-код мы можем встроить уже обратно в модель Engee и сравнить внутри модели два блока. Блок логики, соответствующий сгенерированному коду, и блок исходной модели. Такой подход тоже имеет право на существование. Удобнее в модели сравнить, потому что не придется ничего дописывать.

Третий вариант — это сразу тестировать не в симуляторах и не в моделях, а загрузить код на ПЛИС. Для этого мы уже покидаем Engee и идем к какой-то конкретной платформе и к её Программному Обеспечению, которая предоставляет эту платформу.

Первый пример, который мы по этой теме разберем — это пример реализации счетчика.
Следующий момент, который мы далее будем рассматривать, это верификация сгенерированного кода. Здесь есть несколько вариантов верификации. Мало просто увидеть какие-то буквы. Надо понимать, насколько эти буквы имеют место быть и насколько вообще результат корректен. Главная цель верификации — это убедиться, что сгенерированный код работает корректно.
Причем, каждый блок я сгенерировал отдельно. То есть, что счетчик и фильтр — отдельно. Давайте посмотрим, что у нас в блоке счетчика.
Я начну с того, что мы делаем. Мы изначально будем выполняем модель, логируем из нее выходные порты и далее мы будем сравнивать сгенерированный код с этими выходными портами. Давайте посмотрим на модель. Я заранее сгенерировал код из блоков.
Итак, код был сгенерирован. Мы рассмотрим еще одну галочку в настройках генератора кода, а именно галочку создания C-функции. В нашем варианте папки появился не только файл Verilog, но и какие-то еще дополнительные файлы. Что у нас здесь? У нас здесь просто логика описания нашего счетчика. Давайте посмотрим, что в остальных файлах.
Здесь прописаны пути до папки, в которые мы сгенерировали этот код. Мы можем дальше запускать эту модель.
Я сделал предварительно аналогичную модель. Там есть все те же файлики и тестовая модель. Просто я собрал воедино блок C-функции и наш исходный счетчик. Давайте запустим эту модель и убедимся, что и тот, и другой алгоритм работают корректно.
Перейдем к сгенерированному блоку. Давайте посмотрим, что из себя представляет эта модель. Модель из себя представляет просто блок C-функции с входными и выходными портами и параметрами подключения к нему библиотек.
Первый файл — это файл .jl, который создает нам тестовую модель. Давайте его запустим. Пока он выполняется, посмотрим, что у нас в папке. В папке у нас файл библиотеки C, который подключает к себе модуль и сам исполняемый файл.
Итак, код был сгенерирован. Мы рассмотрим еще одну галочку в настройках генератора кода, а именно галочку создания C-функции. В нашем варианте папки появился не только файл Verilog, но и какие-то еще дополнительные файлы. Что у нас здесь? У нас здесь просто логика описания нашего счетчика. Давайте посмотрим, что в остальных файлах.
Данный пример на самом деле очень простой. У нас есть несколько входных портов. У нас есть порт предела счетчика, есть порт Reset и есть порт шага. Мы выполняем превращение счетчика до тех пор, пока не придет Reset или не достигнут предел. В принципе, вот она вся логика. Давайте сгенерируем этот код.
Если мы посмотрим на код счетчика, то тоже ничего особенного.

Итак, давайте допишем тест-бенч, я его заранее написал. Он очень простой. Все, что в нем есть, это задание Clock-ов, Reset-ов и подключение модулей. На выходе я логирую данные в таблицу по результатам. Результаты работы усредняющего фильтра.

Запуская нашу модель, мы здесь генерируем код. Я генерировал код через командное управление. Давайте еще раз на нем остановимся.
По результатам мы видим то, что они похожи друг на друга. С тестбенчем я не особо заморачивался. Возможно, что-то по тактам или по Reset’ам съезжает. Отсюда и возникают вот эти небольшие погрешности в начале. Но, как мы видим, в конце он устаканивается и ведет себя корректно.

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

Что у нас здесь есть? У нас здесь есть шаблон для мультиплексора, для компайера. Компайер, если вы помните, я уже показывал. Здесь уже реализована вся логика под Fixed Point, реализованы все варианты параметров (больше, меньше, равно и так далее), и реализована сама логика того, как в зависимости от портов меняется выход. Multiport switch использует шаблоны для обычных switch-ей и разбивает нашу конструкцию на набор отдельных switch-ей. Product в данном случае вообще самый простой блок. Просто умножение, дальше таргет чисел и, соответственно, мы умножаем один вход на другой вход.

Давайте откроем модель, посмотрим, что у нас внутри модели.
Здесь просто генерируются биты псевдо случайным образом. Причем в одном генераторе 4 задержки, в другом 3. И все с разными параметрами инициализации, достаточно такая случайная последовательность получается. Мы явно можем отследить, как работает наш алгоритм. Valid здесь всегда равен 1, то есть нет никакой логики для Valid-ов. Valid просто идет от блока к блоку. И просто в fir-фильтр.
Здесь у нас есть 2 бита на входе, QPSK-модулятор.
В сгенерированном коде мы видим аналогичную логику. Здесь идет логика описания задержек, описания сумматоров. В принципе, код вроде как похож на правду.
Это усредняющий фильтр c окном фильтрации 10, 10 отчетов мы наращиваем в задержку, далее делим на 10, и получаем усреднение по 10 значениям. Давайте посмотрим на то, что мы здесь имеем в генерированном коде.
Счетчик реализован аналогично предыдущему за исключением того, что нету никаких входных портов. Все данные задаются внутри него. Говоря о фильтре, фильтр задан вообще очень просто.
Уже когда разбирали CHISEL, немного обратили на него внимание, то разберем команду. Она идентична, что для C, что для Verilog, что для CHISEL. У нас есть первый параметр, это модель, из которой мы будем генерировать код. Второй параметр, это куда мы будем генерировать код. Третий параметр, если он не указан явно, то target наследуется из модели, либо мы точно так же можем указать target и выбрать, какой язык мы генерируем из модели. И последнее — это из какой подсистемы в нашей модели мы генерируем код.

Если мы говорим о запуске нашего тестбенча в Icarus-e, то мы должны перейти в папку проекта, где у нас находится тестбенч, и все файлы, которые к нему подключаются — подключить, начиная с заголовочного. В данном случае тестбенч, понятное дело, заголовочный, и к нему подключается фильтр и счетчик. Мы компилируем этот проект и выполняем его симуляцию.
Первый график, который мы здесь строим, это созвездие. Мы можем убедиться, что созвездие в модели идентично созвездию тест-бенча Verilog, по точкам полностью совпадает. Дальше мы берем реальную часть сигнала. Она идеально совпадает. То же самое с мнимой частью и, с валидами. Что позволяет убедиться в том, что наш алгоритм работает корректно и готов к переносу на ПЛИС.
Что у нас здесь происходит? Мы отдельно запускаем нашу модель и отдельно запускаем код, полученный из модели. В коде у нас только тест-бенч и общий модуль для всех подсистем. По результатам запуска мы выполняем парсинг данных. То есть, в тест-бенче мы сохраняем в текстовый файл все данные, которые мы получаем на выходе. А внутри Engee-скрипта мы уже выполняем именно обработку этих данных и выполняем их конвертацию.
Как мы видим в случае с атомарной подсистемой у нас отдельно модули fir-фильтра, отдельно модуль генерации данных и отдельно модуль QPSK-модулятора. Причем есть общий файл, который объединяет все эти модули. Что он в себя включает? Он включает общие входы-выходы. В данном случае, понятное дело, только выходы и блок Reset и подключение последовательное каждого из модулей.

Если же мы берем файл, в котором мы не использовали атомарные подсистемы, соответственно, здесь вся логика, она находится в одном файле. Как-то переиспользовать это не особо удобно, мы переиспользуем только целиком блочок. Такое различие, которое может помочь вам структурировать ваш проект. Если мы берем какую-то подсистему атомарную, соответственно, она будет отдельным модулем, иначе мы получим просто сплошной код. В данном случае в тест-бенче я писал под систему без атомарного подхода. Для чего? Просто, чтобы как можно меньше в Icarus Verilog вызывать файлов.

Также все эти демонстрации, которые я сегодня разбираю, вы можете увидеть в сообществе.
Как мы видим, вторая подсистема называется Atomic. Что это значит? Это значит то, что каждый из блоков этой подсистемы воспринимается как атомарный блок. То есть каждая подсистема является атомарной и независимой друг от друга. При генерации кода каждый блок будет восприниматься как отдельный модуль во втором случае. А в первом случае вся логика будет использовать один общий модуль. Давайте сгенерируем код для обоих вариантов и сравним результаты.
Есть есть два блока fir-фильтра. И есть задержка по Valid-ам на те же 11 тактов. Из всех этих блоков генерируется код.

Что я хотел в этом примере в первую очередь показать? Я хотел показать различие подходов к генерации.
Выводы
Подводя итог, стоит обратить внимание на то, что мы разобрали подход к модельно-ориентированному проектированию и возможности генерации кода Verilog. Если делать краткие выводы, то Engee экономит недели разработки просто потому, что мы можем накидать все в виде блоков, протестировать на моделях и просто сгенерировать код, который уже дальше где-то будем использовать.

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

В-третьих, мы снижаем таким образом стоимость отладки, потому что элементарно проще собрать модель, чем написать какой-то тестовый код. Проработка концепции, проработка идей, она осуществляется внутри модели. Мы экономим много времени на написание ручного кода. И если отвечать на вопрос, что мы получаем за счет кода генерации, мы получаем меньше кода, больше наглядности, больше возможностей.

В завершении также хотел бы поделиться некоторыми ресурсами. Во-первых, переходите в Engee, регистрируйтесь. Вы можете сами перейти на сайт Engee и зарегистрироваться. Для вас есть бесплатная лицензия, она ограничена только по часам. Вы можете ей спокойно пользоваться, протестировать все, что я сегодня показывал и посмотреть, что из себя представляет продукт.

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

Также переходите в наш телеграм-канал, там мы выкладываем анонсы различных мероприятий, самые интересные примеры, статьи. Следите за новостями в телеграм-канале, там много интересного. Если остались какие-то вопросы, вы можете обратиться на нашу почту. Мы открыты для сотрудничества, готовы помочь вам и с освоением Engee, и с реализацией каких-то коммерческих задач. То же самое и в плане просто каких-то вопросов, мы всегда рады на них ответить. Спасибо за внимание.
Популярные вопросы
Если у вас остались вопросы —
обращайтесь к менеджеру мероприятия
Яна Степко