Найти тему
ПЛИСовод.

Первый проект на Verilog: используем VSCode

В этой статье мы прикрутим к проекту из предыдущей статьи самую настоящую проверку работоспособности без заливки на железо, а также заменим скучный Quartus на Visual Studio Code, при этом научим её выполнять следующией действия:

  • Подсвечивать синтаксис Verilog
  • Подсвечивать допущенные в коде ошибки (любимое)
  • Использовать автодополнение
  • Запускать тесты и отображать результаты симуляции
  • Компилировать проект и заливать на плату без UI Quartus-а.

В прошлой статье был создан проект на Quartus, а также было разобрано проектирование такого простого устройства, как 4-х кнопочное пианино (убьют меня музыканты за то что я свою пищалку так назвал 😀).

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

Но если вы прошли этот путь вместе со мной, то должны были уже отметить некоторую несовременность Quartus-а: и визуальный стиль из начала двухтысячных, и возможности редактора лишь чуточку превосходящие блокнот, да и всякие плюшки типа автодополнения в нём не работают.

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

Значит качаем и устанавливаем Visual Studio Code, если вы его до сих пор не установили.

Для работы с Verilog потребуется вот это дополнение. Оно не единственное возможное, но пожалуй самое лучшее из того что есть на сегодняшний день.

Так что прямо с web-страницы приложение жмём Install, далее Continue в появившимся окне, и Открыть приложение, на запрос браузера. Страничка расширения откроется в VSCode, где снова надо будет нажать Intall. Всё, приложение установлено, но ему требуется кучка посторонних программ для работы.

Первое - и основное для нас - это Icarus Verilog. Вот только качать его приходится с сайта билдов под Windows. Качаем, устанавливаем, не понимаем что это такое и как это запускать. Сразу скажу - icarus является консольной утилитой, но ручками из консоли запускать его мы не будем - всё за нас будет делать VSCode.

Второе - это ctags. Он даст минимальный autocomplete. Качать нужно отсюда. Тут уже не будет даже установщика. Просто создайте себе где-нибудь папку, которую вы не удалите случайно (можно в Program Files, например) и свалите туда содержимое архива.

Последнее - языковой сервер svls. Добавит предупреждений на кривом коде. Качается отсюда. Он ещё смешнее, чем ctags, так как представляет из себя просто единственный exe-файл. Я рекомендую запихнуть его в ту-же папку, что и ctags.

Эти программы были необходимы для работы расширения. От себя добавлю такую пару:

Поскольку я использую makefile в проектах, а VSCode здорово умеет их запускать, то рекомендую поставить Make for Windows. Непосредственная ссылка на скачивание тут.

Python 3. Качать и ставить с официального сайта

Одной лишь установки недостаточно, расширение для VSCode требует, чтобы iverilog и svls были доступны из командной строки с любого места - то есть находились в папках, указанных в PATH. Ну и python с make тоже удобно, когда есть из командной строки. Да и сам Quartus. Сделаем это.

В Windows 10 для этого надо набрать в окне поиска:

Изменение системных переменных среды

Но можно и не вписывать всё целиком, главное чтобы появился нужный пункт
Но можно и не вписывать всё целиком, главное чтобы появился нужный пункт

В Windows 8 делается как и в 10, только там нет окна поиска, нужно было нажать на пуск и начать вбивать нужное. В Windows 7 и ранее это можно было сделать из панели управления, вкладки "система", примерно вот так:

У меня вкладка от Win 10, но разница не велика.
У меня вкладка от Win 10, но разница не велика.

Нужно было на этой вкладке нажать на Дополнительные параметры системы, а в открывшемся окне - Переменные среды...

Так или иначе, у вас откроется подобное окно: в нём будет 2 списка - переменные среды пользователя, и системные.

-4

Если внести изменение во второй список, то ваши изменения коснуться всех пользователей компьютера (если их несколько), но будут доступны лишь после перезагрузки. А для применения изменений в первом списке достаточно закрыть и открыть нужную программу. Поэтому я рекомендую первый, в нём ищем параметр Path. Щёлкаем по нему, и видим окно-редактор.

-5

Я не помню точно, с какой именно версии Windows появилась такая красота, в Win XP уж точно такого не было. Там была строка ввода на 1 строчку, в которой всё подобное содержимое было вписано через точку с запятой. И да - это была чертовски длинная строка, которая в поле не вмещалась, приходилось копировать содержимое в какой-нибудь блокнот/sublime/vscode/... редактировать и вставлять обратно. Но в Windows 10 полноценный редактор списков, возможно в Windows 7 тоже, не помню. И в нём нужно добавить пути к установке всех 3-х программ: Icarus Verilog, ctags, svls. На моём скриншоте вы можете видеть:

