Введение в EDA Playground
Данная заметка создана в помощь новичкам в изучении сайта https://www.edaplayground.com, с помощью которого можно проводить моделирование модулей, запускать различные симуляторы и многое другое. Удобный инструмент для обмена HDL кодом с сообществом, в котором намного проще разбираться чем с кодом в чате.
Введение
Рабочее пространство разделено на 4 основные зоны:
- Настройки языка, библиотек и симулятора
- Редактор testbench
- Редактор тестируемого модуля
- Панель вывода логов
Пишем
Давайте набросаем простой модуль и тест для него:
// Тестируемый модуль(DUT) module dff( input clk , input reset, input d , output reg q ); always @(posedge clk or posedge reset) begin if (reset) q <= 1'b0; else q <= d; end endmodule
Будем тестировать простой D-триггер, описание банально, надеюсь пояснять ничего не надо
// Тестирующий модуль(testbench) `timescale 1ns/1ns module testbench; reg clk = 0; reg reset = 0; reg d = 0; wire q; initial forever #1 clk = ~clk; default clocking main @(posedge clk); endclocking initial begin ##2 reset = 1; ##3 reset = 0; ##10 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars(1); end always @(posedge clk) d <= $random(); dff dut( .clk (clk ), .reset(reset), .d (d ), .q (q ) ); endmodule
С тестовым модулем немного сложнее, поэтому пройдемся по блокам отдельно
Здесь мы объявляем все используемые сигналы в модуле и назначаем начальные значения
reg clk = 0; reg reset = 0; reg d = 0; wire q;
Реализуем поведение клока: бесконечно, каждый timeunit меняем значение clk на противоположное:
initial forever #1 clk = ~clk;
Описываем дефолтный клок для модуля, просто для удобства написания тестового модуля, можно конечно делать временные задержки, но зачем? можно же делать задержки в тактах.
default clocking main @(posedge clk); endclocking
initial блок выполняется с нулевой отметки времени симуляции и пока не закончится. В данном блоке описываем поведение сигнала сброса: после двух тактов от начала симуляции назначаем сигналу сброса значение 1, потом, спустя 3 такта, назначаем значение 0. Ждем 10 тактов и заканчиваем симуляцию.
Важно: всегда заканчивайте симуляцию, иначе временные диаграммы будут или огромны или вообще вылетит по таймауту, ну или в настройках запуска симулятора(Run time) необходимо выставить время симуляции.
initial begin ##2 reset = 1; ##3 reset = 0; ##10 $finish; end
Очередной initial блок, который запускается параллельно с первым. Здесь мы просто делаем настройки, что бы в дальнейшем посмотреть временные диаграммы.
initial begin $dumpfile("dump.vcd"); $dumpvars(1); end
Здесь мы подаем случайные данные на вход тестируемого модуля.
always @(posedge clk) d <= $random();
Можно, конечно и через initial блок описать, но кому как нравится.
initial forever @(posedge clk) d <= $random();
Ну и в самом конце не забываем инстанцировать тестируемый модуль и подключить порты
dff dut( .clk (clk ), .reset(reset), .d (d ), .q (q ) ); // Ну или так, если у вас совпадают имена // dff dut(.*);
Настраиваем и запускаем
- Проверяем что бы у нас был выбран нужный язык
- Выбираем симулятор(лучше платные, они полноценно поддерживают SystemVerilog)
- Ставим галочку EPWave что бы временные диаграммы открывались после финиша симуляции.
Далее сохраняем и жмем большую кнопку RUN и ждем пока все выполнится и откроется окно с временными диаграммами:
Что мы видим:
- Сигнал сброса на 2 такте устанавливается в 1 и спустя 3 такта сбрасывается в 0
- Сигналу d каждый такт присваивается случайное значение
- Сигнал q:
- в начальный момент времени имеет значение x потому что у нас не назначено начальное значение
- во время сброса сигнал равен 0, как и описано
- в дальнейшем повторяет вход с задержкой в такт, что соответствует поведению D-триггера
Итоги
Надеюсь данная заметка немного поможет освоить данный мощный инструмент и избавит чат от нечитаемого без форматирования кода