Появилась простая задача - нужно из скрипта (batch/Windows или bash/Linux) проанализировать результат симуляции Verilog. Причем желательно, чтобы решение работало с разными симуляторами: icarus и modelsim (или другими). Как это сделать?
Первая мысль, которая пришла в голову - найти системную функцию Verilog, которая возвращает результат в операционную систему.
Честно говоря, я почему-то думал, что системная функция $finish([n]) как раз и принимает параметр, значение которого будет возвращено операционной системе. Но при детальном рассмотрении оказалось, что это не так.
Есть две похожих функции $stop([n]) и finish([n]).
Обе функции принимают необязательный параметр n, который определяет, что будет напечатано при остановке:
- 0 - без сообщений;
- 1 - печатается время симуляции и местоположение в коде, то есть номер строки, в которой произошла остановка;
- 2 - время симуляции, местоположение в коде, занимаемая во время симуляции память и процессорное время.
$finish, в отличии от $stop еще и полностью завершает симуляцию, управление возвращается в вызвавшую симулятор программу или скрипт.
Таким образом оказалось, что системная функция $finish() не подходит для моей задачи. Кроме того, нужно заметить, что в симуляторе icarus vvp параметры к функции $finish похоже вообще игнорируются. Обнаружил, что в икарусе при всем при этом, есть специальная системная функция $finish_and_return(n) - вот эта функция делает как раз то, что мне нужно - возвращает код в операционную систему. К сожалению, modelsim не знает этой функции. Так что пришлось искать другие методы.
Я нашел такое решение: я буду результат симуляции (успешно или не успешно) записывать в текстовый файл, а потом в вызывающем скрипте читать текстовый файл и решать что делать в том или ином случае.
Предположим я собираюсь симулировать счетчик с синхронным сбросом:
module counter(
input wire nreset,
input wire clk,
output wire [7:0]out
);
reg [7:0]cnt;
always @( posedge clk )
if( !nreset)
cnt <= 0;
else
cnt <= cnt+8'd1;
assign out = cnt;
endmodule
Тестбенч у меня будет вот такой:
`timescale 1ns / 1ps
module tb;
reg nrst=1'b1;
reg clock = 1'b0;
always #5
clock = ~clock;
wire [7:0]w_out;
counter inst(
.nreset( nrst ),
.clk( clock ),
.out( w_out )
);
integer f;
reg [128*8-1:0]msg;
task do_finish;
input [7:0]code;
begin
f = $fopen("result.txt");
if( code==0 )
$fdisplay(f, "GOOD");
else
$fdisplay(f, "BAD %d",code);
$fclose(f);
$finish;
end
endtask
integer i,j,k;
initial
begin
//$dumpfile("out.vcd");
//$dumpvars(0,tb);
k=0;
nrst = 1'b0;
#100;
nrst =1'b1;
j=220;
//j=260;
$display("lets wait for %d",j*10);
for(i=0; i<j; i=i+1)
#10;
k=1;
//report good result
do_finish(0);
end
always @w_out
begin
//report bad result
if(w_out==255)
do_finish(1);
end
endmodule
Пример правда довольно примитивный и искуственный. Этот тестбенч запускает счетчик и ждет некоторое время (явно задано в переменной j=220). Если за это время счетчик не успеет досчитать до 255, то считается, что тест пройден и вызывается задача do_finish(0), где параметр 0 - признак успеха.
Ниже блок always @w_out постоянно мониторит состояние выхода w_out. Если внезапно он станет 255, то считается, что тест провален и вызывается do_finish(1). Здесь единица в аргументе - признак неуспеха.
Все это довольно притянуто, но моя цель продемонстрировать возвращаемое значение. Сперва хотел сюда прикрутить некоторое случайно генерируемое значение, но это похоже отдельная истария.
Возвращаемое из симуляции значение передается в задачу do_finish( code );
Эта задача открывает текстовый файл result.txt и пишет туда в зависимости от входного параметра строку "GOOD" или "BAD" с кодом ошибки.
Теперь отстается написать BAT файл для запуска симулятора.
Если использовать симулятор icarus verilog, то run_ivl_test.bat файл может выглядеть вот так:
del result.txt
set SimResult=
iverilog -o qqq tb.v counter.v
vvp qqq
set /p SimResult=<result.txt
echo %SimResult%
IF "%SimResult%" == "GOOD" GOTO :GOOD
echo Test Failed
GOTO :END
:GOOD
echo Test Passed
:END
Перед запуском симулятора на всякий случай удаляю файл result.txt и очищяю переменную окружения SimResult. Потом компилирую верилоговские исходники с помощью iverilog и далее запускаю симулятор vvp.
Ну и в завершение скрипта читаю вновь созданный файл result.txt и в зависимости от его содержимого BAT файл исполняет то или иное действие. Здесь он просто выводит текстовое сообщение. А в принципе может например, посылать e-mail об успешном или проваленном тесте.
Для моделсим все примерно так же. Вот бат файл для моделсим:
del result.txt
set SimResult=
vsim -c -do test.do
set /p SimResult=<result.txt
echo %SimResult%
IF "%SimResult%" == "GOOD" GOTO :GOOD
echo Test Failed
GOTO :END
:GOOD
echo Test Passed
:END
Здесь все точно так же, как и в случае с icarus verilog, только моделсим вызывается командой vsim и получает список команд симулятора в отдельном файле test.do. Файл test.do выглядит вот так:
transcript on
vlib my_work
vlog -work my_work counter.v
vlog -work my_work tb.v
vsim my_work.tb
run -all
Команда transcript on - включает режим эхо, вывод исполняемых команд в консоль.
Команда vlib определяет имя библиотеки в которую будут компилироваться мои Verilog файлы из моего проекта.
Команда vlog компилирует Verilog файл в указанную библиотеку. Может компилировать SystemVerilog файлы, если у файлов расширение .sv или если указана опция компилятора -sv.
Команда vsim принимает имя топ модуля из библиотеки, тот модуль который будет симулироваться.
Ну и последняя команда run -all говорит провести симуляцию от начала до конца.
На самом деле у симулятора ModelSim довольно развитая система команд. Полное руководство можно найти например, здесь.
Тут важно, что оба моих бат файла абсолютно одинаково отрабатывают и получают из Verilog симулятора результат успеха или неуспеха.
Вот например, как выглядит вывод при запуске симуляции моделсим:
d:\altera\prb>run_mdls_test.bat
d:\altera\prb>del result.txt
d:\altera\prb>set SimResult=
d:\altera\prb>vsim -c -do test.do
Reading D:/altera/16.1/modelsim_ase/tcl/vsim/pref.tcl
# 10.5b
# do test.do
# vlib my_work
# ** Warning: (vlib-34) Library already exists at "my_work".
# vlog -work my_work counter.v
# Model Technology ModelSim - Intel FPGA Edition vlog 10.5b Compiler 2016.10 Oct 5 2016
# Start time: 20:08:17 on Sep 10,2018
# vlog -work my_work counter.v
# -- Compiling module counter
#
# Top level modules:
# counter
# End time: 20:08:17 on Sep 10,2018, Elapsed time: 0:00:00
# Errors: 0, Warnings: 0
# vlog -work my_work tb.v
# Model Technology ModelSim - Intel FPGA Edition vlog 10.5b Compiler 2016.10 Oct 5 2016
# Start time: 20:08:17 on Sep 10,2018
# vlog -work my_work tb.v
# -- Compiling module tb
#
# Top level modules:
# tb
# End time: 20:08:17 on Sep 10,2018, Elapsed time: 0:00:00
# Errors: 0, Warnings: 0
# vsim my_work.tb
# vsim my_work.tb
# Start time: 20:08:18 on Sep 10,2018
# Loading my_work.tb
# Loading my_work.counter
# run -all
# lets wait for 2200
# ** Note: $finish : tb.v(29)
# Time: 2300 ns Iteration: 0 Instance: /tb
# End time: 20:08:18 on Sep 10,2018, Elapsed time: 0:00:00
# Errors: 0, Warnings: 0
d:\altera\prb>set /p SimResult= 0<result.txt
d:\altera\prb>echo GOOD
GOOD
d:\altera\prb>IF "GOOD" == "GOOD" GOTO :GOOD
d:\altera\prb>echo Test Passed
Test Passed
d:\altera\prb>
А вот здесь вывод при использовании icarus verilog:
d:\altera\prb>run_ivl_test.bat
d:\altera\prb>del result.txt
d:\altera\prb>set SimResult=
d:\altera\prb>iverilog -o qqq tb.v counter.v
d:\altera\prb>vvp qqq
lets wait for 2200
d:\altera\prb>set /p SimResult= 0<result.txt
d:\altera\prb>echo GOOD
GOOD
d:\altera\prb>IF "GOOD" == "GOOD" GOTO :GOOD
d:\altera\prb>echo Test Passed
Test Passed
d:\altera\prb>
Итоговое сообщение Test Passed печатает уже BAT файл.
Для чего это все нужно? Это нужно для автоматизации проектирования. Например, несколько программистов пишут код Verilog и вносят его в репозиторий GIT. Можно сделать так, что после каждого комита автоматически будет вызываться симулятор и будет проверять правильность исполнения тестов. По результатам исполнения тестов программисты могут получать уведомления на почту в случае если тест не прошел.
Подробнее...