c:\User\alexx\.local\bin - а сюда я сгрузил ctags и svls
c:\iverilog\bin - это путь к icarus verilog
c:\iverilog\gtkwave\bin - это путь к gtkwave, которая идёт в комплекте
F:\intelFPGA\18.0\quartus\bin64 - путь к bin папке quartus
c:\Program Files (x86)\GnuWin32\bin - тут make для Windows
c:\Users\alexx\AppData... - пути к питону и его скриптам

Вот эти вещи нужно добавить и вам тоже. Надеюсь на вашу сообразительность, вы не должны копировать мои пути, а ставить свои к соответствующим папкам.

Вот честно - на macOS или linux всё то-же самое, что мы уже абзацев 10 как делаем - делается за 30 секунд в пару команд из консоли...

С установками программ всё, далее серия, ОК, ОК, ОК, закрываем и заново запускаем Visual Studio Code, доставим ещё пару расширений попроще и перейдём к настройкам.

По желанию можете настроить русский язык интерфейса VSCode. После установки надо будет нажать Ctrl+Shift+P, вбить config, выбрать Configure Display Language, и выбрать ru.

Для запуска всяких команд из Makefile подойдёт Task Explorer.

Кроме того, я рекомендую поставить WaveTrace - проприетарную (это может многих не устроить) и даже платную для версии "без ограничений" просматривалку результатов тестбенчей. В качестве альтернативы выступает уже поставленный вами вместе с Icarus-ом бесплатный GTKWave. Да и по возможностям GTKWave будет более весомым. Но WaveTrace интегрируется прямо в VSCode, меня это подкупает. А вам решать самостоятельно.

Ну и последняя рекомендация - это Verilog Testbench Runner. Удобная штуковина для запуска testbench-ей прямо из под VSCode. Он и запустит симуляцию на Icarus Verilog (я выше говорил, что напрямую из командной строки его дёргать не придётся), и результат симуляции предложит открыть в GTKWave.

Ну и наконец - настройки. А настраивать много не надо - только самое первое установленное расширение и Task Explorer. Для этого жмём Ctrl+, (ага, запятая на английской раскладке). Откроется панель настройки. Там открываем пункт Extensions (или Расширения, если русифицировали), в нём Verilog Configuration.

А нужно заполнить такие настройки:

Verilog: Language Server - svls
Verilog › Linting › Iverilog: Run At File Location - YES
Verilog › Linting: Linter - iverilog
Verilog › Logging: Enabled - YES

Для настройки Task Explorer-а в том-же окне щёлкаем на него, и меняем

Path to make - make

до того там стояло nmake.

Собственно и всё, закрываем, и можем открывать папку с созданным до того на Quartus проектом, видим примерно такое:

-6

Дерево файлов на месте, подсветка синтаксиса тоже присутствует. Сверху справа маячит зелёненькая кнопочка-треугольник play. А снизу красуется свёрнутая панель task explorer. Значит всё замечательно.

А раз всё работает, то надо сделать самое важное. То, что не было сделано в предыдущей статье - проверка написанного кода. И да, конечно - заливка на железо и запуск - это уже проверка. Вот только реальные проекты с первого раза практически наверняка работать не будут! В программировании так-же. Ни один программист не надеется, что написанная им на пару тысяч строк программа вот так возьмёт и заработает с первого запуска. Да кому вообще в голову взбредёт в первый раз запустить программу после написания 1000+ строк. В целом - ошибки неизбежны и их надо ловить. Для FPGA принято использовать тестбенчи. И давайте такой и напишем!

Я предпочитаю начинать с заглушки. Ctrl+N, Ctrl+S, beeper_tb.v, Enter. Файл тестбенча создали, пишем в него:

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

Вторая строка - это обычный include. Мы подхватываем файл с пищалкой, которую будем тут тестировать.

Дальше мы объявляем модуль тестбенча. В Verilog всё должно быть разбито по модулям. Вот и тут он нужен. У тестбенчей нет ни входов, ни выходов, поэтому в скобках - пустота.

Осталось заполнить чем то вменяемым. Вспоминаем, какие входы-выходы есть у проверяемого модуля:

