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("\n%m\n[ERROR]%t Frame signal time out!\n",$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