一、实验内容及目标
1、实验内容:
(1)自学《SystemVerilog Testbench Lab Guide》pdf文档,理解掌握相关内容;
(2)用SV给待测试模块(DUT)搭建最简单的测试平台(testbench);
(3)用SV写一个任务(Task)来重置(Reset)DUT;
(4)编译(Complie)和仿真(Simulate)这个SV程序。
2、实验目标:
(1)熟练使用Questa Sim软件编写程序、进行验证;
(2)掌握16输入,16输出的路由器的验证。
二、实验过程或步骤
任务一:创建SV接口(interface)文件
1、创建router_io.sv文件,并用编辑器打开它;
2、以下是需要连接的DUT信号;
interface router_io(input bit clock);
logic reset_n;
logic [15:0] din;
logic [15:0] frame_n;
logic [15:0] valid_n;
logic [15:0] dout;
logic [15:0] valido_n;
logic [15:0] busy_n;
logic [15:0] frameo_n;
注意:①在这一部分的所有接口都是异步且没有方向的(但input,output,inout任有方向)。
②接口的方向只能在针对同步信号的时钟模块(Clocking block)或是针对异步信号的(modport)中被说明。
下一步是为测试程序创建一组同步信号,以驱动和采样DUT信号。
3、声明一个由时钟上升沿驱动的时钟模块(Clocking block,);
这个时钟模块将会被测试程序用来实施同步驱动和采样。此时钟块中信号的所有方向必须与DUT中的信号方向一致。
clocking cb @(posedge clock);
output reset_n;
output din;
output frame_n;
output valid_n;
input dout;
input valido_n;
input busy_n;
input frameo_n;
endclocking: cb
所有的数据驱动都是从Clocking走的。
4、如果需要的话,可以对input和output的偏移添加说明;
default input #1ns output #1ns;
从Clocking驱动出来的数据,加了之后就是相对于时钟的上升沿会有1ns的延迟。
5、最后创建一个modport TB(),来连接这个测试程序;
在它的参数列表中,应该引用我们之前创建的时钟模块Clocking block和其它所有可能会用到的异步信号。
modport TB(clocking cb,output reset_n);
endinterface: router_io
告诉program里面所有的模块要做驱动,必须通过modport。
6、保存并关闭router_io.sv。
任务二:创建SV测试程序文件并重置路由器
1、创建测试程序文件test.sv,并用编辑器打开它;
2、在这个文件中,引用接口模块中的modport TB()作为参数,来将interface和test program连接在一起;
`timescale 1ns/100ps
program automatic test(router_io.TB router);
3、在这个程序(program)模块中,定义一个任务(Task):Reset(),实现重置DUT的功能;

(reset_n既可以是同步信号也可是异步信号)
4、在初始化模块中(initial begin),调用reset()任务来重置DUT;
initial begin
$vcdpluson;
reset();
end
task reset();
router.reset_n <= 1'b0;
router.cb.frame_n <= '1;
router.cb.valid_n <= '1;
#2 router.cb.reset_n <= 1'b1;
repeat(15) @(router.cb);
endtask: reset
5、保存和关闭test.sv文件。
任务三:创建SV测试的壳文件(即TOP文件)
1、创建和打开router_test_top.sv文件;
2、 给Top文件中添加接口的实例化(instance);
router_io top_io(SystemClock);
3、实例化这个测试程序;
test t(top_io);
4、将待测试模块和top_io连在一起,实现DUT与Top的连接;
router dut(
.reset_n (top_io.reset_n),
.clock (top_io.clock),
.din (top_io.din),
.frame_n (top_io.frame_n),
.valid_n (top_io.valid_n),
.dout (top_io.dout),
.valido_n (top_io.valido_n),
.busy_n (top_io.busy_n),
.frameo_n(top_io.frameo_n)
);
5、添加`timescale和$timeformat;
`timescale 1ns/100ps
$timeformat(-9,1,"ns",10);
6、保存并关闭文件。
任务四:编译和仿真
此时,一共有四个文件: 1、顶层(TOP)文件:router_test_top.sv
2、接口(interface)文件:router_io.sv
3、待测试(DUT)文件:router.v
4、测试(test.sv)文件:test.sv
接下来就将文件库中的文件进行编译和仿真。具体操作如下:
(1)建立项目:新建项目名称File -> New -> Project,在project name处填入名称;
(2)添加文件和编译:在project窗口单击右键,选择Add to Project -> Existing File,找到四个文件;
(3)编译文件;
(4)仿真与添加波形:在Library -> Work中找到顶层router_test_top,点击右键选择“Simulate without Optimization”,这种仿真模式是为了消除仿真器可能会加入的一些优化处理。选中top_io,在objects一栏中可以看到它的端口和内部信号。接下来在instance ->router_test_top ->top_io上点击右键,选择Add Wave,这是为了在仿真时存储波形。在命令窗口中敲入命令“run 1us”或者在工具栏中运行1us。观察波形。
三、实验结果
1、实验代码
(1)router_io.sv
`timescale 1ns/100ps
interface router_io(input bit clock); //声明由信号时钟摆位驱动的时钟,例化接口
logic reset_n; // 添加将测试程序连接到 DUT 所需的所有信号
logic [15:0] din; // 参数应列出时钟块和所有其他潜在的异步信号
logic [15:0] frame_n;
logic [15:0] valid_n;
logic [15:0] dout;
logic [15:0] valido_n;
logic [15:0] busy_n;
logic [15:0] frameo_n;
clocking cb @(posedge clock); // 声明一个由时钟上升沿驱动的时钟模块
default input #1ns output #1ns; // 相对于时钟的上升沿会有1ns的延迟
output reset_n; // 在时钟块中添加输入和输出
output din;
output frame_n;
output valid_n;
input dout;
input valido_n;
input busy_n;
input frameo_n;
endclocking: cb
modport TB(clocking cb,output reset_n); // 重新生成一个模块端口以连接到测试程序
endinterface: router_io 应列出时钟块和所有其他潜在的异步信号
(2) test.sv
`timescale 1ns/100ps
program automatic test(router_io.TB router); // 声明一个带有要连接的参数的程序块
以修改接口中声明的 TB
initial begin
$vcdpluson;
reset(); //在程序内部定义一个名为 reset() 的任务
end 以根据规范重置 DUT
task reset();
router.reset_n <= 1'b0; //给复位赋值0
router.cb.frame_n <= '1; //给时钟模块中的frame_n赋值1
router.cb.valid_n <= '1; //给时钟模块中的valid_n赋值1,此时不出传输数据
#2 router.cb.reset_n <= 1'b1; //延迟2us,给时钟模块中的复位赋值为1
注意#和##含义不同
repeat(15) @(router.cb); //等待15个有效时钟沿
endtask: reset
endprogram: test
(3) router_test_top.sv
`timescale 1ns/100ps
module router_test_top;
parameter simulation_cycle = 100; // 添加一个接口实例, 实例化测试程序
通过接口进行 I/O 连接
bit SystemClock;
router_io top_io(SystemClock); //调用底层函数
test t(top_io);
router dut( //修改 DUT 连接以通过接口连接
.reset_n (top_io.reset_n),
.clock (top_io.clock),
.din (top_io.din),
.frame_n (top_io.frame_n),
.valid_n (top_io.valid_n),
.dout (top_io.dout),
.valido_n (top_io.valido_n),
.busy_n (top_io.busy_n),
.frameo_n (top_io.frameo_n)
);
initial begin
$timeformat(-9,1,"ns",10); //精确到小数点后一位
SystemClock = 0; 以及显示数值的最小宽度为10,单位为n
forever begin
#(simulation_cycle/2)
SystemClock = ~SystemClock;
end
end
endmodule
2、仿真波形图
