前言
之前把环境搭起来,写好了gen、drv、mon后就感觉自己理解的还是有点问题,歇了好长一段时间。最近听大神讲了好多细节,感觉理解加深了很多,于是乎继续开动把checker加入环境顺便构建一个合理的结束仿真机制(之前的结束仿真太简单粗暴了)。
相关博文:
【验证小白】只有SV+modelsim学验证(1)——把平台搭起来_尼德兰的喵的博客-CSDN博客_modelsim sv
【验证小白】只有SV+modelsim学验证(2)——加monitor到环境中_尼德兰的喵的博客-CSDN博客_sv中的monitor
修理下pkt_data
既然要加入checker,那么必然涉及到数据对比以及对比失败后数据打印,因此上来就要把pkt_data修理一下,主要修改如下(最后会附完整代码)。
compare函数主要对比了两个属性(实际上我们的数据也没有更多的属性了。。),数据长度和每一拍数据值:
function bit pkt_data::compare(pkt_data to);
bit match = 1;
if(this.pkt_len != to.pkt_len) match = 0;
foreach(payload_q[i]) if(this.payload_q[i] != to.payload_q[i])
match = 0;
return match;
endfunction:compare
psprintf函数把所有需要打印的信息加入到string中,方便之后调用:
function string pkt_data::psprintf(string preset = "");
psprintf = {preset, $psprintf("pkt_len = %0d", pkt_len)};
foreach(payload_q[i])
psprintf = {psprintf, ",", $psprintf("payload[%0d] = 'h%0h", i, payload_q[i])};
endfunction
?,pkt_data修理完成。
pkt_chk
接下来就可以添加愉快的添加checker了,在这里我就把checker和scoreboard两个组建合二为一,毕竟结构这么简单没必要搞两个了。关于checker和scoreboard,有兴趣可以看一下SV绿皮书320页的一些代码,其中使用了回调方法(关于回调方法请参考237页),我就不用了就搞一个最最最简单的好了。
checker顾名思义是要将两个来源的数据进行对比,如果如果对比不通过就要报error提醒环境或者DUT做错了。因此定于checker类如下。
`ifndef PKT_CHK_SV
`define PKT_CHK_SV
`include "pkt_data.sv"
`include "env_cfg.sv"
class pkt_chk;
env_cfg cfg;
pkt_data expect_q[$];
int in_expect, in_actual;
int match, not_match;
mailbox gen2chk_chan;
mailbox mon2chk_chan;
//for finish simu
int idle_cnt;
extern function new(env_cfg cfg,
mailbox gen2chk_chan,
mailbox mon2chk_chan);
extern virtual task run();
extern virtual task expect_gain();
extern virtual task actual_gain();
extern virtual task set_idle();
extern virtual function report();
endclass:pkt_chk
两个信箱分别用来接收环境预期和DUT输出,当然了这个乞丐版环境没有DUT,于是乎gen2chk_chan接收由gen发来的信元作为预期数据,mon2chk_chan接收由mon采集来的信元作为实际数据(具体见1中的结构),如果可以对比通过则基本保证组建是没问题的(后面一仿真直接报error了。。真打脸,发现pkt_data中的pack函数写错了。。。)。四个int类型数据分别用来记录预期数据总量,实际数据总量,对比通过个数和对比不过个数。
这个checker实现简单的顺序对比功能。
这里还加了一个env_cfg类,主要是为了结束仿真用的,回头再说。
函数个功能介绍如下。
run()
task pkt_chk::run();
fork
expect_gain();
actual_gain();
set_idle();
join_none
endtask:run
run()很简单,就是把所有的方法调用起来。
expect_gain()
task pkt_chk::expect_gain();
pkt_data expect_data;
while(1) begin
gen2chk_chan.get(expect_data);
this.expect_q.push_back(expect_data);
in_expect++;
idle_cnt = 0;
//$display("At %0t, [CHK NOTE]: get a expect pkt", $time);
end
endtask:expect_gain
expect_gain()方法用来从gen2chk_chan中获取数据,一旦chan为空则会阻塞在get语句。获取数据后放入队列expect_q中等待对比。
actual_gain()
task pkt_chk::actual_gain();
pkt_data expect_data;
pkt_data actual_data;
while(1) begin
mon2chk_chan.get(actual_data);
in_actual++;
idle_cnt = 0;
if(this.expect_q.size == 0) $display("At %0t, [CHK ERROR]: expect_q==0???", $time);
else begin
expect_data = expect_q[0];
if(!expect_data.compare(actual_data)) begin
$display("At %0t, [CHK ERROR]: no match, \nexpect data:%s \nactual data:%s ", $time, expect_data.psprintf(), actual_data.psprintf());
expect_q.pop_front();
not_match++;
end
else begin
expect_q.pop_front();
//$display("At %0t, [CHK NOTE]: match, \nexpect data:%s \nactual data:%s ", $time, expect_data.psprintf(), actual_data.psprintf());
match++;
end
end
end
endtask:actual_gain
actual_gain()方法把获取mon2chk_chan数据和数据对比合在了一起。思路就是从chan获取mon实际采集的数据后,与expect_q[$]中最顶头的数据进行对比,如果对比不通过则报[CHK ERROR]。而无论对比是否通过,均将expect_q[$]顶头数据取走。
在对比的同时,进行相关的计数。核心task!!!!!!
report()
function pkt_chk::report();
$display("----------------------------------------------------------------------------------------------------");
$display("[CHECKER REPORT]expect pkt_num=%0d, actual pkt_num=%0d, match pkt_num=%0d, not match pkt_num=%0d", in_expect, in_actual, match, not_match);
$display("----------------------------------------------------------------------------------------------------");
endfunction:report
仿真结束后的打印,报告匹配数据的个数等信息。
set_idle()
与结束仿真相关,下次再说吧。
修理ENV
?,checker添加完成,我们需要将其添加到环境当中,作为组件运转起来。env的修改还包括了添加env_cfg以及调整反正结束机制,在下个博客中说明。
env中新添加了gen2chk_chan,连接gen和chk。也就是说gen在给drv发送数据时候也会把同样一份数据复制一下发给chk,作为chk中数据比对的依据,具体代码也见下一篇博客吧。
`ifndef ENV_SV
`define ENV_SV
`include "pkt_gen.sv"
`include "pkt_drv.sv"
`include "pkt_mon.sv"
`include "pkt_chk.sv"
`include "pkt_if.sv"
`include "env_cfg.sv"
class environment;
pkt_gen gen;
pkt_drv drv;
pkt_mon mon;
pkt_chk chk;
env_cfg cfg;
mailbox gen2drv_chan;
mailbox gen2chk_chan;
mailbox mon2chk_chan;
vdrv dif;
vmon mif;
int send_pkt_num;
//for finish fimu
int wait_time;
extern function new(input vdrv dif,
input vmon mif
);
extern virtual task build();
extern virtual task run();
extern virtual task report();
endclass
function environment::new(input vdrv dif,
input vmon mif
);
this.dif = dif;
this.mif = mif;
this.cfg = new();
endfunction
task environment::build();
$display("At %0t, [ENV NOTE]: environment::build() start!", $time);
gen2drv_chan = new();
gen2chk_chan = new();
mon2chk_chan = new();
gen = new(cfg, gen2drv_chan, gen2chk_chan);
drv = new(cfg, dif, gen2drv_chan);
mon = new(cfg, mif, mon2chk_chan);
chk = new(cfg, gen2chk_chan, mon2chk_chan);
$display("At %0t, [ENV NOTE]: environment::build() over!", $time);
endtask
task environment::run();
fork
drv.run();
gen.run();
mon.run();
chk.run();
join_none
#100;
fork
$display("At %0t, [ENV NOTE]: wait for end............", $time);
begin
wait(cfg.gen_idle);
wait(cfg.drv_idle);
wait(cfg.mon_idle);
wait(cfg.chk_idle);
$display("At %0t, [ENV NOTE]: normal finish", $time);
end
begin
while(1)begin
@(negedge top.clk);
if(this.dif.vld) wait_time = 0;
else wait_time++;
if(wait_time > this.cfg.env_wait_pkt_time) break;
end
$display("At %0t, [ENV ERROR]: time out!!!!!", $time);
end
join_any
#1000;
report();
endtask
task environment::report();
$display("At %0t, [ENV NOTE]: report start", $time);
repeat(100) @top.clk;
chk.report();
$display("At %0t, [ENV NOTE]: report over", $time);
endtask
`endif
其他
附上pkt_data的完整代码,pack函数也有修改(之前做错了。。),其他组件的完整代码下一篇附上。
`ifndef PKT_DATA_SV
`define PKT_DATA_SV
class pkt_data;
rand bit [7:0] payload_q[$];
rand int interval;
rand int pkt_len;
bit send_over;
bit [10:0] data[$];
constraint data_size_cons{
payload_q.size() == pkt_len;
};
constraint pkt_len_cons{
pkt_len inside {[1:50]};
//pkt_len == 5;
};
constraint interval_cons{
interval inside {[3:6]};
};
extern function new();
extern virtual function string psprintf(string preset = "");
extern virtual function bit compare(pkt_data to);
extern virtual function void pack();
extern virtual function void unpack();
extern virtual function pkt_data copy(pkt_data to=null);
endclass
function pkt_data::new();
endfunction
function bit pkt_data::compare(pkt_data to);
bit match = 1;
if(this.pkt_len != to.pkt_len) match = 0;
foreach(payload_q[i]) if(this.payload_q[i] != to.payload_q[i])
match = 0;
return match;
endfunction:compare
function void pkt_data::pack();
foreach (this.payload_q[i]) begin
if (i==0 & i==pkt_len-1) //modify!!!!!
this.data.push_back({1'b1, 1'b1, 1'b1, payload_q[i]});
else if (i==0)
this.data.push_back({1'b1, 1'b1, 1'b0, payload_q[i]});
else if (i==pkt_len-1)
this.data.push_back({1'b1, 1'b0, 1'b1, payload_q[i]});
else
this.data.push_back({1'b1, 1'b0, 1'b0, payload_q[i]});
end
for(int i=0; i<interval; i++)begin
this.data.push_front({'0});
end
endfunction
function string pkt_data::psprintf(string preset = "");
psprintf = {preset, $psprintf("pkt_len = %0d", pkt_len)};
foreach(payload_q[i])
psprintf = {psprintf, ",", $psprintf("payload[%0d] = 'h%0h", i, payload_q[i])};
endfunction
function pkt_data pkt_data::copy(pkt_data to=null);
pkt_data tmp;
if (to == null)
tmp = new();
else
$cast(tmp, to);
tmp.interval = this.interval;
tmp.pkt_len = this.pkt_len;
tmp.send_over= this.send_over;
foreach(this.payload_q[i])begin
tmp.payload_q.push_back(this.payload_q[i]);
end
return tmp;
endfunction
function void pkt_data::unpack();
this.pkt_len = payload_q.size();
endfunction
`endif