Тестбенчи и симуляция: проверка до железа
Осваиваем тестбенч — отдельный модуль, который «дёргает» вашу схему и проверяет её до прошивки в железо.
Тестбенч (testbench) — специальный Verilog-модуль без портов, который создаёт экземпляр проверяемой схемы, подаёт на неё входные сигналы (стимулы) и наблюдает за выходами в симуляторе.
Найти ошибку в железе — это часы с осциллографом и перепрошивками. Найти ту же ошибку в симуляции — секунды. Поэтому золотое правило FPGA-разработки: «сначала симулируй, потом прошивай». Инструмент проверки — тестбенч: модуль, который играет роль «испытательного стенда» для вашей схемы. Важно: тестбенч не синтезируется в железо — он существует только для симуляции, поэтому в нём разрешены конструкции, которых нет в синтезируемом коде (задержки, печать).
Анатомия тестбенча
Тестбенч устроен иначе, чем обычный модуль: у него нет портов (он самодостаточен), он сам создаёт такт и стимулы. Вот тестбенч для счётчика:
module counter_tb;
reg clk = 0;
reg rst = 1;
wire [3:0] count;
// создаём экземпляр проверяемой схемы (DUT — Device Under Test)
counter4 dut(.clk(clk), .rst(rst), .count(count));
// генерация такта: каждые 5 единиц времени инвертируем clk -> период 10
always #5 clk = ~clk;
// сценарий проверки
initial begin
#12 rst = 0; // снимаем сброс через 12 ед. времени
#100 $finish; // через 100 ед. завершаем симуляцию
end
endmoduleРазберём по частям: clk и rst объявлены reg, потому что тестбенч ими управляет. Конструкция always #5 clk = ~clk; формирует тактовый сигнал. Блок initial выполняется один раз и задаёт сценарий: символ # — это задержка в единицах модельного времени. $finish — системная задача, останавливающая симуляцию.
$display и $monitor: наблюдаем за схемой
Чтобы видеть, что происходит, используют системные задачи (начинаются с $). Они работают только в симуляции:
$display(...)— печатает строку один раз (какprint), с форматами%b(двоичное),%d(десятичное),%h(шестнадцатеричное).$monitor(...)— печатает автоматически каждый раз, когда меняется любой из перечисленных сигналов; задаётся один раз.$time— текущее модельное время.
initial begin
$monitor("t=%0d rst=%b count=%d", $time, rst, count);
endЭто выведет строку при каждом изменении count — удобно следить за поведением счётчика во времени.
Как работает под капотом
Симулятор моделирует ход времени и срабатывание триггеров. Промоделируем, что напечатал бы $monitor для нашего счётчика после снятия сброса, — на Python:
# Имитация вывода $monitor для 4-битного счётчика
count = 0
rst = 1
time = 0
print("имитация вывода $monitor:")
for step in range(8):
# на времени 12 снимаем сброс (как #12 rst=0 в тестбенче)
if time >= 12:
rst = 0
print(f"t={time:3d} rst={rst} count={count:2d}")
# фронт такта каждые 10 ед. времени
if rst:
count = 0
else:
count = (count + 1) % 16
time += 10Вывод:
имитация вывода $monitor: t= 0 rst=1 count= 0 t= 10 rst=1 count= 0 t= 20 rst=0 count= 0 t= 30 rst=0 count= 1 t= 40 rst=0 count= 2 t= 50 rst=0 count= 3 t= 60 rst=0 count= 4 t= 70 rst=0 count= 5
Пока rst=1, счётчик держит 0; после снятия сброса начинает считать 1,2,3... Ровно это и показал бы реальный симулятор — и так, не трогая железо, вы убеждаетесь, что счётчик работает.
Самопроверяющиеся тестбенчи
Зрелый тестбенч не просто печатает значения, а сам проверяет их через if и $display с сообщением об ошибке (или системную задачу $error). Тогда регрессию можно гонять автоматически: «прошёл/не прошёл» без чтения логов глазами. Это основа верификации — отдельной инженерной дисциплины.
Частые ошибки
- Писать в тестбенче синтезируемый стиль. Тестбенч — не железо; задержки
#,initial,$displayв синтезируемом коде недопустимы, но в тестбенче — норма. - Забыть сгенерировать такт. Без
always #5 clk = ~clk;тактируемая схема «замрёт» — ничего не произойдёт. - Прошивать без симуляции. Пропуск симуляции превращает отладку в многочасовое мучение с железом.
Итог
- Тестбенч — несинтезируемый модуль, подающий стимулы на проверяемую схему (DUT).
- Такт формируют
always #5 clk = ~clk;, сценарий — вinitialс задержками#. $displayпечатает раз,$monitor— при каждом изменении сигналов.- Самопроверяющиеся тестбенчи позволяют гонять регрессию автоматически.