§ Установка ПО
Всем привет! Пока еще не запретили Интернет и всякие китайские товары (вещание происходит из чревовещателя за 14 апреля 2022 ужасного года), я бродкастирую (вещаю) из своего уютного бункера. Сегодня я решил начать серию рассказов о том, каким образом можно создать простую схемку, а потом чуть более сложную схему и запустить ее выполняться на верилоге.Первое и самое важное, что я хочу сказать — работать я буду только в linux ubuntu, потому что там есть необходимое мне для работы программное обеспечение. Для винды не знаю, не пробовал, потому без понятия, как там настроить нормально. Например, если еще в винде есть iverilog и прочие ништяки, то verilator что-то на горизонте не наблюдался, либо я искал плохо.
Итак, что требуется для установки (sudo apt install):
-
iverilog gtkwave
— для синтеза -
verilator
— для тестирования -
libsdl2-2.0-0 libsdl2-dev
— для запуска теста
§ Самый простой проект
Пользоваться будем компилятором icarus verilog и потому я создам самый простой проект в истории человечества, а именно hello world. Файл назову tb.v:1`timescale 10ns / 1ns 2module tb; 3 4 reg clock; 5 always #0.5 clock = ~clock; 6 initial begin clock = 0; #2000 $finish; end 7 8endmoduleРазберу построчно.
-
`timescale 10ns / 1ns
— это директива компилятору и симулятору, что минимальной единицей измерения является 1 наносекунда, а единицей отсчета - 10 наносекунд. Это примерно как 1 миллиметр и 10 миллиметров (1 сантиметр). Отсчеты ведутся именно на 10ns. -
module tb; ... endmodule
— начало и завершение модуля (обертка модуля) -
reg clock;
— объявление регистра clock. Регистр - это 1 бит памяти, который может хранить либо 0, либо 1 -
initial begin clock = 0; #2000 $finish; end
— инициализация при старте кода, вначале регистру clock присваивается значение 0, и спустя 2000*10ns (указано в timescale) = 20 000 нс, или 20 микросекунд, симуляция будет завершена -
always #0.5 clock = ~clock;
— эта конструкция означает, что каждые 0.5*10 нс или каждые 5 нс будет перебрасываться из 0 в 1, и из 1 в 0, тем самым симулируется тактовый генератор.
§ Создание makefile
Обычно я люблю создавать makefile для того, чтобы выполнять разные рутинные действия:1all: 2 iverilog -g2005-sv -DICARUS=1 -o tb.qqq tb.v 3 vvp tb.qqq >> /dev/nullКомпиляция
-
iverilog -g2005-sv -DICARUS=1 -o tb.qqq tb.v
— это компилятор, который компилирует файл tb.v в tb.qqq -
-g2005-sv
— опция означает, что используется system verilog 2005 -
-DICARUS=1
— передача параметр ICARUS=1, то есть, делается #define ICARUS 1, параметров может быть много -
-o tb.qqq
— куда выгрузить откомпилированный файл
vvp tb.qqq >> /dev/null
Эта программа приблизительно симулирует поведение верилог-файла, который был передан. Результаты выгружаются отдельно, команда, которая выгружает результаты, задается в tb.v файле:
1module tb; 2... 3initial begin $dumpfile("tb.vcd"); $dumpvars(0, tb); end 4... 5endmodule;Что означает:
-
$dumpfile("tb.vcd");
— команда говорит, что результаты симуляции будут выгружены в tb.vcd файл -
$dumpvars(0, tb);
— то, из какого модуля будут выгружены результаты
§ Отладка
После компиляции появится 2 новых файла:- tb.qqq
- tb.vcd
gtkwave tb.vcd
.Перед тем, как сигнал посмотреть, надо выбрать модуль, потом выбрать один из проводов или регистров и добавить через Append. Обновлять сигналы можно через сочетание клавиш ctrl+shift+r, чтобы не перезагружать снова.
После того, как сигналы были добавлены, можно сохранить их через ctrl+s, выбрав имя, например, tb.gtkw. Теперь же, после каждой загрузки можно не добавлять сигналы снова, а просто вызвать команду
gtkwave tb.gtkw
.§ Verilator
Признаюсь, верилятор я освоил совсем недавно и был крайне удивлен тому, как он работает и вообще, считаю его самым интересным инструментом для работы. К нему я еще могу вернуться в будущем, когда буду делать видеоадаптер. А сейчас пока что расскажу как первично его настроить.Для чего нужен верилятор вообще? Собственно, только для того, чтобы преобразовать модуль из верилога в c++ код, представить его полный эквивалент, чтобы потом использовать в тестировании кода вне ПЛИС-а.
Как обычно, сначала перейдем к рассмотрению makefile
1VINC=/usr/share/verilator/include 2 3all: 4 verilator -Wall -Wno-unused -cc tb.v 5 cd obj_dir && make -f Vtb.mk 6 g++ -o tb -I$(VINC) tb.cc $(VINC)/verilated.cpp obj_dir/Vtb__ALL.aТеперь внимательно разберусь с каждым пунктом:
-
VINC=/usr/share/verilator/include
- это путь к библиотекам верилятора, очень важно -
verilator -Wall -Wno-unused -cc tb.v
- собственно, компиляция tb.v -
cd obj_dir && make -f Vtb.mk
- переход в рабочий каталог и сборка -
g++ -o tb -I$(VINC) tb.cc $(VINC)/verilated.cpp obj_dir/Vtb__ALL.a
- строка компиляции С++ файла
obj_dir/Vtb__ALL.a
- это архивированные коды для дальнейшего их использования в линковщике g++.Итак, теперь рассмотрим то, что находится в tb.cc:
1#include "obj_dir/Vtb.h" 2 3int main(int argc, char **argv) { 4 5 Verilated::commandArgs(argc, argv); 6 Vtb* top = new Vtb; 7 8 top->a = 0; 9 10 // Обработка 10 тактов 11 for (int i = 0; i < 10; i++) { 12 13 top->eval(); 14 printf("i=%d | a=%d, b=%d\n", i, top->a, top->b); 15 top->a = !top->a; 16 17 if (Verilated::gotFinish()) break; 18 } 19 20 delete top; 21 return 0; 22}Этот код можно считать неким таким шаблонов для тестбенчей на вериляторе.В самом начале подключается необходимый h-файл:
#include "obj_dir/Vtb.h"А потом создается объект класса:
Vtb* top = new Vtb;При каждом запуске обязательно надо инициализировать код:
Verilated::commandArgs(argc, argv);Инициализируется
top->a = 0;
стартовое состояние входа a
. Выполняется 10 тактов. На каждом такте происходит исполнение модуля, симуляция:top->eval();И потом значение входа меняется на противоположное:
top->a = !top->a;И так 10 раз.
В результате выполнения программы получается следующее:
i=0 | a=0, b=1 i=1 | a=1, b=0 i=2 | a=0, b=1 i=3 | a=1, b=0 i=4 | a=0, b=1 i=5 | a=1, b=0 i=6 | a=0, b=1 i=7 | a=1, b=0 i=8 | a=0, b=1 i=9 | a=1, b=0Что, собственно, и правильно.
[Оглавление] [Следующая >>]