SystemVerilog Testbench学习总结(Lab2~3)

1、对于信号几种赋值方式的区别:

1 logic [15:0] frame_n;
2 
3 rtr_io.cb.frame_n <= 1;//port0=1,port1~15=0
4 
5 //如果想对所有的信号赋值,用下面这种方法
6 rtr_io.cb.frame_n <= '1;//port0~15=1
7 
8 //如果只想对信号的某一位单独赋值,用下面这种赋值方法
9 rtr_io.cb.frame_n[5] <= 1'b0;

2、随机数方法和函数

  $urandom_range()
语法:$urandom_range(int unsigned maxval,int unsigned minval = 0);
功能:返回一个在maxval和minval之间的无符号整数

Example:

1 val = $urandom_range(7,0);
2 //等价于
3 val = $urandom_range(7);//如果minval没有指定,默认为0
4 
5 //如果mixval比minval小,参数列表会自动反向
6 val = $urandom_range(0,7);//等价于上面两个例子

  $urandom()
语法:$urandom[(int seed)];
功能:产生伪随机数,每次调用时返回一个32位的无符号伪随机数;
For example:

1 bit [61:1] addr;
2 bit [3:0] number;
3 
4 addr[32:1] = $urandom(254);//初始化生成器,获得一个32位的随机数
5 addr = {$urandom,$urandom};//64位随机数
6 number = $urandom & 15;//4位随机数

在lab2中,使用随机数来产生地址和payload队列的值:

1 task gen();
2     sa = $urandom;
3     da = $urandom;
4     payload.delete();//clear previous data
5     repeat($urandom_range[4,2])
6         payload.push_back($urandom);
7 endtask:gen    

3、更新仿真时间
  Pay particular attention to how you advance the clock. If in one
subroutine you advance the clock upon exiting the subroutine, then
in another subroutine you advance the clock upon entering the
subroutine, erroneous timing may result.

更新仿真时间的方式,@(在tb参数列表中例化的interface名 + clocking名):

For example:

1 @(rtr_io.cb);//单次更新时钟
2 
3 repeat(10) @(rtr_io.cb);//更新10次时钟

在lab2中的时钟更新:
Example 1:

 1 initial begin
 2     $vcdpluson;
 3     run_for_n_packets = 21;
 4     reset();
 5     repeat(run_for_n_packets) begin
 6         gen();
 7         send();
 8     end
 9     repeat(10) @(rtr_io.cb);//更新10个时钟周期的仿真时间,这样做可以保证输出的数据能被观察到
10 end        

Example 2:

1 task send_addrs();
2     rtr_io.cb.frame_n[sa] <= 1'b0;
3     for(int i=0;i<4;i++) begin
4         rtr_io.cb.din[sa] <= da[i];//i'th bit of da
5         @(rtr_io.cb);
6     end
7 endtask:send_addrs        

Example 3:

 1 task send_payload();
 2     foreach(payload[index]) begin
 3         for(int i=0;i<8;i++) begin
 4             rtr_io.cb.din[sa] <= payload[index][i];
 5             rtr_io.cb.valid[sa] <= 1'b0;
 6         //payload初始值为空,不停的装入数据,在装满第七位时,需要将其拉高
 7             rtr_io.cb.frame_n[sa] <= ((i == 7) && (index == (payload.size()-1)));
 8         @(rtr_io.cb);//更新仿真时间
 9         end
10     end
11     rtr_io.cb.valid_n[sa] <= 1'b1;//最后将valid_n拉高;
12 endtask:send_payload

4、看门狗,防止deadlock的产生:
  在lab3中,get_payload需要等到frameo_n的下降沿到来以后,进而进行下一步操作,
如果直接写@(negedge rtr_io.frameo_n[sa])来等待时钟边沿的到来,存在一种潜在的可能,
就是如果代码出错,可能程序永远等不到下降沿。
For example:

1 for(i = 0; i < run_for_n_packets;i++)
2     gen();
3     fork
4         begin
5             send();
6             recv();
7         end
8 join