beeper(input clk, input enable, input [WIDTH-1:0] tone, output reg q = 1'b1)

Нам его надо будет подключать, а значит нужно создать необходимые провода и регистры в тестбенче. На каждый входной параметр создаём по регистру. Первый это clk.

Тут мы не только создали регистр clk и записали в него значение 0, мы ещё ему сказали, чтобы каждые 10 (число после решётки) тактов значение clk менялось на противоположное.

Дальше enable и tone: их просто создаём

Тип reg сам содержит значения. Его можно рассматривать как переменную в языках программирования. Именно он нам был нужен для того, чтобы задать входные значения для модуля. Но в случае с q всё иначе, так как он уже и без нас рулит своим значением. Чтобы подключиться к такому, нужен простой провод

Грубо говоря, провода можно рассматривать как ссылки на переменные. Или на некоторые промежуточные значения.

Как только мы объявили все нужные переменные - поставим в testbench наш beeper и подключим его

Так - сначала идёт название модуля. Потом после решётки (это решётка, а не звёздочка) - значения статических параметров модуля (опционально). В случае с beeper - это WIDTH, который мы поставили равным 2, так как нам не зачем для теста большие значения. За первой скобкой идёт название инстанса. Называй хоть горшком. Совпадающее с названием модуля имя тоже годиться. Его и использую за недостатком фантазии. Ну а потом - через запятую все провода в том-же порядке, что они объявлены в модуле beeper.

Тут я хотел-бы сделать уточнение. Никто меня не заставлял в модуле beeper_tb называть clk именно clk, а не какой-нибудь clk1. Вместо tone я бы мог создать регистр cmp или с каким-угодно другим именем. clk модуля beeper_tb связывается с clk модуля beeper исключительно потому, что он первый в скобках.

Так. Это был подготовительный этап, теперь сам тест.

Запускаем при помощи той самой кнопки play над окном редактора справа: снизу появится всплывашка, предлагающая открыть результат в GTKWave. Можете открыть из этой всплывашки, а я открою в WaveTrace

Вот так работает наша пищалка
Вот так работает наша пищалка

Сигналы в область вывода ещё надо добавить из списка.

Теперь давайте разберём, что мы такое написали. Блок initial - это блок, выполняемый в момент загрузки тестбенча. Их может быть много, и все будут выполнены в момент загрузки. Сама попытка определения очерёдности их выполнения будет некорректной. В схемотехнике одновременность - вполне естественное понятие, в то время как последовательность - результат сознательных действий.

Начинаем с $dumpfile и $dumpvars - эти инструкции сообщают icarus-у, что он должен сформировать файл с результатами тестирования и имя этого файла.

Конструкции решётка-число - это задержки. Если dumpfile и dumpvars будут исполнены прямо в момент запуска, то всё что правее и ниже #30, будет выполнено через 30 тактов после загрузки. Страшная конструкция @(posedge clk) обозначает, что 30 тактов нам мало, мы ещё хотим дождаться и переднего фронта сигнала clk. А из-за упомянутого ранее блока always, он обязательно произойдёт. Получается что мы ждём прямой сигнал clk, но не раньше 30-го такта. И как дождались - устанавливаем регистр enable в 1. На приведённой мной диаграмме это видно: сигнал enable устанавливается через 2 такта. 30 шагов - это полтора такта и ещё 10 тактов до переднего фронта.

Потом мы снова ждём (уже от момента установки enable = 1). Сначала 200 тактов, а потом ещё и передний фронт сигнала q чтобы дождаться полного выходного цикла. Потом меняем значение tone и снова ждём один цикл q. Видим, что длительность импульсов и вправду понизилась. При tone = 3, на цикл требовалось 8 тактов. А при tone=2, всего 6. Ну - ровно то что и предсказывала формула, разобранная в прошлой статье.

Можно придраться, что исходя из предложенного тестбенча непонятно, будет ли правильно отрабатываться сброс enable, но это можете уже добавить самостоятельно. Ну а мой файл beeper_tb.v:

Теперь сделаем тестбенч для второго модуля. Им у нас был блок управления. Мы жали на разные кнопки, он должен был определиться - какую ноту надо играть и посчитать для неё подходящий делитель частоты.

В обязанности модуля управления входит отработка в том числе и вариантов, когда зажаты несколько кнопок одновременно: он должен выбирать лишь одну ноту, отдавая приоритет более высоким). А раз кнопки всего 4, то можно перебрать вообще все варианты. Кроме того, у модуля напрочь отсутствует внутреннее состояние, в нём ни одного регистра. И пусть не смутит вас кейворд reg в объявлении

Хотя это и требует некоторого понимания языка Verilog для того, чтобы понять - почему при указании кейворда reg не синтезируется регистр.

Поскольку мы можем перебрать все, то сделаем регистр на 4 бита, и будем каждые 20 тактов прибавлять к нему единичку. через 320 тактов этот регистр переберёт все возможные варианты:

Это наш регистр для кнопочек. Далее нужен вывод из проверяемого модуля. Его объявим сначала как:

Ну и всё, инстанцируем модуль.

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

Как только мы сохраним файл, строка с инстанцированием подкрасится жёлтым:

Port 5 (tone) of selector expects 5 bits, got 32

Мол криво мы передаём tone, он ведь в модуле всего-лишь 5-битный, а мы передаём ему 32-бит. Ну - раз теперь нам подсказали, сколько именно бит потребуется, то меняем определение tone на

Я уже говорил раньше, что никто не заставляет называть сигналы в тестбенче также, как и в модуле. Вот и тут в модуле входы названы:

selector(input a, input g, input f, input c, output reg [WIDTH-1:0] tone);

А в тестбенче в них передаются keys[0], keys[1], keys[2] и keys[3]. В целом - это работает ровно также, как и вызов какой-нибудь вызов функции в python.

Сама последовательность теста будет такой

Привычные dumpfile/dumpvars, и новая конструкция wait. Но мне кажется она интуитивна. Просто повисает до тех пор, пока значение keys не станет равным числу 16 (ну или битам 1111). Дальше ждём ещё 20 шагов и завершаем тестбенч.

А на выходе будет такое:

-8

Минимальное число 10 обязано соответствовать ноте ЛЯ, ведь она делит тактовую частоту на максимальный делитель 440. И именно этой ноте отдаётся максимальный приоритет. И вправду, в каждой чётной ячейке диаграммы записано 10 - всякий раз, когда младший бит keys равен 1. Именно то, что мы ожидали. А нота ДО с минимальным приоритетом и числом 18 встречается только 2 раза: для нажатой одинокой кнопке (1000) и для отпущенных всех кнопках (0000). В целом - тестбенч показывает, что управляющий модуль тоже работает без нареканий. Файл selector_tb.v:

Теперь о заливке проекта на плату. Сделать это можно, открыв проект в Quartus-е. А можно воспользоваться моим Makefile, который я дополнил для кроссплатформенности. Он успешно отрабаывает на macOS с докером постольку, поскольку именно для macOS я его изначально и создавал. Также все его команды отлично отрабатывают на Windows без докера. По идее, он должен успешно работать и с докером под Windows, а также под Linux как с докером так и без, но я не проверял.

Makefile

Запускать задачи из Makefile можно через консоль, но не все её любят - тут и помогает Task Explorer. Как то так:

-9

Снизу Task Explorer. Там отображён make и куча целей для него. К сожалению, сам Task Explorer натаскал из Makefile всё, что было похоже на цель, так что из всего этого нужны лишь следующие цели:

  • waves - пройтись по всем testbench-ам (по всем файлам *_tb.v), выполнить их и сложить результаты в папку output_files. Разом прогнать все тестбенчи проекта может быть весьма удобным решением
  • clean - удаляет папку output_files со всеми результатами тестбенчей и сборки прошивки
  • firmware - запускает Quartus без интерфейса и компилирует на нём всю прошивку
  • flash - записывает прошивку на устройство. Если прошивки ещё нет, то сначала выполняется цель firmware
  • quartus - открывает текущий проект в Quartus. Команда будет висеть до выхода из Quartus-а. Подразумевается, что вы его открываете для каких-то быстрых манипуляций.

В таком режиме можно работать. Quartus теперь нужен только для непосредственного создания проекта и добавления в него сторонних модулей. А разрабатывать схему можно прямо из VSCode.

Так. Осталась одна беда. При открытии top.v все инстанцирования подсвечиваются красным. Всё потому, что icarus-у нужно либо передавать все файлы в командной строке или же расставить include. Командной строкой заведует плагин для VSCode. Остаются только include-ы. Сделаем так. Добавим в верх top.v строчки:

И теперь все подчёркивания исчезают. А вот задача firmware работать перестаёт, Quartus сыплет ошибками. Ему не нравится, что одни и те-же файлы добавлены и в проект, и через include.

Ну да - удалим их из проекта, они и через include сгодятся. Убираем строки:

из top.qsf

И теперь всё работает. И никаких ошибок не возникает.

Как вывод - мы избавились от Quartus в ежедневной работе, зато научились делать testbench-и и заливать проекты на плату. А ведь это и есть 99% работы схемотехника. Quartus нужен лишь для старта проекта и для редких добавлений IP Cores. Так что можно сказать что второй шаг готов.

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

Ещё я мог бы написать руководство по самому Verilog. Если есть желающие такое почитать - пишите в комменты.