SystemVerilog

基于SystemVerilog的测试程序

电路验证是确认所设计的电路功能正确性的过程,而仿真(simulation)是进行电路验证的主要手段,它可以及早发现所存在的设计问题,降低设计风险,节约设计成本。通常,仿真是通过编写测试程序(testbench)完成的。

测试程序也称为测试台,它是用于测试待测模块(Device Under Test, DUT)功能是否正确的一段SystemVerilog HDL代码,是不可综合的,由激励信号、DUT和输出响应三部分组成。

待测模块DUT:

1
2
3
4
module sillyfunction(input logic a, b, c,
output logic y);
assign y= ~b & ~c | a & ~b;
endmodule

测试程序示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
`timescale 1ns/1ns          //预编译指令,定义时间单位和时间精度
module sillyfunction_tb(); //测试程序没有输入/输出端口
logic a, b, c, y;
sillyfunction dut(.a(a), .b(b), .c(c), .y(y)); //实例化待测模块
initial begin //给出激励信号
a = 0; b = 0; c = 0; #10; //000
c = 1; #10; //001
b = 1; c = 0; #10; //010
c = 1; #10; //011
a = 1; b = 0; c = 0; #10; //100
c = 1; #10; //101
b = 1; c = 0; #10; //110
c = 1; #50; //111
$finish
end
initial begin //输出结果,否则只产生波形
$monitor($time, "a = %b, b = %b, c = %b, y = %b", a, b, c, y);
end
endmodule

10 表示延迟10个时间单位,$monitor, $time表示系统任务,

测试程序的模板如下:

1
2
3
4
5
6
module testbench_name();  //testbench为顶层模块,不会被其他模块实例化,因此不需要任何端口
//信号定义
//模块实例化
//添加激励信号
//显示输出结果(可以不添加任何显示打印语句,只生成波形图即可)
endmodule

在SystemVerilog中,施加激励就是向DUT添加输入信号(即测试向量),主要有三种方法:

  1. 通过initial过程块施加(线性)激励;
  2. 通过always过程块施加(循环)激励,主要用于产生时钟信号;
  3. 通过文件施加激励。

通过initial过程块施加激励

在initial块中施加激励,每个仿真时刻只用列出值需要改变的信号。initial块只执行一次。

在一个测试程序中可以包含多个initial块,并且它们都是同时并行执行,因此需要特别注意,不要在多个initial块中,在同一个仿真时刻对同一个信号赋值,否则将产生冲突。

1
2
3
4
5
6
7
8
9
initial begin
//时刻0发生赋值
data_bus = 8'h00; addr = 8'h3f;
#10 data_bus = 8'h45; //时刻10发生赋值
end

initial begin
#15 data_bus = 8'hff; //时刻15发生赋值
end
1
2
3
4
5
6
7
8
9
initial begin
//时刻0发生赋值
data_bus = 8'h00; addr = 8'h3f;
#10 data_bus = 8'h45; //时刻10发生赋值
end

initial begin
#10 data_bus = 8'hff; //错误,发送冲突
end

通过文件施加激励

将激励(测试向量)存放在一个文本文件中,测试程序从文件中读取激励,对DUT进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module sillyfunction_tb();
logic a,b,c,y;
logic [2:0] stim [7:0]; //声明一个logic类型的数组stim
int i;
sillyfunction dut(.a(a),.b(b),.c(c),.y(y));
initial begin
$readmemb("testvector.txt",stim); //将所有激励读入数组
for(i=0;i<8;i=i+1) begin
{a,b,c}=stim[i]; #10; //依次测试各个激励
end
end
initial begin
$monitor($time, "a = %b, b = %b, c = %b, y = %b", a, b, c, y);
end
endmodule
1
2
3
4
5
6
7
8
9
testvector.txt
000
001
010
011
100
101
110
111

输出响应

在SystemVerilog中,输出响应是指在向DUT的输入端施加激励后,通过观察DUT输出的结果,并与预期结果进行比较,以验证电路功能是否正确。这一过程可通过观测波形图或借助SystemVerilog HDL提供的一系列系统任务显示输出结果来实现。

SystemVerilog HDL中常用的系统任务包括:

获取仿真时间:$time

显示信号值:$display, $monitor

结束/中断仿真:$finish, $stop

文件输入:$readmemb, $readmmemh

文件输出:$fopen, $fclose, $fdisplay, $fmonitor

获取仿真时间

获取仿真时间的系统任务的返回值使用由`timescale定义的时间单位。如:

