该学习笔记基于路科SV的实验3,MCDT的testbench的搭建。
搭建TB的一个核心:
1)验证结构的层次(组件的例化new());
2) 在run()之前,完成接口的连接(set_interface)、组件的连接;
3)环境运行run().
MCDT为MCDF的简化版,此时只有channle和arbiter,没有register和formatter。
TB1实验:
放大看generator与initiator之间用mailbox传递数据的结构简图如下
generator组件产生数据后,利用mailbox传递给initiator组件,之后initiator通过interface接口将数据传递给DUT中的channel slave。
从实验3开始,将chnl_pkg.sv与tb.sv文件分开。(chnl_pkg.sv中存放的是各种class的集合,tb.sv中完成interface的定义,接口的例化和各个测试用例的例化和运行)。
在chnl_pkg中:
1.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;
local static int obj_id = 0;//obj_id初始化。
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 {[1:10]};
};
function new();
this.obj_id++;//看别人讨论说,new的时候可以放一些想在初始化的时候设置的内容
endfunction
function chnl_trans clone();//数据的克隆
chnl_trans c = new();
c.data = this.data;
c.ch_id = this.ch_id;
c.pkt_id = this.pkt_id;
c.data_nidles = this.data_nidles;
c.pkt_nidles = this.pkt_nidles;
c.rsp = this.rsp;
return c;
endfunction
function string sprint();//数据的打印
string s;
s = {s, $sformatf("=======================================\n")};
s = {s, $sformatf("chnl_trans object content is as below: \n")};
s = {s, $sformatf("obj_id = %0d: \n", this.obj_id)};
foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
s = {s, $sformatf("ch_id = %0d: \n", this.ch_id)};
s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
s = {s, $sformatf("data_nidles = %0d: \n", this.data_nidles)};
s = {s, $sformatf("pkt_nidles = %0d: \n", this.pkt_nidles)};
s = {s, $sformatf("rsp = %0d: \n", this.rsp)};
s = {s, $sformatf("=======================================\n")};
return s;
endfunction
endclass: chnl_trans
2.chnl_generator产生数据的类的定义:(完成mailbox的例化,以及sent_trans的任务)
class chnl_generator;
int pkt_id;
int ch_id;
int ntrans;//决定传输多少个数据。
int data_nidles;
mailbox #(chnl_trans) req_mb;//声明req_mb(mailbox)传输的参数是chnl_trans.
mailbox #(chnl_trans) rsp_mb;//声明rsp_mb(mailbox)传输的参数是chnl_trans.
function new(int ch_id, int ntrans);
this.ch_id = ch_id;
this.pkt_id = 0;
this.ntrans = ntrans;
this.req_mb = new();//mailbox做例化
this.rsp_mb = new();//mailbox做例化
endfunction
task run();//使得generation可以运行起来。
repeat(ntrans) send_trans();
endtask
// generate transaction and put into local mailbox
task send_trans();//关键任务
chnl_trans req, rsp;
req = new();//数据类的例化
assert(req.randomize with {ch_id == local::ch_id; pkt_id == local::pkt_id;})//随机化
else $fatal("[RNDFAIL] channel packet randomization failure!");//随机化
this.pkt_id++;
$display(req.sprint());
this.req_mb.put(req);//使用mailbox传输trans
this.rsp_mb.get(rsp);//使用mailbox传输trans
$display(rsp.sprint());
assert(rsp.rsp)//断言,在req中rsp=0,在rsp中rsp=1,其余数据rsp与req相同
else $error("[RSPERR] %0t error response received!", $time);
endtask
endclass: chnl_generator
3.chnl_initiator类的定义:(完成mailbox的握手,以及将数据通过interface传输给DUT)
class chnl_initiator;
local string name;
local virtual chnl_intf intf;//class中的接口需要加virtual.
mailbox #(chnl_trans) req_mb;//定义参数为chnl_trans的req_mb的mailbox.
mailbox #(chnl_trans) rsp_mb;//定义参数为chnl_trans的rsp_mb的mailbox.
function new(string name = "chnl_initiator");
this.name = name;
endfunction
function void set_name(string s);
this.name = s;
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();//task可以嵌套别的task.
endtask
task drive();
chnl_trans req, rsp;
@(posedge intf.rstn);
forever begin
this.req_mb.get(req);//init从mailbox中获取数据;
this.chnl_write(req);//init将数据再发送给DUT中的chnnal slave.
rsp = req.clone();
rsp.rsp = 1;
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];
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
4.chnl_agent类中包含generator和initiator的例化:(mailbox句柄的连接,以及generator和initiator的run)
class chnl_agent;
chnl_generator gen;
chnl_initiator init;
local virtual chnl_intf vif;
function new(string name = "chnl_agent", int id = 0, int ntrans = 1);
this.gen = new(id, ntrans);
this.init = new(name);
endfunction
function void set_interface(virtual chnl_intf vif);
this.vif = vif;
init.set_interface(vif);
endfunction
task run();
this.init.req_mb = this.gen.req_mb;//把gen里面不悬空的句柄赋值给init里面悬空(即没有例化)的句柄。
this.init.rsp_mb = this.gen.rsp_mb;//把gen里面不悬空的句柄赋值给init里面悬空(即没有例化)的句柄。
fork
gen.run();
init.run();
join_any
endtask
endclass: chnl_agent
总结:
**class :类名
数据类型名称
mailbox 名字
function new()
设置(this……)
mailbox的实例化
endfunction
task run()
repeat(ntrans) send_trans();//任务嵌套了别的任务
endtask
task send_trans();//被嵌套的任务
chnl_trans req, rsp;
req = new();
this.req_mb.put(req);//使用mailbox传输trans。
this.rsp_mb.get(rsp);//使用mailbox传输trans。
endtask
endclass: 类名**
TB2实验:
为了方便对gen随机化,将TB1中的gen从agent中提出来,单独放在test层次中,再利用mailbox进行gen和agent中的init数据通信。
TB3实验:
在实验2的基础上,增加了checker和monitor模块,结构如上图所示。
channel monitor监控的是channel initiator与slave之间interface中的数据,并且channel monitor所得数据会送给mcdt checker;而mcdt monitor监控的则是arbiter的输出数据。
在channel monitor中很关键的一个task是:
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
mon_mb.put(m);//将mon接收到的数据通过mailbox传出
$display("%0t %s monitored channle data %8x", $time, this.name, m.data);
end
endtask
在mcdt monitor中很关键的一个task是:
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
在mcdt checker中:
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.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