【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
我们都知道,做fpga开发,可以用verilog,也可以用vhdl。开发完之后,如果需要实际验证,那么就可以综合一下,下载到fpga,看下效果。但是如果开发的内容比较多,或者开发的内容,是由很多人一起完成的,并且fpga验证平台也不多,这个时候应该怎么做呢?通常我们都是借助于verilog仿真来实现功能验证的。
1、verilog仿真工具
提起verilog仿真,大家都会想起modelsim。但是有一些开源工具也是可以完成verilog仿真的,这里面就有iverilog和verilator。其中verilator比较特殊,它是把verilog翻译成c语言来执行。
2、iverilog仿真
和verilator不同,iverilog工具本身是编译成一个中间语言,然后通过vvp执行中间语言,最后通过gtkwave生成波形。所以整个过程当中有三个工具,一个是iverilog,一个是vvp,一个是gtkwave。
3、功能代码
为了说明iverilog如何使用,我们先写一个简单的功能代码,即cnt.v,
module count(rst, clk, out);
// input & output
input rst;
input clk;
output out;
// wire & reg
wire rst;
wire clk;
reg ce;
reg out;
// inner wire & reg
reg[5:0] cnt;
// clause defined
always @(posedge clk or posedge rst)
if(rst)
ce <= 1'b0;
else
ce <= 1'b1;
always @(posedge clk or posedge rst)
if (rst) begin
cnt <= 6'h00;
end else if (ce == 1'b1)begin
if (cnt == 6'd59)
cnt <= 6'h00;
else
cnt <= cnt + 1;
end
always @(posedge clk or posedge rst)
if(rst)
out <= 1'b0;
else if(out == 1'b1)
out <= 1'b0;
else if(cnt == 6'd59)
out <= 1'b1;
endmodule
4、测试代码
功能代码一般需要外部的激励才能完成测试,所以我们还需要写一个激励文件,这就是testbench。可以把这个激励文件命名为tb.v,
`timescale 1ns/1ps
module test();
reg rst;
reg clk;
wire data;
count tt(.rst(rst),
.clk(clk),
.out(data));
initial
begin
rst = 0;
clk = 0;
#12 rst = 1;
#21 rst = 0;
#1000 $finish;
end
initial
begin
while(1)
clk = #5 !clk;
end
initial
begin
$dumpfile("hello.vcd");
$dumpvars(0, test);
end
endmodule
5、测试命令
实际的测试命令是iverilog、vvp、gtkwave依次执行。不过我们可以拼在一起,这样使用就非常方便,也就是把命令用&&连接在一起,
C:\iverilog\bin\iverilog.exe -o tb tb.v cnt.v && C:\iverilog\bin\vvp.exe -n tb -lxt2 && C:\iverilog\gtkwave\bin\gtkwave.exe hello.vcd
6、查看波形
这里生成的波形文件就是hello.vcd,运行完命令之后,拖入信号,就可以看到波形了。并且后续开发的时候,也就只需要保留这个框架,修改cnt.v就好了。

7、中间文件tb
整个仿真过程还是比较简单,其中比较有意义的,就是生成的中间文件tb,
#! /c/Source/iverilog-install/bin/vvp
:ivl_version "12.0 (devel)" "(s20150603-1539-g2693dd32b)";
:ivl_delay_selection "TYPICAL";
:vpi_time_precision - 12;
:vpi_module "C:\iverilog\lib\ivl\system.vpi";
:vpi_module "C:\iverilog\lib\ivl\vhdl_sys.vpi";
:vpi_module "C:\iverilog\lib\ivl\vhdl_textio.vpi";
:vpi_module "C:\iverilog\lib\ivl\v2005_math.vpi";
:vpi_module "C:\iverilog\lib\ivl\va_math.vpi";
S_000001792f479e10 .scope module, "test" "test" 2 2;
.timescale -9 -12;
v000001792f476980_0 .var *"_ivl_0", 0 0; Local signal
v000001792f30d840_0 .var "clk", 0 0;
v000001792f30d8e0_0 .net "data", 0 0, v000001792f476840_0; 1 drivers
v000001792f30d980_0 .var "rst", 0 0;
S_000001792f476610 .scope module, "tt" "count" 2 8, 3 1 0, S_000001792f479e10;
.timescale -9 -12;
.port_info 0 /INPUT 1 "rst";
.port_info 1 /INPUT 1 "clk";
.port_info 2 /OUTPUT 1 "out";
v000001792f479fa0_0 .var "ce", 0 0;
v000001792f47b920_0 .net "clk", 0 0, v000001792f30d840_0; 1 drivers
v000001792f4767a0_0 .var "cnt", 5 0;
v000001792f476840_0 .var "out", 0 0;
v000001792f4768e0_0 .net "rst", 0 0, v000001792f30d980_0; 1 drivers
E_000001792f4792a0 .event posedge, v000001792f4768e0_0, v000001792f47b920_0;
.scope S_000001792f476610;
T_0 ;
%wait E_000001792f4792a0;
%load/vec4 v000001792f4768e0_0;
%flag_set/vec4 8;
%jmp/0xz T_0.0, 8;
%pushi/vec4 0, 0, 1;
%assign/vec4 v000001792f479fa0_0, 0;
%jmp T_0.1;
T_0.0 ;
%pushi/vec4 1, 0, 1;
%assign/vec4 v000001792f479fa0_0, 0;
T_0.1 ;
%jmp T_0;
.thread T_0;
.scope S_000001792f476610;
T_1 ;
%wait E_000001792f4792a0;
%load/vec4 v000001792f4768e0_0;
%flag_set/vec4 8;
%jmp/0xz T_1.0, 8;
%pushi/vec4 0, 0, 6;
%assign/vec4 v000001792f4767a0_0, 0;
%jmp T_1.1;
T_1.0 ;
%load/vec4 v000001792f479fa0_0;
%cmpi/e 1, 0, 1;
%jmp/0xz T_1.2, 4;
%load/vec4 v000001792f4767a0_0;
%cmpi/e 59, 0, 6;
%jmp/0xz T_1.4, 4;
%pushi/vec4 0, 0, 6;
%assign/vec4 v000001792f4767a0_0, 0;
%jmp T_1.5;
T_1.4 ;
%load/vec4 v000001792f4767a0_0;
%addi 1, 0, 6;
%assign/vec4 v000001792f4767a0_0, 0;
T_1.5 ;
T_1.2 ;
T_1.1 ;
%jmp T_1;
.thread T_1;
.scope S_000001792f476610;
T_2 ;
%wait E_000001792f4792a0;
%load/vec4 v000001792f4768e0_0;
%flag_set/vec4 8;
%jmp/0xz T_2.0, 8;
%pushi/vec4 0, 0, 1;
%assign/vec4 v000001792f476840_0, 0;
%jmp T_2.1;
T_2.0 ;
%load/vec4 v000001792f476840_0;
%cmpi/e 1, 0, 1;
%jmp/0xz T_2.2, 4;
%pushi/vec4 0, 0, 1;
%assign/vec4 v000001792f476840_0, 0;
%jmp T_2.3;
T_2.2 ;
%load/vec4 v000001792f4767a0_0;
%cmpi/e 59, 0, 6;
%jmp/0xz T_2.4, 4;
%pushi/vec4 1, 0, 1;
%assign/vec4 v000001792f476840_0, 0;
T_2.4 ;
T_2.3 ;
T_2.1 ;
%jmp T_2;
.thread T_2;
.scope S_000001792f479e10;
T_3 ;
%pushi/vec4 0, 0, 1;
%store/vec4 v000001792f30d980_0, 0, 1;
%pushi/vec4 0, 0, 1;
%store/vec4 v000001792f30d840_0, 0, 1;
%delay 12000, 0;
%pushi/vec4 1, 0, 1;
%store/vec4 v000001792f30d980_0, 0, 1;
%delay 21000, 0;
%pushi/vec4 0, 0, 1;
%store/vec4 v000001792f30d980_0, 0, 1;
%delay 1000000, 0;
%vpi_call 2 18 "$finish" {0 0 0};
%end;
.thread T_3;
.scope S_000001792f479e10;
T_4 ;
T_4.0 ;
%pushi/vec4 1, 0, 32;
%or/r;
%flag_set/vec4 8;
%jmp/0xz T_4.1, 8;
%load/vec4 v000001792f30d840_0;
%nor/r;
%store/vec4 v000001792f476980_0, 0, 1;
%pushi/vec4 5000, 0, 64;
%ix/vec4 4;
%delayx 4;
%load/vec4 v000001792f476980_0;
%store/vec4 v000001792f30d840_0, 0, 1;
%jmp T_4.0;
T_4.1 ;
%end;
.thread T_4;
.scope S_000001792f479e10;
T_5 ;
%vpi_call 2 30 "$dumpfile", "hello.vcd" {0 0 0};
%vpi_call 2 31 "$dumpvars", 32'sb00000000000000000000000000000000, S_000001792f479e10 {0 0 0};
%end;
.thread T_5;
# The file index is used to find the file name in the following table.
:file_names 4;
"N/A";
"<interactive>";
"tb.v";
"cnt.v";
这个文件其实是可以读懂的,比如每一部分的内容都是一个thread。
.scope S_000001792f479e10;
T_4 ;
T_4.0 ;
%pushi/vec4 1, 0, 32;
%or/r;
%flag_set/vec4 8;
%jmp/0xz T_4.1, 8;
%load/vec4 v000001792f30d840_0;
%nor/r;
%store/vec4 v000001792f476980_0, 0, 1;
%pushi/vec4 5000, 0, 64;
%ix/vec4 4;
%delayx 4;
%load/vec4 v000001792f476980_0;
%store/vec4 v000001792f30d840_0, 0, 1;
%jmp T_4.0;
T_4.1 ;
%end;
.thread T_4;
.scope S_000001792f479e10;