系统框图:
总体上看,实验2 转到实验3,把更多的验证环境好而组建都放置到package里面,chnl_trans的内容丰富了很多,变得更复杂了。实验二里面的chnl_trans是在generator里面完成赋值的,实验三赋值、随机化则是自己完成的
知识点:
信箱:
旗语: 主要是可以实现对同一资源的访问控制,semaphore是SV内建的类,因此需要句柄,方法包含new(),get(),put(),try_get()。旗语想当一个篮子,想要获取资源必须从篮子里面获取一把后者多把钥匙,获取完资源后必须把钥匙放回篮子里面去。另外一个线程想要获取资源的话,必须等待篮子里面拥有足够多的钥匙才可以,否则会是一直阻塞的状态。
断言: 断言分为立即断言和并发断言,断言属性类似于if语句中的条件表达式,用于检查表达式中的值是否为真。
随机约束
主要是数据空闲周期和包数据周期,data.size被限制在[4:8],data_nidles 限制在[0:2],pkt_nidle限制在[3:5]
data.size inside {[4:8]};
/*...................................*/
constraint cstr{
data.size inside {[4:8]};
foreach(data[i]) data[i] == 'hC000_0000 + (this.ch_id<<24) + (this.pkt_id<<8) + i;
soft ch_id == 0;
soft pkt_id == 0;
data_nidles inside {[0:2]};
pkt_nidles inside {[3:5]};
};
/*...................................*/
task chnl_write(input chnl_trans t);
foreach(t.data[i]) begin
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 1;
intf.drv_ck.ch_data <= t.data[i];
wait(intf.ch_ready === 'b1);
$display("%0t channel initiator [%s] sent data %x", $time, name, t.data[i]);
repeat(t.data_nidles) chnl_idle();
end
repeat(t.pkt_nidles) chnl_idle();
endtask
仿真结束:原本在 chnl_root_test 类中用来结束仿真的$finish()变迁到 generator 中。
agent部分不再控制发送数据的多少,只是控制运行generate和initiator,发送ntrans的数量在generate里面控制,initiator里面因为forever的存在是一直运行的。如果放在165行代码的位置,会提前结束,发送完一次数据就会结束仿真。
165处会有个breakpoint,生成的对象只有1184个,$finish 在这个位置中,程序会提早结束,因为fork jion_any中有一个程序结束就会导致程序直接结束。
解决方法:initiator是不会结束的,所以需要使用generate来结束控制agent结束,从而使得整个结束掉。所以在chnl_root_test中在三个agent运行结束以后finish
Could we put c.obj_id = this.obj_id here? and why?
obj_id是一个local静态变量,处于一个独立了的空间,句柄C和this都是通过访问静态变量来获得obj_id的值,相当于是自己给自己赋值,所以有没有这句话是一样的。
restart之后两个数据是一样的,因为默认产生随机数的种子是相同的。运行仿真器命令vsim -novopt -solvefaildebug -sv_seed 0 work.tb1结果也是一样的,运行vsim -novopt -solvefaildebug -sv seed random work.tb1,结果不一样。
function new();
this.obj_id++;
endfunction
mailbox部分:
generate首先生成一个req然后放到req_mb里面,然后传递给initiator,initiator会条用clone产生一个rsp,使得rsp.rsp=1,再放到rsp_mb里面。然generate还会从rsp_mb里面拿回一个rsp的句柄。在initiator里面并没有例化这两个信箱,所以req_mb和rsp_mb都是句柄,句柄都是指向generator里面,在agent里面赋值句柄,把generator两个不悬空的句柄赋值给initiator中悬空的句柄
更加灵活的控制:
实现不同的test,需要对generator里面作出不同的控制,控制对象就是chnl_trans。可以达到在顶层里面控制chnl_trans的效果。在chnl_root_test里面声明initiator和generator的句柄,然后利用信箱进行连接。
function new(string name = "chnl_root_test");
foreach(agent[i]) begin
this.agent[i] = new($sformatf("chnl_agent%0d",i));
this.gen[i] = new();
this.agent[i].init.req_mb = this.gen[i].req_mb;
this.agent[i].init.rsq_mb = this.gen[i].rsq_mb;
end
this.name = name;
$display("%s instantiate objects", this.name);
endfunction
参考gen[0]书写gen[1] 、gen[2]
virtual function void do_config();
super.do_config();
assert(gen[0].randomize() with {ntrans==100; data_nidles==0; pkt_nidles==1; data_size==8;})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
assert(gen[1].randomize() with {ntrans==50; data_nidles inside {[1:2]}; pkt_nidles inside {[3:5]}; data_size==6;})
else $fatal("[RNDFAIL] gen[1] randomization failure!");
assert(gen[2].randomize() with {ntrans==80; data_nidles inside {[0:1]}; pkt_nidles inside {[1:2]}; data_size==32;})
else $fatal("[RNDFAIL] gen[2] randomization failure!");
分别对chnl_burst_test和chnl_fifo_ fullt_test中的config进行配置
仿真的参数传递选择测试的种类:对比tb1和tb3
//更改测试的话,需要重新更改代码,重新编译
import chnl_pkg1::*;
chnl_intf chnl0_if(.*);
chnl_intf chnl1_if(.*);
chnl_intf chnl2_if(.*);
chnl_basic_test basic_test;
chnl_burst_test burst_test;
chnl_fifo_full_test fifo_full_test;
initial begin
basic_test = new();
basic_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
basic_test.run();
end
结合系统框图 ,根据脚本命令选择测试类型,如果没有声明的话则会自动选择basic_test测试
import chnl_pkg2::*;
chnl_intf chnl0_if(.*);
chnl_intf chnl1_if(.*);
chnl_intf chnl2_if(.*);
chnl_basic_test basic_test;
chnl_burst_test burst_test;
chnl_fifo_full_test fifo_full_test;
chnl_root_test tests[string];
string name;
initial begin
basic_test = new();
burst_test = new();
fifo_full_test = new();
tests["chnl_basic_test"] = basic_test;
tests["chnl_burst_test"] = burst_test;
tests["chnl_fifo_full_test"] = fifo_full_test;
if($value$plusargs("TESTNAME=%s", name)) begin
if(tests.exists(name)) begin
tests[name].set_interface(chnl0_if, chnl1_if, chnl2_if);
tests[name].run();
end
else begin
$fatal($sformatf("[ERRTEST], test name %s is invalid, please specify a valid name!", name));
end
end
else begin
$display("NO runtime optiont TEST=[testname] is configured, and run default test chnl_basic_test");
tests["chnl_basic_test"].set_interface(chnl0_if, chnl1_if, chnl2_if);
tests["chnl_basic_test"].run();
end
end
endmodule
测试平台的结构:
增加了组件monitor和checker。
task mon_trans();
mon_data_t m;
forever begin
@(posedge intf.clk iff (intf.mon_ck.ch_valid==='b1 && intf.mon_ck.ch_ready==='b1));
m.data = intf.mon_ck.ch_data;
mon_mb.put(m);
$display("%0t %s monitored channle data %8x", $time, this.name, m.data);
end
endtask
endclass
task mon_trans();
mon_data_t m;
forever begin
@(posedge intf.clk iff intf.mon_ck.mcdt_val==='b1);
m.data = intf.mon_ck.mcdt_data;
m.id = intf.mon_ck.mcdt_id;
mon_mb.put(m);
$display("%0t %s monitored mcdt data %8x and id %0d", $time, this.name, m.data, m.id);
end
endtask
endclass
class chnl_agent;
local string name;
chnl_initiator init;
chnl_monitor mon;
virtual chnl_intf vif;
function new(string name = "chnl_agent");
this.name = name;
this.init = new({name, ".init"});
this.mon = new({name, ".mon"});
endfunction
function void set_interface(virtual chnl_intf vif);
this.vif = vif;
init.set_interface(vif);
mon.set_interface(vif);
endfunction
task run();
fork
init.run();
mon.run();
join
endtask
endclass: chnl_agent
task do_compare();
mon_data_t im, om;
forever begin
out_mb.get(om);
case(om.id)
0: in_mbs[0].get(im);
1: in_mbs[1].get(im);
2: in_mbs[2].get(im);
default: $fatal("id %0d is not available", om.id);
endcase
if(om.data != im.data) begin
this.error_count++;
$error("[CMPFAIL] Compared failed! mcdt out data %8x ch_id %0d is not equal with channel in data %8x", om.data, om.id, im.data);
end
else begin
$display("[CMPSUCD] Compared succeeded! mcdt out data %8x ch_id %0d is equal with channel in data %8x", om.data, om.id, im.data);
end
this.cmp_count++;
end
endtask
endclass
class chnl_root_test;
chnl_generator gen[3];
chnl_agent agents[3];
mcdt_monitor mcdt_mon;
chnl_checker chker;
protected string name;
event gen_stop_e;
function new(string name = "chnl_root_test");
this.name = name;
this.chker = new();
foreach(agents[i]) begin
this.agents[i] = new($sformatf("chnl_agent%0d",i));
this.gen[i] = new();
this.agents[i].init.req_mb = this.gen[i].req_mb;
this.agents[i].init.rsp_mb = this.gen[i].rsp_mb;
this.agents[i].mon.mon_mb = this.chker.in_mbs[i];
end
this.mcdt_mon = new();
this.mcdt_mon.mon_mb = this.chker.out_mb;
$display("%s instantiated and connected objects", this.name);
endfunction
为什么约束值都是-1,-1可以使得随机化的初始条件不成立,实现随机化中递进的层层控制。local::ch_id指类chnl_generator的变量,而ch_id指当前req的变量,local::只在randomize里出现。
class chnl_generator;
rand int pkt_id = -1;
rand int ch_id = -1;
rand int data_nidles = -1;
rand int pkt_nidles = -1;
rand int data_size = -1;
rand int ntrans = 10;
task send_trans();
chnl_trans req, rsp;
req = new();
assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id;
local::pkt_id >= 0 -> pkt_id == local::pkt_id;
local::data_nidles >= 0 -> data_nidles == local::data_nidles;
local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
local::data_size >0 -> data.size() == local::data_size;
})
else $fatal("[RNDFAIL] channel packet randomization failure!");
运行之前,首先配置我们的generate,然后运行agent,在运行generate,generate在运行之前的随机化发生在do_config里面
virtual task run();
$display($sformatf("*****************%s started********************", this.name));
this.do_config();
fork
agent[0].run();
agent[1].run();
agent[2].run();
join_none
fork
gen[0].run();
gen[1].run();
gen[2].run();
join
$display($sformatf("*****************%s finished********************", this.name));
// USER TODO 1.3
// Please move the $finish statement from the test run task to generator
// You woudl put it anywhere you like inside generator to stop test when
// all transactions have been transfered
$finish();
endtask