- SystemVerilog常用的随机函数
(1) $random() -----平均分布,返回32位有符号随机数;
(2) $urandom()-----平均分布,返回32位无符号随机数;
(3) $urandom_range(int unsigned max,int unsigned min)-----平均分布,在(min,max)范围中返回无符号随机数。
例如lab2代码中:
logic [7:0]payload[$];
repeat($urandom_range(4,2))
payload.push_back($urandom);
第一句是声明队列payload;第二句是通过$urandom_range()函数生成重复次数,这里是随机生成{2,3,4};第三句是通过 $urandom()函数产生一个32位无符号随机数,由于payload声明为8bit,所以通过push_back每次向payload队列中添加一个8bit的数据。
- fork- join
fork-join 结构可以使得其内部语句并行执行。例如lab3代码中:
fork
send();//进程1
recv();//进程2
join
fork-join中的send()和recv()会并行执行。
另外还有fork-join_any,该结构会在其内部任何一个进程完成时退出并行,执行后续代码。例如lab3代码中:
fork : frameo_wd_timer
@(negedge rtr_io.cb.frameo_n[da])//进程1
;//空语句
begin //进程2
repeat (1000) @(rtr_io.cb);
...
end
join_any
disable fork;//杀死所有与之最近的fork中的进程
该部分代码是为了防止仿真器等待不到frameo_n信号的下降沿,从而导致无法退出task get_payload(),进而陷入recv()死循环。 采用fork-join_any会在进程1或者进程2完成时退出,防止仿真出错。
此外还有 fork-join_none结构,该结构不会等待其内部进程完成,直接执行后续的代码。
链接: link.中对这三种结构进行了详细的介绍。
disable fork;语句会杀死与之最近的fork中的代码,即fork:frameo_wd_timer。
与之对应的是wait fork;语句会引起调用的进程阻塞直到所有进程完成,常用于确保所有子进程全部执行结束。
- get_payload数据采集过程
lab3任务get_payload部分代码如下:
task get_payload();
pkt2cmp_payload.delete();
...//省略部分代码
forever begin
logic[7:0] datum;
for(int i=0; i<8; i=i) begin //i=i prevents VCS warning messages
if(!rtr_io.cb.valido_n[da])
datum[i++] = rtr_io.cb.dout[da];
if(rtr_io.cb.frameo_n[da]) begin
if(i==8) begin //byte alligned
pkt2cmp_payload.push_back(datum); //用于采集队列中最后一个byte数据
return; //done with payload 跳出task get_payload 结束数据采集
end
else begin
$display("\n%m\n[ERROR]%t Packet payload not byte aligned!\n", $realtime);
$finish;
end
end
@(rtr_io.cb);
end
pkt2cmp_payload.push_back(datum);//用于采集除最后一个byte数据之外的数据,之后继续执行forever中的语句
end
endtask: get_payload
首先第一个问题:为何在for中采用i=i,循环语句中为i++?
这是因为是实际运行中valid_n信号可能会存在为“1”的情况,此时if语句不成立,数据不会被采集到datum中,如果采用for(i=0,i<8,i++)在循环体中其他语句执行之后i会加一,导致datum[i]采集不到准确的数据。而采用for(i=0,i<8,i=i),当if语句成立时会采集数据并i++,这就防止了在采集过程中valid_n信号为“1”所导致数据不准确。
第二个问题: pkt2cmp_payload.push_back(datum);语句为何出现了两次?
在之前payload()中repeat了{2,3,4},那么一个数据包最多会有4byte数据,这里假设产生了4byte数据,那么前3个byte数据时frameo_n信号一直为0,所以不会执行if语句,当i=8时跳出for循环执行第二个 pkt2cmp_payload.push_back(datum);语句将采集到的数据存入队列get_payload中。当采集第4byte数据时,frameo_n会在i=8时置“1”(由设计文档得出)此时进入if语句,由第一个 pkt2cmp_payload.push_back(datum);语句将采集到的数据存入队列get_payload中,至此4个byte数据都采集到队列get_payload中,最后通过return 跳出forever,完成get_payload。
4.$ sformatf系统函数
该函数标准格式为:$sformatf(format,arge) ,将参数arge按照格式填入format中,组成的字符串作为函数的返回值。lab3代码中:
if(payload.size() != pkt2cmp_payload.size()) begin
message = "Payload size Mismatch:\n";
message = { message, $sformatf("payload.size() = %0d, pkt2cmp_payload.size() = %0d\n", payload.size(), pkt2cmp_payload.size()) };
return (0);
end
$sformatf将payload.siz与pkt2cmp_payload.size分别填入两个%0d中,得到的字符串作为函数的返回值,并与message拼接组成新的message。在后续代码中通过 $display函数打印。
拓展1:
$sformat(str,format,arge)与 $是formatf类似,只不过是将字符串赋值给str,并没有作为函数返回值。例如:
string a;
$sformatf(a,"test=%0d",i);
假设 i=8,那么a=“test=8”。
拓展2:
$sscanf(str,format,arge);将字符串按照format规定的模板进行扫描,即将stdin中输入的字符串赋值给str。
拓展3:
$display(format,arge);将参数arge填入format中并打印显示到stdout。
初次学习,可能会有错误的地方,仅供参考。