Lab2的学习目标是:
- 拓展lab1中的测试平台,从一个输入端向一个输出端发送数据包。
- 用新的测试平台来编译和仿真设计文件。
在这次Lab中,你将继续去搭建测试平台上的相关组件(component) :
- 激励产生器(Stimulus Generator)
- 驱动器(Driver)
- 等。
????????你会使用一些的子程序(routine)去把一个数据包从输入端口3发送到输出端口7,并观察到这个数据包的payload。
在该实验完成时,你将得到如下验证结构。
任务一:声明程序(program)的全局变量
通过路由器发送数据包需要指定
为了使引用这些变量变得简单,您将它们声明为程序全局变量
1、用文本编辑器打开 test.sv文件。
2.、为数据包声明程序全局变量( program globals):
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
....
...
endprogram:test
任务二:产生数据包(?Packet Data)
?????????在lab1中,你通过调用reset()子程序来配置(Configure)待测试模块。
? ? ? ? ?接着你创建一个Task: gen()来继续发展这个测试平台。
????????(gen()是一个可以产生测试激励的子程序)
?1、在程序的initial块中的reset()后面,调用gen()任务。
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
reset();
gen(); //产生测试激励
end
...
endprogram:test
2、在任务 reset() 代码块之后,声明gen()任务:
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
reset();
gen(); //产生测试激励
end
task reset ( );
...
endtask: reset
//gen task
task gen () ;
...
endtask: genend
endprogram: test
3、在 gen ( ) 任务的主体中:
- 将 sa(源地址) 设置为 3,将 da(目标地址) 设置为 7。
- 用 2 到 4 个随机字节(random bytes)来填满有效负载序列(payload queue) 。
完成后,您的代码应如下所示:
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
reset();
gen(); //产生测试激励
end
task reset ( );
...
endtask: reset
//gen task
task gen ( );
sa = 3 ;da = 7 ;
payload.delete ( ) ;//清空队列
repeat ($urandom_range ( 2,4) )
payload.push_back ( $urandom) ;//往队列送数据,repeat(2-4)次,每次写入一个数据
endtask: gen
//系统函数$urandom提供了一种生成伪随机数的机制。
//这个函数每次调用时返回一个新的32位随机数。数字为无符号。
endprogram:test
任务三:创建发送数据包的程序(Routine)
?send_addr()、send_pad()、send_payload()封装在send()里。
在数据包的信息产生后,你准备好创建Transactor和Driver来发送数据包。
把一个数据包发送通过路由器的行为,可以通过以下三个进程来完成:
- 发送Destination Address(目标地址)
- 发送padding bits。(空闲)
- 发送payload。(数据队列)
其中每一个进程将发展成为driver routine中的一个独立部分(device) 。
Driver中各部件和Transactor的区别是:
- 当Transactor调用device driver时,device driver和硬件信号是直接相互作用的。
这些抽象的分层可以使testbench的子程序(routine)更容易管理,更好复用,更加可靠。
以下的步骤可以帮助你构建这些子程序
1、在initial块中,在 gen() 语句之后立即调用 send() 任务发送数据包。
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
reset();
gen(); //产生测试激励数据包
send(); //发送数据包
end
...
endprogram:test
2、在调用 send( ) 调用之后,仿真10 个时钟周期。
????????这将允许观察从路由器出来的数据。否则,当您在 DVE 中查看波形时,输出将被截断。
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
$vcdpluson;
run_fornpackets=21;
reset();
repeat(run_for_n _packets) begin
gen(); //产生测试激励数据包
send(); //发送数据包
end
repeat(10)@(rtr_io.cb ) ;
end
//task
....
...
endprogram:test
3、在program块中添加任务send()的声明。
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
task reset ( );
...
endtask: reset
task gen () ;
...
endtask: genend
task send();
...
endtask: send
...
endprogram:test
4、在 send( ) 任务中,编写以下操作:
- 调用send_addrs()?
- 调用send_pad()
- 调用send_payload()
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
task reset ( );
...
endtask: reset
task gen () ;
...
endtask: genend
task send();
send_addrs()?;
send_pad();
send_payload();
endtask: send
...
endprogram:test
?5.创建 send_addrs ( ) 任务。
这个Driver的部件将会驱动4位的地址进入Router
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
...
task send();
send_addrs()?;
send_pad();
send_payload();
endtask: send
task send_addrs()?;
...
endtask: send_addrs
...
endprogram:test
6. 在 send_addrs ( ) 中,编写以下操作:?
- 驱动frame_n信号作为每一个路由器的配置内容。
- 驱动目标地址通过din信号传送。
- 把din作为单bit的连续信号。使用一个环结构来驱动din,以每周期一bit的速度循环四个周期。(din 是一个单比特串行信号,使用循环结构在每个时钟周期驱动 din 一位,持续四个周期。 )
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
...
task send();
send_addrs()?;
send_pad();
send_payload();
endtask: send
task send_addrs()?;
rtr_io.cb.frame_n[sa] <=1'b0;
for(int i=0; i<4; i++) begin
rtr_io.cb.din[sa] <= da[i];
@(rtr_io.cb) ;
end
endtask:send_addrs
...
endprogram:test
注意:
每个端口由 frame_n.din 和 valid_n 中的一个单独的位表示。 确保指定正确的位。
将输入端口 3 的 frame_n 信号驱动为“1”的形式为:
rtr_io.cb .frame_n [ 3] <= 1'b1 ;
?7、创建 send_pad ( ) 任务。 这个device driver可以驱动五个pad bits进入路由器。
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
...
task send();
send_addrs()?;
send_pad();
send_payload();
endtask: send
task send_addrs()?;
...
endtask: send_addrs
task send_pad();?
...
endtask: send_pad
...
endprogram:test
8、在发送pad( )任务体中,会驱动frame_n,valid_n和din信号作为每一个路由器的配置(specification)
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
...
task send();
send_addrs()?;
send_pad();
send_payload();
endtask: send
task send_addrs()?;
...
endtask: send_addrs
task send_pad();?
rtr_io.cb.frame_n[sa] <=1'b0 ;
rtr_io.cb.valid_n[sa] <= 1'b1;
rtr_io.cb.din [sa] <=1'bl;
repeat (5)e(rtr_io.cb);
endtask: send_pad
...
endprogram:test
9.创建send_payload(),这个device driver发送payload到路由器中。
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
...
task send();
send_addrs()?;
send_pad();
send_payload();
endtask: send
task send_addrs()?;
...
endtask: send_addrs
task send_pad();?
...
endtask: send_pad
task send_payload();
...
endtask: send_payload
endprogram:test
10.在send_payload()任务的body中,有如下操作: .
- 写一个可执行“payload.size()”的次的循环。
- 这个循环内的payload[$]序列中,每8-bit数据被以一个周期一个bit的速度进行传送。
- 记着在每次路由器的配置中驱动valid_n。
- 在每次路由器的配置中,在发送到这个数据包的最后一位时,将frame_n信号反转至1'’ b1.。
- 当数据包发送完成后,将valid_n信号返回到1’ b1。
program automatic test(router_io.TB rtr_io);
int run for_n packets; //number of packets to test
bit[ 3:0] sa; // source address (input port)
bit [3:0] da; // destination address (output port)
logic[7:0] payload[$]; // packet data array
//logic stores 0,1,x and z values
initial begin
...
end
...
task send();
send_addrs()?;
send_pad();
send_payload();
endtask: send
task send_addrs()?;
...
endtask: send_addrs
task send_pad();?
...
endtask: send_pad
task send_payload();
foreach (payload[index] ) begin //队列里面的index不需要定义,也不需要修改
for(int i=0; i<8; i++) begin
rtr_io.cb.din[sa] <= payload [index][i];
rtr_io.cb.valid_n[sa] <= 1'b0 ;
rtr_io.cb.frame_n[sa]<=(index== (payload.size ()-1)) && (i-=7) ;
@(rtr_io.cb);
end
end
rtr_io.cb.valid_ n[sa]<=1 "b1 ;
endtask: send_payload
endprogram:test
注意:
????????特别注意你是怎样提前触发时钟(advance the clock)。
????????如果在一个子程序中您在退出子程序时提前触发时钟,然后在另一个子程序中您在进入子程序时提前触发时钟,可能会导致错误的计时。
11、保存并关闭test.sv文件
任务四:对program进行编译和纠错(Debug)
任务五:拓展program到可以发送21个数据包
1、修改program模块,使用相同的sa=3和da=7,发送21个数据包。(上面是2-4个)
2、编译(Compile),仿真(Simulate),查看波形。
3、对-sv_seed random的仿真选项的认识
(1) 尝试着多次重新启动仿真,可以使用“restart”命令来重启,再对比连续两次生成的随机数据,看看它们之间是否相同呢?
(2)然后再在仿真器命令行处使用下面命令来加载仿真。
vsim -novopt -solvefaildebug -sv_seed 0 work.router_test_top
//这里我们多传递了两个必须的仿真参数,
//-solvefaildebug是为了调试随机变量的,
//-sv_seed NUM则是为了传递随机的种子。
那么使用这个命令再看,是否与之前没有使用-sv_seed 0的命令产生了相同的数据呢?
(3)最后,请改为使用下面命令再来比较前后两次的数据是否相同呢?
vsim -novopt -solvefaildebug -sv_seed random work.router_test_top
那么.你对-sv_seed random的仿真选项的认识是什么? ?
|