目录
3.1 对 run() 里的 do_reset() 进行解释
3.2 对 run() 里的 do_reg_updata() 进行解释
4. 对 mcdf_checker 中的 do_compare()进行代码解释
1. 对整个 MCDF_pkg.sv 的 结构的理解
mcdf_checker 包含了 reg_mb 实例 , chnl_mb 实例 , fmt_mb 实例 ,和 mcdf_refmod 参考模型,以及 do_compare()比较功能
其中, refmod 是用来模拟 硬件的行为的, 它包括
reg_mb 句柄 :
用来接收从reg_pkg中的 monitor 监测到的寄存器的信号(cmd,addr,data)
in_mbs[0] 句柄 in_mbs[1] 句柄 in_mbs[2] 句柄 :
用来接收 从chnl_pkg中的 monitor 监测到的 chnnel 的数据 (data)
out_mbs[0] 实例 out_mbs[1] 实例 out_mbs[2] 实例 :
用来对 in_mbs[i] 中的数据进行分别打包
以及 两个 do_reg_update() 和 do_packet():
对监测到的数据和信号 进行寄存器的内容的更新和 对 chnnel 的数据打包成 fmt_trans 形式的数据包, 并输出到 out_mbs[i] 中
refmod 模拟了硬件的行为,数据传输 和 数据打包 和 寄存器更新; 没有模拟arbiter 的仲裁 和 数据的优先级,以后可加入这些模拟。
remod 完成了它的功能了之后,在 do_cmpare() 里 与 从fmt 的 monitor 检测到的数据包进行比较。其中 fmt 最终形成了一个数据包, 而在 refmod 中形成了3个数据包,怎么比较呢? 通过得知 fmt 数据包的 id, 然后把那个 id 的 在 refmod 中 一个拿出来进行比较。
reg_mb chnl_mb fmt_mb 与 各自 agent 中的 monitor 中的 mon_mb 句柄的连接 ,在更大的 mcdf_env 类中实现
先实例化,再连接句柄
句柄与实例的连接,要在它们所在的公共层
比如 mcdf_checker 中的 reg_mb 实例 和 monitor 中的 mon_mb 句柄 就要在它们的公共顶层 mcdf_env 中 连接:this.reg_agt.monitor. mon_mb = this.chker.reg_mb;
以及它里面的一层refmod 中的 reg_mb 句柄 就要在它们的公共层 mcdf_checker 中 连接:
this.refmod.reg_mb = this.reg_mb;
其他的情况是一样的,只有refmod 中的 三个out_mbs[i] 实例, 在mcdf_checker 中也声明了三个句柄,指向这三个实例,exp_mbs[i]
并在 mcdf_checker 中连接:
this.exp_mb[i] = this.refmod.out_mbs[i];
为了之后挑选出一个与 fmt_mbs 进行比较(比较的不是句柄 而是句柄指向的mailbox 中存放的数据)
2. mcdf_reg_t
在定义 mcdf_refmod 之前 , 先定义了 refmod 要用到的 为了模拟硬件的寄存器的
结构体 mcdf_reg_t 如下图所示
3. 对 mcdf_refmod 的理解
mcdf_refmod() 用来 模拟硬件的功能的两个任务:do_reg_update() 更新寄存器的内容和 do_packet() 对chnl 的 数据进行打包 ,完成的对应的硬件功能如下图示意
3.1 对 run() 里的 do_reset() 进行解释
3.2 对 run() 里的 do_reg_updata() 进行解释
从monitor 检测到的寄存器的数据内容,进而对我们的参考模型里的寄存器进行实时更新,
对读写寄存器 没有必要更新读操作 因为你读到的数 就是你写进去的数 没有意义
3.3 对run() 里的do_packet()进行解释
两种写法: 其中 wait(this.in_mbs[id].num() >0) 和 this.in_mbs[id].peek(it)
功能是一样的, 目的就是在打包之前 确保 in_mbs[id] 不为空, wait 会一直等待,peek()是复制mailbox的内容,为空的话就阻塞。
class mcdf_refmod 完整代码如下:
class mcdf_refmod;
local virtual mcdf_intf intf;
local string name;
mcdf_reg_t regs[3]; // 只声明了三个寄存器,因为把 只读 和 读写 放在了一起
mailbox #(reg_trans) reg_mb;
mailbox #(mon_data_t) in_mbs[3];
mailbox #(fmt_trans) out_mbs[3];
function new(string name="mcdf_refmod");
this.name = name;
foreach(this.out_mbs[i]) this.out_mbs[i] = new();
endfunction
task run();
fork
do_reset();
this.do_reg_update();
do_packet(0);
do_packet(1);
do_packet(2);
join
endtask
task do_reg_update();
reg_trans t;
forever begin
this.reg_mb.get(t);
if(t.addr[7:4] == 0 && t.cmd == `WRITE) begin
this.regs[t.addr[3:2]].en = t.data[0];
this.regs[t.addr[3:2]].prio = t.data[2:1];
this.regs[t.addr[3:2]].len = t.data[5:3];
end
else if(t.addr[7:4] == 1 && t.cmd == `READ) begin
this.regs[t.addr[3:2]].avail = t.data[7:0];
end
end
endtask
task do_packet(int id); // id = 0,1,2
fmt_trans ot;
mon_data_t it;
bit[2:0] len;
forever begin
this.in_mbs[id].peek(it); //
ot = new();
len = this.get_field_value(id, RW_LEN); // 返回对应寄存器的对应的内容 也称域
ot.length = len > 3 ? 32 : 4 << len; // 对应的就是 len = 0 length = 4; len = 1 length = 8; len = 2 length = 16; len = 3 length = 32;
ot.data = new[ot.length]; // 4左移0位是4 4左移1位是8 4左移2位是16 4左移3位是32
ot.ch_id = id;
foreach(ot.data[m]) begin
this.in_mbs[id].get(it); //
ot.data[m] = it.data;
end
this.out_mbs[id].put(ot);
end
endtask
function int get_field_value(int id, mcdf_field_t f);
case(f)
RW_LEN: return regs[id].len;
RW_PRIO: return regs[id].prio;
RW_EN: return regs[id].en;
RD_AVAIL: return regs[id].avail;
endcase
endfunction
task do_reset();
forever begin
@(negedge intf.rstn);
foreach(regs[i]) begin
regs[i].len = 'h0;
regs[i].prio = 'h3;
regs[i].en = 'h1;
regs[i].avail = 'h20;
end
end
endtask
function void set_interface(virtual mcdf_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
endclass
4. 对 mcdf_checker 中的 do_compare()进行代码解释
class mcdf_checker 完整代码如下:
class mcdf_checker;
local string name;
local int err_count;
local int total_count;
local int chnl_count[3];
local virtual mcdf_intf intf;
local mcdf_refmod refmod;
mailbox #(mon_data_t) chnl_mbs[3];
mailbox #(fmt_trans) fmt_mb;
mailbox #(reg_trans) reg_mb;
mailbox #(fmt_trans) exp_mbs[3];
function new(string name="mcdf_checker");
this.name = name;
foreach(this.chnl_mbs[i]) this.chnl_mbs[i] = new();
this.fmt_mb = new();
this.reg_mb = new();
this.refmod = new();
foreach(this.refmod.in_mbs[i]) begin
this.refmod.in_mbs[i] = this.chnl_mbs[i]; // 把 checker 中实例化过的 句柄 chnl_mbs 赋值给 refmod 中的 in_mbs
this.exp_mbs[i] = this.refmod.out_mbs[i];
end
this.refmod.reg_mb = this.reg_mb;
this.err_count = 0;
this.total_count = 0;
foreach(this.chnl_count[i]) this.chnl_count[i] = 0;
endfunction
function void set_interface(virtual mcdf_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
this.refmod.set_interface(intf);
endfunction
task run();
fork
this.do_compare();
this.refmod.run();
join
endtask
task do_compare();
fmt_trans expt, mont;
bit cmp;
forever begin
this.fmt_mb.get(mont);
this.exp_mbs[mont.ch_id].get(expt);
cmp = mont.compare(expt);
this.total_count++;
this.chnl_count[mont.ch_id]++;
if(cmp == 0) begin
this.err_count++;
rpt_pkg::rpt_msg("[CMPFAIL]",
$sformatf("%0t %0dth times comparing but failed! MCDF monitored output packet is different with reference model output", $time, this.total_count),
rpt_pkg::ERROR,
rpt_pkg::TOP,
rpt_pkg::LOG);
end
else begin
rpt_pkg::rpt_msg("[CMPSUCD]",
$sformatf("%0t %0dth times comparing and succeeded! MCDF monitored output packet is the same with reference model output", $time, this.total_count),
rpt_pkg::INFO,
rpt_pkg::HIGH);
end
end
endtask
5. 对 mcdf_env 的理解
1)声明chnl_agent , reg_agent, fmt_agent, 以及 mcdf_checker 的句柄,并在构造函数中将其实例化,
2)在实例化后,将 monitor 中的 mon_mb 这个mailbox 句柄 赋值给 checker 中对应的句柄,使其指向监测到的对象
3)先调用进行配置的do_config(), 然后调用各个组件的 run(),使其运行
4)并且把 do_config() 和 run() 都声明为 虚函数virtual, 为实现动态绑定,do_config() 的内容也为空,在具体的 test 中,在进行配置
5)do_report() 也就是调用 checker 中的 do_report(), 进行比较之后的打印
class mcdf_env;
chnl_agent chnl_agts[3];
reg_agent reg_agt;
fmt_agent fmt_agt;
mcdf_checker chker;
protected string name;
function new(string name = "mcdf_env");
this.name = name;
this.chker = new();
foreach(chnl_agts[i]) begin
this.chnl_agts[i] = new($sformatf("chnl_agts[%0d]",i));
this.chnl_agts[i].monitor.mon_mb = this.chker.chnl_mbs[i];
end
this.reg_agt = new("reg_agt");
this.reg_agt.monitor.mon_mb = this.chker.reg_mb;
this.fmt_agt = new("fmt_agt");
this.fmt_agt.monitor.mon_mb = this.chker.fmt_mb;
$display("%s instantiated and connected objects", this.name);
endfunction
virtual task run();
$display($sformatf("*****************%s started********************", this.name));
this.do_config(); // 这个 do_config 在 mcdf_base_test 中 再配置 generator
fork
this.chnl_agts[0].run();
this.chnl_agts[1].run();
this.chnl_agts[2].run();
this.reg_agt.run();
this.fmt_agt.run();
this.chker.run();
join
endtask
// virtual function void do_config();
// endfunction
virtual function void do_report();
this.chker.do_report();
endfunction
endclass
6. 对mcdf_base_test 的理解
class mcdf_base_test; // 从顶层开始 逐渐往下例化 从外往里 例化 base_test 例化 generator 和 mcdf_env
chnl_generator chnl_gens[3]; // mcdf_env 例化了 agent 和 checker
reg_generator reg_gen; // agent 例化了 driver 和 monitor checker 例化了 refmod
fmt_generator fmt_gen;
mcdf_env env;
protected string name;
function new(string name = "mcdf_base_test");
this.name = name;
this.env = new("env");
foreach(this.chnl_gens[i]) begin
this.chnl_gens[i] = new();
this.env.chnl_agts[i].driver.req_mb = this.chnl_gens[i].req_mb;
this.env.chnl_agts[i].driver.rsp_mb = this.chnl_gens[i].rsp_mb;
end
this.reg_gen = new();
this.env.reg_agt.driver.req_mb = this.reg_gen.req_mb;
this.env.reg_agt.driver.rsp_mb = this.reg_gen.rsp_mb;
this.fmt_gen = new();
this.env.fmt_agt.driver.req_mb = this.fmt_gen.req_mb;
this.env.fmt_agt.driver.rsp_mb = this.fmt_gen.rsp_mb;
rpt_pkg::logname = {this.name, "_check.log"};
rpt_pkg::clean_log();
$display("%s instantiated and connected objects", this.name);
endfunction
virtual task run();
fork
env.run();
join_none
rpt_pkg::rpt_msg("[TEST]",
$sformatf("=====================%s AT TIME %0t STARTED=====================", this.name, $time),
rpt_pkg::INFO,
rpt_pkg::HIGH);
this.do_reg();
this.do_formatter();
this.do_data(); // 这三个 顺序不能变 因为要在 发送数据之前先配置好寄存器,然后配置好接收来自fmt的数据 的下行通道 fmt_agent, 最后发送数据
rpt_pkg::rpt_msg("[TEST]",
$sformatf("=====================%s AT TIME %0t FINISHED=====================", this.name, $time),
rpt_pkg::INFO,
rpt_pkg::HIGH);
this.do_report();
$finish();
endtask
// do register configuration
virtual task do_reg();
endtask
// do external formatter down stream slave configuration
virtual task do_formatter();
endtask
// do data transition from 3 channel slaves
virtual task do_data();
endtask
// do simulation summary
virtual function void do_report();
this.env.do_report();
rpt_pkg::do_report();
endfunction
virtual function void set_interface(virtual chnl_intf ch0_vif
,virtual chnl_intf ch1_vif
,virtual chnl_intf ch2_vif
,virtual reg_intf reg_vif
,virtual fmt_intf fmt_vif
,virtual mcdf_intf mcdf_vif
);
this.env.chnl_agts[0].set_interface(ch0_vif);
this.env.chnl_agts[1].set_interface(ch1_vif);
this.env.chnl_agts[2].set_interface(ch2_vif);
this.env.reg_agt.set_interface(reg_vif);
this.env.fmt_agt.set_interface(fmt_vif);
this.env.chker.set_interface(mcdf_vif);
endfunction
virtual function bit diff_value(int val1, int val2, string id = "value_compare");
if(val1 != val2) begin
rpt_pkg::rpt_msg("[CMPERR]",
$sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2),
rpt_pkg::ERROR,
rpt_pkg::TOP);
return 0;
end
else begin
rpt_pkg::rpt_msg("[CMPSUC]",
$sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2),
rpt_pkg::INFO,
rpt_pkg::HIGH);
return 1;
end
endfunction
virtual task idle_reg();
void'(reg_gen.randomize() with {cmd == `IDLE; addr == 0; data == 0;});
reg_gen.start();
endtask
virtual task write_reg(bit[7:0] addr, bit[31:0] data);
void'(reg_gen.randomize() with {cmd == `WRITE; addr == local::addr; data == local::data;});
reg_gen.start();
endtask
virtual task read_reg(bit[7:0] addr, output bit[31:0] data);
void'(reg_gen.randomize() with {cmd == `READ; addr == local::addr;});
reg_gen.start();
data = reg_gen.data; // 这个 data 是怎么获得的? 同过 与 driver 的 握手通信 this.rsp_mb.get(rsp); 而且在generator 定义了 data
endtask
endclass