Files
APS/Basic Verilog structures/Testbench.md
Andrei Solodovnikov f4c0960704 Initial commit
2023-09-07 17:06:55 +03:00

91 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Тестовое окружение (Testbench)
Для проверки правильного функционирования цифровых устройств необходимо разработать тестовое окружение. Тестовое окружение (testbench) это блок, который окружает проверяемое устройство, формирует для него тестовые сигналы и автоматически проверяет, что сигналы на выходе проверяемого устройства соответствуют заложенным функциям. Тестовое окружение не является реальным аппаратным блоком (он только симулируется), поэтому в нем возможно использовать традиционные конструкции программирования. Например, то, что описывается в блоках `initial` в тестовом окружении, выполняется как программа в классическом программировании строчка за строчкой.
![../.pic/Basic%20Verilog%20structures/testbench/tb_1.png](../.pic/Basic%20Verilog%20structures/testbench/tb_1.png)
Для того, чтобы создать тестовое окружение (testbench) необходимо создать новый файл симуляции в проекте. Для этого нажмите на `Add source`, после чего нужно выбрать `Add or create simulation sources` (на картинке ниже) → `Create File…` и так далее.
![../.pic/Basic%20Verilog%20structures/testbench/tb_2.png](../.pic/Basic%20Verilog%20structures/testbench/tb_2.png)
Так как для проверки разных модулей придется создавать различные тестовые окружения, то Vivado придется сообщать в явном виде какой из файлов симуляции вы сейчас хотите запустить. Для этого надо щелкнуть на нужном файле правой кнопкой и выбрать пункт `Set as Top` (продемонстрировано на картинке далее).
![../.pic/Basic%20Verilog%20structures/testbench/tb_3.png](../.pic/Basic%20Verilog%20structures/testbench/tb_3.png)
Название файла, для которого будет запускаться симуляция, отображается жирным шрифтом в окне `Sources` в папке `Simulation Sources`. После того, как выбран нужный файл симуляции, его можно запустить через панель `PROJECT MANAGER`, нажав на `Run Simulation`, а затем, в самом простом случае, можно запустить поведенческое моделирование `Run Behavioral Simulation`, оно позволяет увидеть поведение устройства в ответ на воздействия testbenchа. Временные задержки на прохождение сигналов через цифровые блоки при этом не учитываются. Так же можно посмотреть на реакцию устройства после синтеза `Post-Synthesis` или после имплементации `Post-Implementation`. Функциональная симуляция не учитывает временные задержки, временная учитывает. Наиболее приближенная к реальности симуляция `Post-Implementation Timing Simulation`. Она учитывает временные задержки конкретных компонентов, в конкретной ПЛИС, с конкретными задержками распространения сигнала по каждому из проводов.
![../.pic/Basic%20Verilog%20structures/testbench/tb_4.png](../.pic/Basic%20Verilog%20structures/testbench/tb_4.png)
Пример тестового окружение для сумматора
Ниже приводится пример тестового окружения, в котором:
создаются провода и регистры для подключения к тестируемому модулю,
подключается проверяемый модуль,
описывается задача `task`, которую, подобно функции или подпрограмме, можно вызывать с различными параметрами,
в блоке `initial` последовательно два раза вызывается задача `add_op`, после чего симуляция останавливается `$stop`,
пример генерации тактового сигнала для подачи на вход проверяемого устройства.
``` verilog
`timescale 1ns / 1ps // Первое число указывает в каких величинах задержка
// например, использование #10 это 10 наносекунд
// если бы параметр был 10ns, то #10 означало бы 100ns
// Второе число указывает точность симуляции
// тут симуляция происходит для каждой пикосекунды
module my_testbench (); // объявляем модуль тестового окружения
// внешних сигналов нет, поэтому скобки пустые
reg [31:0] A, B; //❶ объявляем регистры для управления входами сумматора
wire [31:0] S; // объявляем провод для подключения к выходу суммы
reg Cin; // объявляем регистр для управления входом Cin
wire Cout; // объявляем провод для подключения к выходу Cout
adder dut ( //❷ подключаем тестируемый модуль
.a(A), // dut (device under test) классическое название тестируемого модуля,
.b(B), // при желании можно использовать любое другое имя
.cin(Cin),
.s(S),
.cout(Cout));
initial begin // блок последовательного исполнения, начинает работу с момента времени 0
add_op(6, 3); //❹ запустить задачу task add_op с параметрами 6 и 3
add_op(2, 7); // когда закончиться предыдущая задача запустить новую
$stop; // остановить симуляцию
end
task add_op; //❸ объявляем задачу add_op
input [31:0] a_op, b_op; // task получает на вход два параметра
begin
A = a_op; // подать на вход A сумматора новое значение a_op
B = b_op; // подать на вход B сумматора новое значение b_op
Cin = 0; // подать на вход Cin ноль
#100; // выждать 100 ns чтобы сигнальчики разбежались и сумматор успел посчитать
if (S == (a_op + b_op)) // если реальность (S) и ожидание (a_op+b_op) совпадают, то
$display("GOOD %d + %d = %d", A, B, S); // вывести в терминал сообщение good
else // в противном случае
$display("BAD %d + %d = %d", A, B, S); // вывести в терминал другое сообщение
end
endtask
endmodule
```
``` verilog
reg clk; //❺ это вообще не относится к описанному выше testbenchу
always #10 clk = ~clk; // каждые 10ns менять clk на противоположное значение
```
Блоков initial в тестовом окружении может быть сколько угодно. Все эти блоки запускаются на исполнение параллельно. Блоков task так же может быть сколько угодно много и каждый из них может выполнять разные проверки. Так же поддерживаются множество стандартных языковых конструкции, например цикл for. Параметры для $display передаются так же, как у printf в языке C (на википедии есть вся информация).
Данный пример проверяет две суммы (6+3) и (2+7). Тест необходимо дополнить большим количеством проверок: несколько с очень большими числами, несколько с отрицательными, несколько с отрицательными и положительными, несколько со входным переносом Cin = 1, несколько операций с числами вызывающим переполнение (чтобы проверить формирование Cout).
По аналогии с этим примером необходимо реализовать модули проверки для:
- АЛУ (по несколько проверок на каждую операцию)
- Регистрового файла (последовательно записать какие-нибудь данные в разные адреса, а потом считать, убедившись, что все правильно, при этом считывание по адресу 0 должно всегда показывать 0)
- Памяти инструкций (считать содержимое из нескольких ячеек, чтобы убедиться, что память проинициализирована)