说明:send()和recv()两个任务本来是并行执行的,但是由于编程时,错误的将其放在了
begin...end内,变成了顺序操作。此时如果使用@(negedge rtr_io.frameo_n[sa])来检测一个新的从dut输出的数据包,
仿真将会死循环。

为了有效的避免这种情况的产生,可以加入看门狗机定时器,防止子程序超时:

 1 fork
 2     begin:wd_timer_fork
 3     fork:frameo_wd_timer
 4     @(negedge rtr_io.cb.frameo_n[sa]);
 5     begin
 6         repeat(1000) @(rtr_io.cb);
 7         $display("
%m
[ERROR]%t Frame signal time out!
",$realtime);
 8         $finish;
 9     end
10     join_any:frameo_wd_timer
11     disable fork
12     end:wd_timer_fork
13 join

说明:在上面代码中,fork...join中的代码是并行执行的。
也即@(negedge rtr_io.frameo_n[sa])和其下面begin...end块中的内容是并行执行的。

fork...join_any中,只要其中任何一个子程序执行,即可跳出此模块执行后续代码,
因此,如果代码出错一直等不到 frameo_n信号下降沿的到来,时钟更新1000次后,看门狗
计数器超时,报错,此时即跳出了此模块,不会进入死循环的情况。

扩展阅读:

fork...join_none模块中,任何线程都不用执行就可以执行下面的代码。

disable fork——停止多个线程,包括从当前线程衍生出来的线程。
使用时为了防止停止过多的线程,应该使用fork...join将目标代码围起来限制disable fork的
作用范围,如上述例子中只停止fork...join中的进程。

For another example:

 1 initial begin
 2         check_trans(tr0);    //线程0
 3         //创建一个线程来限制disable fork的作用范围
 4         fork    //线程1
 5         begin
 6             check_trans(tr1);    //线程2
 7             fork    //线程3
 8             check_trans(tr2);    //线程4
 9             join
10             //停止线程1-4,单独保留线程0
11             #(TIME_OUT/2) disable fork;
12         end
13         join
14 end
15 
16 
17         

5、lab3中出现的打印选项

  %m —— display hierarchical name

  %p —— display an assignment pattern

Example:

 1 function bit compare(ref string message);
 2     ...
 3     message = "Payload Content Mismatch:
"
 4     message = {message,$sformatf("Packet Sent:    %p
Pkt         Received:    %p",payload,pkt2cmp_payload)};
 5     return(0);
 6 ...
 7 endfunction:compare
 8 
 9 task check();
10     string name;
11     static int pkts_checked = 0;
12     if(!compare(message)) begin
13         $display("
%m
[ERROR]%t Packet #%0d %s
",$realtime,pkts_checked,message);
14         $finish;
15     end
16     $display("[NOTE]%t Packet #%0d %s",$realtime,pkts_checked,message);//[NOTE]137350.0ns Packet #38 Successfully Compared!
17 endtask:check

说明:上面的例子中,如果check()调用compare()函数时,若刚好函数中的那部分代码被执行,即发送的代码与回读内容不一致,则返回0;
成都市check()调用compare(),执行if条件成立的语句,即$display(" %m [ERROR]%t Packet #%0d %s ",$realtime,pkts_checked,message);
就会打印出如下的信息:

1 router_test_top.t.check    //%m 打印出的信息,即出现问题地方名字的层次结构
2 [ERROR]141650.0ns Packet #39 Payload Content Mismatch:
3 Packet Send: '{'h3a, 'hc7, 'hbd, 'h84} //%p打印出的信息,即显示一个赋值形式
4 Pkt Received: '{'hxx, 'hxx, 'hxx, 'hxx}

6、仿真控制系统任务:$finish $stop $exit

  $stop ——终止仿真

  $finish ——退出仿真器,将控制返回给主操作系统

  $exit —— wait for all program blocks to complete,and then make an imlicit call to $finish

注:$stop和$finish有可选的参数0,1,2,决定什么类型的诊断信息被打印:
0 —— printing nothing
1 —— printing simulation time and location
2 —— printing simulation time,location and statistics about the memory and Central Process Unit(CPU) time used in simulation




原文地址:https://www.cnblogs.com/loves6036/p/5752798.html