SV实验3的学习记录,MCDT部分的Testbench已逐渐完整,包含initiator、generator、agent、monitor、checker和test等组件。
随机约束
- 原package独立放置在chnl_pkg.sv文件
- 数据类chnl_trans声明了更多随机成员变量
class chnl_trans;
rand bit[31:0] data[];
rand int ch_id;
rand int pkt_id;
rand int data_nidles;
rand int pkt_nidles;
bit rsp; // default = 0,用于标明数据是否成功发送
local static int obj_id = 0;
constraint cstr{
soft 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 {[1:10]};
};
function new();
this.obj_id++;
endfunction
function chnl_trans clone(); // 深拷贝
chnl_trans c = new();
c.data = this.data;
c.ch_id = this.ch_id;
...
return c;
endfunction
function string sprint(); // 打印所有成员变量值
string s;
s = {s, $sformatf("obj_id = %0d: \n", this.obj_id)};
foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
...
return s;
endfunction
endclass: chnl_trans
-
采用"vsim -novopt -solvefaildebug -sv_seed random work.tb"仿真命令产生不同随机seed
-
对数据产生和测试控制的主动权交于generator
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;
mailbox #(chnl_trans) req_mb; // 声明参数化的mailbox
mailbox #(chnl_trans) rsp_mb;
constraint cstr{
soft ch_id == -1; // 若generator被随机则覆盖trans的随机,由send_trans()实现
soft pkt_id == -1;
soft data_size == -1;
soft data_nidles == -1;
soft pkt_nidles == -1;
soft ntrans == 10;
}
function new();
this.req_mb = new(); // 例化mailbox
this.rsp_mb = new();
endfunction
task run();
repeat(ntrans) send_trans();
run_stop_flags.put();
endtask
// generate transaction and put into local mailbox
task send_trans();
chnl_trans req, rsp;
req = new();
// 对trans数据做随机化,并添加了一些外部约束,"->"操作符类似"case"
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!");
this.pkt_id++; // 每发送一次trans加1
$display(req.sprint());
this.req_mb.put(req); // mailbox将数据传递给initiator
this.rsp_mb.get(rsp);
$display(rsp.sprint());
assert(rsp.rsp) // 断言检查数据是否成功发送
else $error("[RSPERR] %0t error response received!", $time);
endtask
function string sprint();
...
function void post_randomize();
string s;
s = {"AFTER RANDOMIZATION \n", this.sprint()};
$display(s);
endfunction
endclass: chnl_generator
- generator和initiator间的数据传递
class chnl_initiator;
local string name;
local virtual chnl_intf intf;
mailbox #(chnl_trans) req_mb; // initiator内不用再例化mailbox,仅声明悬空句柄
mailbox #(chnl_trans) rsp_mb;
function new(string name = "chnl_initiator");
this.name = name;
endfunction
function void set_interface(virtual chnl_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
task run();
this.drive();
endtask
task drive();
chnl_trans req, rsp;
@(posedge intf.rstn);
forever begin // 永动
this.req_mb.get(req);
this.chnl_write(req);
rsp = req.clone(); // 拷贝生成新对象
rsp.rsp = 1; // 标注trans已成功发送
this.rsp_mb.put(rsp);
end
endtask
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];
@(negedge intf.clk);
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
task chnl_idle();
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 0;
intf.drv_ck.ch_data <= 0;
endtask
endclass: chnl_initiator
更加灵活的测试控制
- 随机化可分层,test层通过do_config() 对generator做随机化,并进一步随机化chanl_trans对象

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();
// Connect the mailboxes handles of gen[i] and agents[i].init
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
virtual task gen_stop_callback();
// empty
endtask
virtual task run_stop_callback();
$display("run_stop_callback enterred");
// by default, run would be finished once generators raised 'finish'
// flags
$display("%s: wait for all generators have generated and tranferred transcations", this.name);
run_stop_flags.get(3);
$display($sformatf("*****************%s finished********************", this.name));
$finish();
endtask
virtual task run();
$display($sformatf("*****************%s started********************", this.name));
this.do_config();
fork
agents[0].run();
agents[1].run();
agents[2].run();
mcdt_mon.run();
chker.run();
join_none // agent的run()无法结束,故采用fork-join_none
// run first the callback thread to conditionally disable gen_threads
fork
this.gen_stop_callback();
@(this.gen_stop_e) disable gen_threads;
join_none
fork : gen_threads
gen[0].run();
gen[1].run();
gen[2].run();
join // 此处为等待所有generator发送完激励,故采用fork-join
run_stop_callback(); // wait until run stop control task finished
endtask
virtual function void set_interface(virtual chnl_intf ch0_vif
,virtual chnl_intf ch1_vif
,virtual chnl_intf ch2_vif
,virtual mcdt_intf mcdt_vif
);
agents[0].set_interface(ch0_vif);
agents[1].set_interface(ch1_vif);
agents[2].set_interface(ch2_vif);
mcdt_mon.set_interface(mcdt_vif);
endfunction
virtual function void do_config();
endfunction
endclass
class chnl_basic_test extends chnl_root_test;
function new(string name = "chnl_basic_test");
super.new(name);
endfunction
virtual function void do_config();
super.do_config();
//对generator随机化
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==16;})
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!");
endfunction
endclass: chnl_basic_test
- 测试选择可由仿真时添加命令"+TESTNAME=testname"控制

chnl_root_test tests[string]; // 句柄关联数组
...
// 每次只跑一个测试
initial begin
basic_test = new();
burst_test = new();
fifo_full_test = new();
tests["chnl_basic_test"] = basic_test; //index为类型
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
测试平台的结构
- 加入monitor和checker组件;checker置于test层中,而不是agent中

typedef struct packed { // 定义一个监测到数据的结构体
bit[31:0] data;
bit[1:0] id;
} mon_data_t;
class chnl_monitor;
local string name;
local virtual chnl_intf intf;
mailbox #(mon_data_t) mon_mb; // 悬空句柄
function new(string name="chnl_monitor");
this.name = name;
endfunction
function void set_interface(virtual chnl_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
task run();
this.mon_trans();
endtask
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));
// 将数据放入mon_mb并使用$display()打印monitor名称数据值
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
class mcdt_monitor;
...
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"}); // 例化monitor
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(); // 注意initiator的run()永远无法结束
mon.run();
join
endtask
endclass: chnl_agent
class chnl_checker;
local string name;
local int error_count; // 每报错一次加1
local int cmp_count; // 每比较一次加1
mailbox #(mon_data_t) in_mbs[3];
mailbox #(mon_data_t) out_mb;
function new(string name="chnl_checker");
this.name = name;
foreach(this.in_mbs[i]) this.in_mbs[i] = new();
this.out_mb = new();
this.error_count = 0;
this.cmp_count = 0;
endfunction
task run();
this.do_compare();
endtask
task do_compare();
mon_data_t im, om;
forever begin
// 先从out_mb拿数据,再与对应id的in_mb比较
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
博客介绍了测试平台相关内容。在随机约束方面,原package独立放置,数据类声明更多随机成员变量,用特定仿真命令产生不同随机seed,将数据产生和测试控制主动权交generator。测试控制更灵活,随机化可分层,测试选择可由仿真命令控制。测试平台结构加入monitor和checker组件。

9468

被折叠的 条评论
为什么被折叠?



