知识点:
TLM定义:TLM是事务级建模的缩写,所谓transaction level是相对于DUT中各个模块之间信号线级别的通信来说的,就是把某一个特定功能的一组信息封装成一个类。端口类型可以分为三种:port、export和imp。
单项通信就是从initiator到target之间的数据流向是单一的,多向通信指的是initiator与target之间相同的TLM端口数目超过一个的时候的解决办法。
多向通信的宏命名方式:
通信管道:需要在两个组件中例化端口,然后同时在target中实现相应的传输方法,uvm_tlm_fifo类是一个新的组件,继承于uvm_component,已经预先定义了多个端口实现了多个对应方法可以使用。uvm_tlm_fifo功能类似mailbox
1、TLIM单向通信和多项通信:
1.1 在monitor中的用来与checker中的邮箱通信的mon_mb句柄替换为对应的uvm_blocking_put_port类型。
class chnl_monitor extends uvm_monitor;
local virtual chnl_intf intf;
uvm_blocking_put_port #(mon_data_t) mon_bp_port;
`uvm_component_utils(chnl_monitor)
function new(string name="chnl_monitor", uvm_component parent);
super.new(name, parent);
mon_bp_port = new("mon_bp_port", this);
endfunction
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_bp_port.put(m);
`uvm_info(get_type_name(), $sformatf("monitored channel data 'h%8x", m.data), UVM_HIGH)
end
endtask
endclass: chnl_monitor
以前在SV里面是通过mailbox的句柄进行传递的,mailbox #(mon_data_t) mon_mb; 在uvm里面通过替换为对应的uvm_blocking_put_port 类型,function new里面进行例化,只能采用new()的形式,因为是object组件;以前是通过mailbox的句柄往里面put传输数据,现在是通过端口往里面put,所以把句柄更改为端口名称就可以了。无论是chnl_pkg还是fmt_pkg、reg_pkg都是一样的,都是在monitor里面传递的。
1.2 声明import的端口类型,并且完成例化:
`uvm_blocking_put_imp_decl(_chnl0)
`uvm_blocking_put_imp_decl(_chnl1)
`uvm_blocking_put_imp_decl(_chnl2)
`uvm_blocking_put_imp_decl(_fmt)
`uvm_blocking_put_imp_decl(_reg)
`uvm_blocking_get_peek_imp_decl(_chnl0)
`uvm_blocking_get_peek_imp_decl(_chnl1)
`uvm_blocking_get_peek_imp_decl(_chnl2)
`uvm_blocking_get_imp_decl(_reg)
uvm_blocking_put_imp_chnl0 #(mon_data_t, mcdf_checker) chnl0_bp_imp;
uvm_blocking_put_imp_chnl1 #(mon_data_t, mcdf_checker) chnl1_bp_imp;
uvm_blocking_put_imp_chnl2 #(mon_data_t, mcdf_checker) chnl2_bp_imp;
uvm_blocking_put_imp_fmt #(fmt_trans , mcdf_checker) fmt_bp_imp ;
uvm_blocking_put_imp_reg #(reg_trans , mcdf_checker) reg_bp_imp ;
uvm_blocking_get_peek_imp_chnl0 #(mon_data_t, mcdf_checker) chnl0_bgpk_imp;
uvm_blocking_get_peek_imp_chnl1 #(mon_data_t, mcdf_checker) chnl1_bgpk_imp;
uvm_blocking_get_peek_imp_chnl2 #(mon_data_t, mcdf_checker) chnl2_bgpk_imp;
uvm_blocking_get_imp_reg #(reg_trans , mcdf_checker) reg_bg_imp ;
chnl0_bp_imp = new("chnl0_bp_imp", this);
chnl1_bp_imp = new("chnl1_bp_imp", this);
chnl2_bp_imp = new("chnl2_bp_imp", this);
fmt_bp_imp = new("fmt_bp_imp", this);
reg_bp_imp = new("reg_bp_imp", this);
chnl0_bgpk_imp = new("chnl0_bgpk_imp", this);
chnl1_bgpk_imp = new("chnl1_bgpk_imp", this);
chnl2_bgpk_imp = new("chnl2_bgpk_imp", this);
reg_bg_imp = new("reg_bg_imp", this);
因为checker里面有多个相同类型的import,所以是多向的通信,为了避免方法名的冲突,所以要占用宏的声明,
利用uvm_blocking_put_imp_decl(SFX)来声明导入的import端口,左边部分的五个import端口,还有邮箱右边的的get和get/peek类型的import,利用宏已经声明了端口的类型,然后用new例化
1.3 根据声明的 import 端口类型,请分别实现其对应的方法:
实现五种不同名的put,以及不同的get()和get/peek()的任务,目的还是把以前通过mailbox句柄的连接改为端口的连接。
task put_chnl0(mon_data_t t);
chnl_mbs[0].put(t);
endtask
task put_chnl1(mon_data_t t);
chnl_mbs[1].put(t);
endtask
task put_chnl2(mon_data_t t);
chnl_mbs[2].put(t);
endtask
task put_fmt(fmt_trans t);
fmt_mb.put(t);
endtask
task put_reg(reg_trans t);
reg_mb.put(t);
endtask
task peek_chnl0(output mon_data_t t);
chnl_mbs[0].peek(t);
endtask
task peek_chnl1(output mon_data_t t);
chnl_mbs[1].peek(t);
endtask
task peek_chnl2(output mon_data_t t);
chnl_mbs[2].peek(t);
endtask
task get_chnl0(output mon_data_t t);
chnl_mbs[0].get(t);
endtask
task get_chnl1(output mon_data_t t);
chnl_mbs[1].get(t);
endtask
task get_chnl2(output mon_data_t t);
chnl_mbs[2].get(t);
endtask
task get_reg(output reg_trans t);
reg_mb.get(t);
endtask
1.4在mcdf_refmod中声明用来和mcdf_checker中连接的import,完成例化。把原来邮箱的句柄调用的方法改为TLM端口呼叫方式的方法:
uvm_blocking_get_port #(reg_trans) reg_bg_port;
uvm_blocking_get_peek_port #(mon_data_t) in_bgpk_ports[3];
/********************************************************************/
reg_bg_port = new("reg_bg_port", this);
/********************************************************************/
this.reg_bg_port.get(t);
1.5在mcdf_env的connect_phase部分完成mcdf_refmod的TLM port和mcdf)checker的TLM import连接:把各个发起端(chnl_agent)里面的agent中端口和checker里面的端口进行连接,可以理解为图中左边五个put端口的连接。
chnl_agts[0].monitor.mon_bp_port.connect(chker.chnl0_bp_imp);
chnl_agts[1].monitor.mon_bp_port.connect(chker.chnl1_bp_imp);
chnl_agts[2].monitor.mon_bp_port.connect(chker.chnl2_bp_imp);
reg_agt.monitor.mon_bp_port.connect(chker.reg_bp_imp);
fmt_agt.monitor.mon_bp_port.connect(chker.fmt_bp_imp);
1.6 在mcdf_checker的connect_phase部分完成mcdf_refmod的TLM port和mcdf_checker的TLM import连接:可以理解为中间部分的连接,方向是从reference_model向checker。
refmod.in_bgpk_ports[0].connect(chnl0_bgpk_imp);
refmod.in_bgpk_ports[1].connect(chnl1_bgpk_imp);
refmod.in_bgpk_ports[2].connect(chnl2_bgpk_imp);
2、TLM通信管道:
TLM通信的优点:通信函数可以定制化put()/get()/peek()的内容和参数,这其 实比 mailbox 的通信更加灵活;将组件实现了完全的隔离,就可以很好地避免直接将不同层次的数据缓存对象的句柄进行 “空中传递”.而 TLM 端口按照层次的连接,可以使得组件之间保持很好的独立性。
2.1将原本在 mcdf_refmod 中的out_mb 替换为 uvm_tlm_fifo 类型,并且完成例化,以及对应的变量名替换。uvm_tlm_fifo 既有缓存的功能,也有import的功能,uvm_tlm_fifo 不需要在例化了。
uvm_tlm_fifo #(fmt_trans) out_tlm_fifos[3];
foreach(out_tlm_fifos[i]) out_tlm_fifos[i] = new($sformatf("out_tlm_fifos[%0d]", i), this);
this.out_tlm_fifos[id].put(ot);
2.2 将原本在mcdf_checker中的exp_mbs[3]的邮箱句柄数组,替换为 uvm_blocking_get_port 类型句柄数组,并且做相应的例化以及变量名替换.也就是图中最右边的三个进行替换。
uvm_blocking_get_port #(fmt_trans) exp_bg_ports[3];
foreach(exp_bg_ports[i]) exp_bg_ports[i] = new($sformatf("exp_bg_ports[%0d]", i), this);
this.exp_bg_ports[mont.ch_id].get(expt);
2.3 在mcdf_checker中,完成在mcdf_checker中的TLM端口端口到mcdf_refmod中的uvm_tlm_fifo自带的blocking_get_export端口的连接。
foreach(exp_bg_ports[i]) begin
exp_bg_ports[i].connect(refmod.out_tlm_fifos[i].blocking_get_export);
end
3、UVM回调类:
目的:将原有的mcdf_data_consistence_basic_test 和 mcdf_full_random_test 的类实现方式 ( 即类继承方式 ) 修改为回调函数的实现方式。
3.1 在路桑给的uvm_callback类中,预先定义需要的几个虚方法:首先是设置了一个call_back的类--cb_mcdf_base ,然后在类里面预定义一些空函数
virtual task cb_do_reg();
// User to define the content
endtask
virtual task cb_do_formatter();
// User to define the content
endtask
virtual task cb_do_data();
// User to define the content
endtask
3.2 使用 callback 对应的宏,完成目标 uvm_test 类型与目标 uvm_callback 类型的关联:首先是定义call_back,然后是绑定和插入,3.2便是绑定部分。
`uvm_register_cb(mcdf_base_test, cb_mcdf_base)
3.3 在uvm_test类型指定的方法中,完成 uvm_callback 的方法回调指定:3.3便是插入,mcdf_base_test和cb_mcdf_base是刚绑定的父类,cb_do_reg便是父类里面的方法。
// do register configuration
virtual task do_reg();
//TODO-3.3 Use callback macro to link the callback method
`uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_reg())
endtask
// do external formatter down stream slave configuration
virtual task do_formatter();
//TODO-3.3 Use callback macro to link the callback method
`uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_formatter())
endtask
// do data transition from 3 channel slaves
virtual task do_data();
//TODO-3.3 Use callback macro to link the callback method
`uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_data())
endtask
3.4 完成uvm_callback和对应测试类的定义:调整 mcdf_data_consistence_basic_test 到cb_mcdf_data_consistence_basic_test 嵌入内容的类型,继承do_reg()/do_formatter()/do_data()的方法到回调类,并在测试中链接回调。
mcdf_data_consistence_basic_test和cb_mcdf_data_consistence_basic_test在内容上是一致的,都是实现do_reg()、do_formatter()、do_data()这三个功能,只不过实现的方式不同,一个是继承,一个是call_back。就是把事先关联的call_back类,填充对应的方法,然后把当前的类型和call_back类关联就可以了。 需要注意的是数据仍然是mcdf_base_test的数据类型,这也是一开始关联导致的。
class cb_mcdf_data_consistence_basic extends cb_mcdf_base;
`uvm_object_utils(cb_mcdf_data_consistence_basic)
function new (string name = "cb_mcdf_data_consistence_basic");
super.new(name);
endfunction
task cb_do_reg();
bit[31:0] wr_val, rd_val;
super.cb_do_reg();
// slv0 with len=8, prio=0, en=1
wr_val = (1<<3)+(0<<1)+1;
test.write_reg(`SLV0_RW_ADDR, wr_val);
test.read_reg(`SLV0_RW_ADDR, rd_val);
void'(test.diff_value(wr_val, rd_val, "SLV0_WR_REG"));
// slv1 with len=16, prio=1, en=1
wr_val = (2<<3)+(1<<1)+1;
test.write_reg(`SLV1_RW_ADDR, wr_val);
test.read_reg(`SLV1_RW_ADDR, rd_val);
void'(test.diff_value(wr_val, rd_val, "SLV1_WR_REG"));
// slv2 with len=32, prio=2, en=1
wr_val = (3<<3)+(2<<1)+1;
test.write_reg(`SLV2_RW_ADDR, wr_val);
test.read_reg(`SLV2_RW_ADDR, rd_val);
void'(test.diff_value(wr_val, rd_val, "SLV2_WR_REG"));
// send IDLE command
test.idle_reg();
endtask
task cb_do_formatter();
super.cb_do_formatter();
void'(test.fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});
test.fmt_gen.start();
endtask
task cb_do_data();
super.cb_do_data();
void'(test.chnl_gens[0].randomize() with {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });
void'(test.chnl_gens[1].randomize() with {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;});
void'(test.chnl_gens[2].randomize() with {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;});
fork
test.chnl_gens[0].start();
test.chnl_gens[1].start();
test.chnl_gens[2].start();
join
#10us; // wait until all data haven been transfered through MCDF
endtask
endclass: cb_mcdf_data_consistence_basic
//TODO-3.4 define cb_mcdf_data_consistence_basic_test
class cb_mcdf_data_consistence_basic_test extends mcdf_base_test;
cb_mcdf_data_consistence_basic cb;
`uvm_component_utils(cb_mcdf_data_consistence_basic_test)
function new(string name = "cb_mcdf_data_consistence_basic_test", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
cb = cb_mcdf_data_consistence_basic::type_id::create("cb"); //添加的方法
uvm_callbacks#(mcdf_base_test)::add(this, cb);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
cb.test = this;
endfunction
endclass: cb_mcdf_data_consistence_basic_test
4、UVM仿真控制方法:
4.1 在mcdf_base_test类中添加新的phase函数end_of_elaboration_phase(),同时利用uvm_root类来将信息的冗余度设置为UVM_HIGH,以此来允许更多低级别的信息打印出来:end_of_elaboration_phase是在build_phase、connect_phase之后,run_phase之前,是在一切环境准备就绪的时候,运行之前再对环境做一些配置
4.2 请利用uvm_root::set_timeout()设置仿真的最大时间长度,同时由于此功能的生效,可以清除原有方法 do.watchdog()的定义和调用:以前是通过do_watchdog在run_phase运行来停止仿真,这里是通过set_timeout来停止仿真。
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_root::get().set_report_verbosity_level_hier(UVM_HIGH);
uvm_root::get().set_report_max_quit_count(1);
uvm_root::get().set_timeout(10ms);
endfunction