前言:
这是路科的V0课程实验,就是路桑所说的“把大象装进冰箱里”,其实就是Synopsys公司的SV testbench lab(有英文的pdf,要是有这样的指导,就可以对sv验证有个清晰的感知了),如果需要pdf文档,去eetop下载,或者进企鹅群:11427一七989下载。
设计模块介绍
实验中的待测试模块(DUT)是一个16输入,16输出的路由器,这个路由器的功能是把数据通过各个输入端(Input)发送到任意输出端(Output)。下面是最终完成的整个验证平台示意图。
打开实验文件,你将会看到如下实验代码的结构,其中rtl中为设计代码,labs中为原始代码,solutions中为参考答案。
实验一
在Lab1中我们应该掌握以下内容:
- 用SV给待测试模块(DUT)搭建最简单的测试平台(Testbench)。
- 用SV写一个任务(Task)来重置(Reset)DUT。
- 编译(Compile)和仿真(Simulate)这个SV程序。
我们需要建立的几个文件:顶层(Top)文件:router_test_top.sv ,接口(Interface)文件:router_io.sv ,待测试(DUT)文件:router.v , 测试(Test)文件:test.sv。在该实验完成时,你将得到如下验证结构。
lab overview
constructing systemverilog testbench
使用VCS创建这个SystemVerilog测试平台的过程如下:
- 创建一个接口来连接测试程序和DUT。
- 编写测试程序。
- 使用包含接口的装具文件连接测试和DUT。
任务一:进入lab1文件夹
任务二:创建SV接口(interface)文件
1.创建router_io.sv文件,并用编辑器打开它。
//router_io.sv
//文件开始的样子,可以直接拷贝,进行实验
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;
//Lab 1 - Task 2, Step 3
//
//Declare a clocking block driven by posedge of signal clock
//Add all signals required to connect test program to the DUT
//All directions must be with respect to test program
//ToDo
//Lab 1 - Task 2, Step 4
//
//Add input and output skew in clocking block(optional)
//ToDo
//Lab 1 - Task 2, Step 5
//
//Create a modport to connect to test program
//Arguments should list clocking block and all other potential asynch signals
//ToDo
endinterface: router
2.以下是需要连接到DUT的信号。
interface router_io(input bit clock);
logic reset_n ;
logic [1 5 :0] din ;
...
logic [1 5 :0 ] frameo_n ;
endinterface: router_io
注意:在这一部分的所有接口都是异步且没有方向的(例如input,output,inout就是有方向)。接口的方向只能在针对同步信号的时钟模块(Clocking block)或是针对异步信号的(modport)中被说明。
在下一步中,我们会去创建同步(synchronous)信号给测试程序(test program)从而可以驱动(drive)和采样(sample)DUT中的信号。
3.声明一个由时钟上升沿所驱动的时钟模块(Clocking block,以后简称CB)。这个时钟模块将会被测试程序用来实施同步驱动(drive)和采样(sample)。这个CB中的所有信号的方向(direction)必须和DUT中的信号方向一致。
这是dut中的接口,我们需要根据这个来完成端口的声明
input reset_n, clock;
input [15:0] din, frame_n, valid_n;
output [15:0] dout, valido_n, busy_n, frameo_n;
4.如果需要的话,可以对input和output的(skew)添加说明,就是确保有建立时间和保持时间以避免竞争;
5.最后创建一个modport TB(),来连接这个测试程序。在它的参数(argument)列表中,应该引用我们之前创建的时钟模块CB和其它所有可能会用到的异步(asynchronous)信号。
6.保存并关闭router_io.sv文件。
//router_io.sv
//任务2结束的样子,可以直接拷贝,进行实验
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;
//Lab 1 - Task 2, Step 3
//
//Declare a clocking block driven by posedge of signal clock
//Add all signals required to connect test program to the DUT
//All directions must be with respect to test program
//ToDo
clocking cb @(posedge clock);
default input #1ns output #1ns;
output reset_n;
output din;
output frame_n;
output valid_n;
input dout;
input valido_n;
input frameo_n;
input busy_n;
endclocking: cb
//Lab 1 - Task 2, Step 4
//
//Add input and output skew in clocking block(optional)
//ToDo
//Lab 1 - Task 2, Step 5
//
//Create a modport to connect to test program
//Arguments should list clocking block and all other potential asynch signals
//ToDo
modport TB(clocking cb, output reset_n);
endinterface: router_io
任务三:创建SV测试程序文件。
1.创建测试程序文件test.sv,并用编辑器打开它。
//test.sv
//最开始的样子,可以直接复制粘贴进行实验
//Lab 1 - Task 3, Step 2
//
//Declare a program block with arguments to connect
//to modport TB declared in interface
//ToDo
//Lab 1 - Task 3, Step 3
//
//Declare an initial block
//In the initial block print a simple message to the screen
//ToDo
//Lab 1 - Task 6, Steps 3 and 4 -
//
//Replace $display() in initial block with $vcdpluson
//Call reset() task
//ToDo - Caution!! Do only in Task 6
//Lab 1 - Task 6, Step 2
//
//Define a task called reset() inside the program to reset DUT per spec.
//ToDo - Caution!! Do only in Task6
2.在这个文件中,引用接口模块中的modport TB作为参数(argument),来将interface和test program连接在一起。
program automatic test (router_io . TB rtr_io) ;
endprogram: test
3.在这个程序(program)模块中,在屏幕上输出一个简单的信息;
program automatic test (router_io . TB rtr_io) ;
initial begin
$display("Hello World!");
end
endprogram: test
4.保存和关闭test.sv文件。
//test.sv
//任务二结束的样子
//Lab 1 - Task 3, Step 2
//
//Declare a program block with arguments to connect
//to modport TB declared in interface
//ToDo
program automatic test(router_io.TB rtr_io);
//Lab 1 - Task 3, Step 3
//
//Declare an initial block
//In the initial block print a simple message to the screen
//ToDo
initial begin
$display("Hello World!");
end
//Lab 1 - Task 6, Steps 3 and 4 -
//
//Replace $display() in initial block with $vcdpluson
//Call reset() task
//ToDo - Caution!! Do only in Task 6
//Lab 1 - Task 6, Step 2
//
//Define a task called reset() inside the program to reset DUT per spec.
//ToDo - Caution!! Do only in Task 6
endprogram:test
任务四:创建SV测试的壳文件(即TOP文件)
1.创建和打开router_test_top.sv文件。
//router_test_top.sv
//最开始的样子
//Lab 1 - Task 4, Step 6a
//
//Add `timescale
//ToDo
module router_test_top;
parameter simulation_cycle = 100;
bit SystemClock;
//Lab 1 - Task 4, Step 3
//
//Add an interface instance
//ToDo
//Lab 1 - Task 4, Step 4
//
//Instantiate the test program
//Make I/O connection via interface
//ToDo
router dut(
//Lab 1 - Task 4, Step 5
//
//Modify DUT connection to connect via interface
//ToDo
.reset_n (reset_n),
.clock (clock),
.din (din),
.frame_n (frame_n),
.valid_n (valid_n),
.dout (dout),
.valido_n (valido_n),
.busy_n (busy_n),
.frameo_n (frameo_n)
);
initial begin
//Lab 1 - Task 4, Step 6b
//
//Add $timeformat
//ToDo
SystemClock = 0;
forever begin
#(simulation_cycle/2)
SystemClock = ~SystemClock;
end
end
endmodle
2.上面展示了基本结构。
3.给Top文件中添加接口的实例化(instance)
4.实例化这个测试程序。(通过将测试程序在top中的例化t 和接口在top中的例化top_io相联系,将test程序和Top连接在一起)
5.将待测试模块和top_io连在一起,实现DUT与Top的连接。
此时你有没有发现呢?Top文件中经过三次例化成功将DUT文件,test program文件,interface文件包含了起来。
6.添加`timescale和$timefornat到Top中。
7.保存并关闭router_test_top.sv文件。
//router_test_top.sv
//此处任务结束时的代码效果
//Lab 1 - Task 4, Step 6a
//
//Add `timescale
//ToDo
`timescale 1ns/100ps
module router_test_top;
parameter simulation_cycle = 100;
bit SystemClock;
//Lab 1 - Task 4, Step 3
//
//Add an interface instance
//ToDo
router_io top_io(SystemClock);
//Lab 1 - Task 4, Step 4
//
//Instantiate the test program
//Make I/O connection via interface
//ToDo
test t(top_io);
router dut(
//Lab 1 - Task 4, Step 5
//
//Modify DUT connection to connect via interface
//ToDo
.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
//Lab 1 - Task 4, Step 6b
//
//Add $timeformat
//ToDo
$timeformat(-9,1,"ns",10);
SystemClock = 0;
forever begin
#(simulation_cycle/2)
SystemClock = ~SystemClock;
end
end
endmodule
任务五:编译(compile)和仿真(simulate)
此时,我们一共拥有四个文件:待测试文件:router.v,接口文件:router_io.sv ,测试文件:test.sv , 顶层文件:router_test_top.sv。
1.用VCS编译和仿真
直接使用命令
vcs -sverilog router_test_top.sv test.sv router_io.sv ../../rtl/router.v
会生成simv文件,执行./simv仿真
2.用Questasim进行编译和仿真
-
建立项目
- 新建项目名称File -> New -> Project,在project name处填入名称例如lab1
-
添加文件和编译
- 在project窗口单击右键,选择Add to Project -> Existing File,找到下载的lab1实验文件。
- 编译文件。
-
仿真与添加波形
- 在Library -> Work中找到顶层router_test_top,点击右键选择“Simulate without Optimization”,这种仿真模式是为了消除仿真器可能会加入的一些优化处理。
- 选中top_io,在objects一栏中可以看到它的端口和内部信号。
- 接下来在instance ->router_test_top ->top_io上点击右键,选择Add Wave,这是为了在仿真时存储波形。
- 在命令窗口中敲入命令“run 1us”或者在工具栏中运行1us。
- 观察波形
到这里我们整个验证的最基本框架就建立起来,以后我们会不断向里面填充东西,并将它们标准化,在整个过程结束后我们可以建立起一个完整的验证平台。
任务六 重置路由器(Reset the Router)
1.打开test.sv文件,定义一个任务(Task):Reset(),实现重置DUT的功能。
2.在初始化模块中(initial begin),调用reset()任务来重置DUT。(reset_n既可以是同步信号也可以是异步信号)
3.在initial block中,用$vcdpluson替换$display(),在仿真时得到波形文件
4.在$vcdpluson后面立马调入reset()任务
5.保存和关闭文件。
//test.sv
//此时的文件
//Lab 1 - Task 3, Step 2
//
//Declare a program block with arguments to connect
//to modport TB declared in interface
//ToDo
program automatic test(router_io.TB rtr_io);
//Lab 1 - Task 3, Step 3
//
//Declare an initial block
//In the initial block print a simple message to the screen
//ToDo
initial begin
$vcdpluson;
reset();
end
//Lab 1 - Task 6, Steps 3 and 4 -
//
//Replace $display() in initial block with $vcdpluson
//Call reset() task
//ToDo - Caution!! Do only in Task 6
//Lab 1 - Task 6, Step 2
//
//Define a task called reset() inside the program to reset DUT per spec.
//ToDo - Caution!! Do only in Task 6
task reset();
rtr_io.reset_n = 1'b0;
rtr_io.cb.frame_n <= '1;
rtr_io.cb.valid_n <= '1;
##2 rtr_io.cb.reset_n <= 1'b1;
repeat(15) @(rtr_io.cb);
endtask: reset
endprogram:test
任务七.编译和仿真
A.编译
vcs -sverilog -debug router_test_top.sv test.sv router_io.sv ../../rtl/router.v
-debug这个开关可以结合$vcdpluson函数产生vcdplus.vpd
B.仿真
任务八.用dve查看波形。
后记:
冲冲冲!