最近需要学习SystemVerilog,入门是SV_LAB。因此在EETOP搜集了一些学习资料。
相关资料 SV_LAB、SV_TestbenchGuider、SV_LABGuider,是可以在EETOP搜索。
快捷跳转如下:
(1)2016最新Synopsys 官方Lab http://bbs.eetop.cn/thread-614054-1-1.html
(2)SystemVerilog Testbench Student Guide & Lab Guide http://bbs.eetop.cn/forum.php?mod=viewthread&tid=357937
篇中所说的绿皮书,指的是《SystemVerilog验证 测试平台编写指南》的中文版,张春...译的那本;
这里只是学习笔记,方便自己查看,写有不周,望理解。
目录
二、测试平台顶层代码
一、DUT
1.1 DUT端口
由于第一个LAB比较简单,这里先介绍一下DUT;

DUT是一个16进16出的路由器,你看din、frame_n、valid_n都是16位,din一位就表示一个路由的输入口,dout同理。
1.2 DUT输入时序
输入时序中,从frame_n拉低开始,在frame_n拉高结束,注意最后一个数据时frame_n是为高的。
输入时序中,din分为三个部分:(1)addr 占4个周期,(2)pad 占5个周期,(3)data 长度不定。
输入时序中,valid_n在(1)输入addr阶段时不关注,在(2)输入pad阶段为高,在(3)输入数据阶段指示数据是否有效。
其中地址4位,范围0~15,表示数据要从哪个dout转发出去。

1.3 DUT输出时序
输出时序,相对简单一些,使用frameo_n下降沿表示开始,frameo_n上升沿表示结束。
然后,转发时序中只包含data阶段,在传输过程中valid_n用来指示数据线是否有效。

1.4 DUT代码参考
位于sv_lab的rtl目录下,接口可参考1.1中图。
1.5 Interface 代码
由于6个lab,接口代码 router_io.sv都一样,所以提前介绍了;
(1)接口信号,使用logic定义;
(2)定义clocking块定义时钟域,指定信号的方向;这里方向是 相对于测试平台而言的。
(3)使用modport将信号分组。
关于这个分组的含义主要是为了方便和安全吧,关于接口的说明位于绿皮书4.2章节。然后关于使用modport后信号赋值的问题,有自己的一些疑惑和思考,10月27号之前尽快做点实验出一个篇7。
这里有个大佬讲的挺好,放出链接 https://blog.csdn.net/liubin1222/article/details/81169314
摘抄一个重点:“大概是这个意思的:input #1ns 指的是采样时间相对时钟上升沿提前1ns,但不在波形上显示。用来模拟真实电路中的建立时间。 output #1ns指的是驱动时间相对时钟上升沿推后1ns,会在波形上显示出来。用来模拟真实电路中的传播延时。”
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;
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
modport TB(clocking cb, output reset_n);
endinterface: router_io
二、测试平台顶层代码
对应代码是lab中的 router_test_top.sv 。这是搭建测试平台的最重要一步,主要有五方面内容:定义时钟 --- 定义接口 --- 接口连接测试平台 --- 接口连接待测模块 --- 产生时钟信号。
请务必理解和理清这5个步骤,只有只有这样才能理解测试平台呀。数字电路的测试,总归还是以时钟和时序为中心的,只不过驱动时序的表述使用了高级语言,比verilog方便而已。
2.1 定义时钟
定义了一个bit信号,这里用作时钟。bit在SV中是一个双状态数据类型,即只有0和1,用绿皮书2.1.2中说法,这“有利于提高仿真器稳定性”。
bit SystemClock;
2.2 定义接口
以2.1定义的时钟信号作为关联输入,定义了一个接口变量;其中 router_io 就是1.5中定义的与DUT相关的interface类型,也就是在这里接口top_io中的clock信号和顶层中的SystemClock信号关联起来。然后,top_io中信号的赋值将在SystemClock作用下完成。
router_io top_io(SystemClock);2.3 接口连接测试平台
将接口传递个驱动模块,这里test是用来产生激励的program块,具体在第三节叙述,其作用是驱动top_io中的信号。
test t(top_io);2.4 接口连接待测模块
将接口与DUT连接,在该实验中test程序块驱动接口信号,那么只要将接口的信号与DUT相连接,就可以驱动DUT,或收集DUT输出了。
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)
);2.5 产生时钟信号
启动平台,就是产生是时钟,然后测试平台和DUT就在时钟作用下开始工作。
initial begin
$timeformat(-9, 1, "ns", 10);
SystemClock = 0;
forever begin
#(simulation_cycle/2) SystemClock = ~SystemClock;
end
end三、 测试平台的驱动代码 test.sv
(1)首先在顶层需要把router_io类型的变量传递进来,而且指定了modport组,明确接口中信号的类型是input还是output。
(2)在task reset()任务中,将reset_n等信号置为默认值,然后等待15个周期结束该task。
思考一点:这里驱动信号时并没有指明时钟信号、时钟触发沿,那是因为时钟在声明接口top_io时已经指明了是SystemClock,没有指明触发沿是因为在interface中定义clocking cb时已经指明了触发沿。
值得说明,这个teat并没有对dut输入太多内容,仅仅是一个复位。然后这里task中赋值,有两个疑问,待做实验探究:
(1)通过 rtr_io.reset_n = 1'b0 赋值与通过 rtr_io.cb.reset_n <= 1'b1 的区别 ?
(2)##2是等待两个时钟周期的时间,还是等待两个时钟触发沿(完全等同于repeat(2) @(rtr_io.cb)) ?
program automatic test(router_io.TB rtr_io);
initial begin
$vcdpluson;
reset();
end
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