lab1实验介绍
实验的工具:Questa
设计模块介绍
实验中的待测试模块(DUT)是一个16输入,16输出的路由器,这个路由器的功能是把数据通过各个输入端(Input)发送到任意输出端(Output)。下面是最终完成的整个验证平台示意图。
实验文件
打开实验文件,你将会看到如下实验代码的结构,其中rtI中为设计代码,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
在该实验完成时,你将得到如下验证结构。
program
把所有产生激励、监测激励、比较激励等验证部分都放在program 。
这里的program只做了reset()
reset
所有的正常工作的数字逻辑要做的事情,除了上电以外,power、reset、clock缺一不可。
reset可以让在时钟供给的情况下,让所有的寄存器被reset为一个稳定的值。
思考:如果没有reset,设计是一个什么状态。
任务:发送一个激励,连接到router的reset_n接口。
验证环境:program
通过什么连接:interface(充当验证环境和DUT之间的一个媒介,interface router_io)
顶层top(testbench)做什么
- 分别例化program、例化router design、例化 router io 的interface
- 把program、interface、DUT连接起来(硬件之间的通信,都是基于pin level的)
实验流程
- 创建SV接口(interface)文件
- 创建SV测试程序文件(program,验证组件环境),重置(reset)路由器
- 创建SV测试的壳文件(即TOP文件)
- Top文件中添加接口的实例化(instance)
- 实例化这个测试程序( test program)
任务一:创建SV接口(interface)文件
1、创建router_io.sv文件,并用编辑器打开它。
2、以下是需要连接到DUT的信号。
查看router.v设计文件,可以看到DUT的端口信号
写把这些信号写入interface中
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;
endinterface: router_io
注意:
在这一部分的所有接口都是异步且没有方向的(例如input,output,inout就是有方向)。
接口的方向只能在针对同步信号的时钟模块(Clocking block)或是针对异步信号的(modport)中被说明。
在下一步中,我们会去创建同步(synchronous)信号给测试程序(test program)从而可以驱动(drive)和采样(sample)DUT中的信号。
3、声明一个由时钟上升沿所驱动的时钟模块(Clocking block,以后简称CB)。
这个时钟模块将会被测试程序用来实施同步驱动(drive)和采样(sample)。
这个CB中的所有信号的方向(direction)必须和DUT中的信号方向一致。
clocking作用:
1、clocking首先规定了方向。如果利用clocking做驱动,方向是固定的
例子, 如果驱动信号din,是向外驱动的。但是不能驱动dout,因为dout是input,是一个输入端。
2、clocking模拟硬件驱动和采样
clocking很重要,之后的实验的数据驱动,都是从 clocking走的。
查看router.v设计文件,可以看到DUT的端口信号的方向
为什么设计里面信号的方向和interface里面的信号方向 不一致呢?
我理解的是,
向设计输入信号din,是从interface输出信号din,再输入到设计的
设计输出信号dout,输出到interface。interface是输入信号dout的。
即这个CB中的所有信号的方向(direction)必须和DUT中的信号方向一致。
interface router_io (input bit clock) ;
logic reset_n;
logic [15:0] din;
...
logic [15:0] frameo_n ;
//通过将信号放置到具有特定于以下的方向的时钟块中来创建同步信号
clocking cb @(posedge clock) ;
output reset_n;
output din ; // no bit reference
output frame_n; // no bit reference
output valid_n; // no bit reference
input dout; // no bit reference
input valido_n; // no bit reference
input busy_n; // no bit reference
input frameo_n; // no bit reference
endclocking: cb
endinterface : router_io
4、如果需要的话,可以对input和output的 (skew)添加说明。
interface router_io (input bit clock) ;
...
clocking cb @(posedge clock);
default input #1ns output #1ns ;//模拟硬件上面的建立与保持时间
//从clocking驱动出来的数据,相对于上升沿,有1ns的delay
// input #1ns 对里面input的采样,是在时钟上升沿前1ns位置做数据采样,这样从input采样的数据更稳定
//output #1ns 时钟上升沿之后有1ns延迟
output reset_n;
output din; // no bit reference
...
endclocking: cb
endinterface: router_io
5.最后创建一个modport TB (),来连接这个测试程序。
在modport TB ()的参数(argument)列表中,应该引用
- 时钟模块CB(我们之前创建的)(同步时钟信号)
- 其它所有可能会用到的异步(asynchronous)信号。
interface router_io( input bit clock ) ;
...
clocking cb @(poseclge clock ) ;
default input #1 output #1;
output reset_n ;
output din ; //no bit reference
...
endclocking: cb
modport TB(clocking cb, output reset_n);
endinterface: router_io
6、保存并关闭router_io.sv文件 。
modport的作用:
在接口interface中使用modport结构能够将信号
- 分组
- 指定方向。
在接口内部声明modport将信号分组,并指定方向
//带有modport的接口
interface arb_if(input bit clk);
logic [1:0] grant,request;
logic rst;
//将信号分类为TEST一类
modport TEST (output request,rstm, //指定输出方向
input grant,clk); //指定输入方向
//将信号分类为DUT一类
modport DUT (intput request,rstm,clk, //指定输入方向
output grant); //指定输出方向
endinterface
在接口内部使用modport方法
//在接口中使用modport
//modport名(TEST或DUT)放在接口名(arb_if)的后面,用 . 连接
//arb_if.TEST arbif arb_if.DUT arbif
module test(arb_if.TEST arbif);
....
endmodule
module arb(arb_if.DUT arbif);
....
endmodule
任务二:创建SV测试程序文件并重置(reset)路由器
1、创建测试程序文件test.sv,并用编辑器打开它。
2、在这个文件中,引用接口模块中的modport TB作为参数( argument),来将interface和test program连接在一起。
program automatic test (router_io.TB rtr_io);
...
endprogram: test
3.在这个程序(program)模块中,定义一个任务(Task) : Reset(),实现重置DUT的功能。
(reset_n既可以是同步信号也可以是异步信号)
program automatic test (router_io.TB rtr_io);
...
task reset () ;
rtr_io. reset_n = 1'b00 ;
rtr_io.cb.frame =<'1;
rtr_io.cb.valid_n <='1;
#2 rtr_io.cb.reset_n <= 1 'bl;
repeat(15) @(rtr_io.cb) ;
endtask : reset
endprogram: test
4、在初始化模块中(initial begin),调用reset ()任务来重置DUT。
program automatic test (router_io.TB rtr_io);
initial begin
reset();
end
task reset () ;
rtr_io. reset_n = 1'b0 ;
rtr_io.cb.frame =<1'b1;
rtr_io.cb.valid_n <='1'b1;
#2 rtr_io.cb.reset_n <= 1'b1;
repeat(15) @(rtr_io.cb) ;
endtask : reset
endprogram: test
5、保存并关闭test.sv文件
任务三:创建SV测试的壳文件(即TOP文件)
1、创建和打开router_test_top.sv文件。
2、输入如下基本结构。
module router_test_top;
parameter simulation_cycle = 100;
bit Systemclock;
router dut (
.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
systemClock = 0 ;
forever begin
#(simulation_cycle/ 2)
systemClock = ~SystemClock;
end
endendmodule
3、给Top文件中添加接口的实例化(instance)
module router_test_top;
parameter simulation_cycle = 100;
bit SystemClock;
//Instantiate interface
//时钟是interface的输入
router_io top_io(Systemc1ock) ;
router dut ( ... );
initial begin
systemClock = 0 ;
end
endmodule
4、实例化这个测试程序( test program)
通过 interface instance建立l/O连接
通过将测试程序在top中的例化t 和接口在top中的例化top_io相联系,将test程序和Top连接在一起
module router_test_top;
parameter simulation_cycle - 100 ;
bit Systemclock;
router_io top_io(systemClock);// instantiating interface
test t(top_io) ; // add program
router dut ( .. . );
initial begin
systemClock = 0;
...
end
endmodule
5. 将待测试模块和top_io连在一起,实现DUT与Top的连接。
router dut (
//top_io.接口 通过interface instance连接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)
);
6、添加`timescale和$timefornat到Top中。
`timescale 1ns/100ps //Set time (%t)to ns scale
module router_test_top ;
router dut ( ... );
initial begin
$timeformat (-9,1,"ns" ,10);
SystemClock = 0;
end
endmodule
7.保存并关闭router_test_top.sv文件。
任务四:编译(compile)和仿真(simulate)
拥有四个文件:
- 待测试文件:router.v
- 接口文件:router_io.sv
- 测试文件:test.sv
- 顶层文件:router_test_top.sv
新建项目
打开软件Questa
新建项目:new——>project
注意:项目路径没有中文
添加文件lab1
reference用的是软链接
读入需要的文件
编译文件
- 先编译design
- 然后编译test.sv(program)
- 最后编译router_test_top.sv
注意:在test.top文件里面include了interface( route_io文件) 。所以不需要额外编译interface
示例:
编译design(router.v文件)
打开work,可以看到之前编译的文件
仿真
选择这个 router_test_top.sv 跑仿真
出现一个sim仿真窗口,显示当前层级关系。
在跑仿真之前,先输入这个命令
log -r /*
//存储当前仿真环境的所有硬件信号的波形
//-r表示递归的去存储最顶层(/*)路径以下所有硬件层次
跑仿真
举例,先跑100ns
发现没有波形显示。
添加波形
注意选择的模块不一样,信号不一样
比如,选择dut模块,里面不仅有输入输出信号,还有内部信号
观察波形
1、如果想看dut信号波形
把dut模块的边界信号(input output),拖到wave 窗口
2、选中interface
点击wave窗口左下角的toggle leaf name
可以把层次路径勾选掉