`timescale 1ns/1ps

`timescale 1ns/1ns

$time返回一个64位的整数时间值

$stime返回一个32位的整数时间值

$realtime返回一个实数时间值

如:$monitor($time,"a=%b b=%b c=%b y=%b",a,b,c,y)

显示信号值

显示信号值的系统任务包括:$display和$monitor

$display($time,"a=%b",a);

$monitor($time,"a=%b",a);

$display和$monitor的区别在于前者只有执行到该语句时才会进行显示操作,而后者是一个监视器,只要输出变量列表中的某个变量发生变化,就执行一次显示操作。后者使用更方便。

%h %o %d %b %c %s %t %m
hexadecimal octonary decimal binary ASCII 字符串 时间 模块名

结束/中断仿真

结束/中断仿真的系统任务包括:$finish和$stop,用于对仿真过程进行控制。

$finish;

$finish(n);

$stop;

$stop(n);

参数n可以取0, 1等值,”0”表示不输出任何信息,”1”表示给出仿真时间。

文件输入

在SystemVerilog HDL中文件输入不需要打开文件操作,直接读取文件即可,相关的系统任务包括:$readmemb和$readmemh,前者读取2进制数据,后者读取16进制数据。

$readmemb(“数据文件名”, 数组(存储器)名, <起始地址>, <结束地址>);
$readmemh(“数据文件名”, 数组(存储器)名, <起始地址>, <结束地址>);

起始地址和结束地址均可缺省。

文件格式:

  • 可用”_”提高数据可读性

  • 可包含单行或多行注释

  • 可用空格换行来区分单个数据

  • 可以设定一个特定地址,规定其后的数据从该地址开始存储,格式如下:

    @hex_addr

    地址必须是16进制,且大小写不敏感,并且@和地址之间不允许有空格。

例如:

testmem.txt:

0000_0000
0110_0001 0011_0010
//地址3-255没有定义
@100 //hex, 256
1111_1100
//地址257-1022没有定义
@3FF
1100_0010

数组stim:
0 00000000
1 01100001
2 00110010
3
4

255
256 11111100
257

1022
1023 11000010

code:

1
2
3
4
5
logic [7:0] stim [1023:0];
initial begin
//$readmemb("testmem.txt",stim);
$readmemb("testmem.txt",stim,0,1023);
end

文件输出

在SystemVerilog HDL中文件输出需要先打开文件,相应的系统任务为$fopen,然后可以通过系统任务$fdisplay或$fmonitor将需要保存的信息输入到指定文件中。

1
2
3
4
5
int MCD;
MCD = $fopen("文件名", "操作模式");
$fdisplay(MCD, "显示格式控制符", <输出变量(信号)列表>);
$fmonitor(MCD, "显示格式控制符", <输出变量(信号)列表>);
$fclose(MCD);

$fopen打开指定文件并返回一个32位整数,若打开失败,则返回0。操作模式为w, w+, a, a+.

$fclose关闭打开的文件。

$fdisplay和$fmonitor的用法与$display和$monitor的用法一致。

自动化测试

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module sillyfunction_tb();
logic a,b,c,y,yexpected;
logic [3:0] stim [7:0];
int i;
sillyfunction dut(.a(a),.b(b),.c(c),.y(y));

initial begin
$readmemb("at_vec.txt",stim);
for(i=0; i<8; i=i+1) begin
{a,b,c,yexpected}=stim[i]; #10;
if(y==yexpected)
$display($time,"test pass!");
else
$display($time,"Error: inputs=%b,%b,%b",{a,b,c});
end
end
endmodule
1
2
3
4
5
6
7
at_vec.txt

000_1
001_0
010_0
...
111_0