1.UVM put/get通信
在UVM中一对一的TLM有很多种类型,以sequencer和driver为例。
driver是请求端,在driver列化了两种端口(在uvm_driver中列化,可直接使用):
1.uvm_seq_item_pull_port #(REQ, ESP)seq_item_port;
2.uvm_analysis_port #(RSP) rep_port;
而sequencer是接收端,同样在其侧例化了两个对应的端口
1.uvm_seq_item_pull_imp #(REQ, ESP,this_type)seq_item_export;
2.uvm_analysis_export #(RSP) rsp_export;
但是driver的port和sequencer的export的连接需要由用户完成:
driver::seq_item_port.connect(sequencer::seq_item_export);
另外,在sequencer这边,uvm_sequencer提供了多种方法(,不止pu()/get()),供driver使用,在driver中只需通过seq_item_port.mthod()的方式直接使用。
2 uvm analysis port
除了在sequencer与driver之间的通信是一对一的,在monitor,checker,reference model,scorboard之间的通信往往是一对多,因此在这些组件之间通行一般使用uvm analysis port;analysis port采用observer pattern来实现;
举例如下:
背景是sub_env_master中的mst_monitor在interface上监测到transaction后,将其以observer p
class sub_env_matser extends uvm_env;
// variable define.
uvm_analysis_port#(dram_intf_trans) dram_data_ap; // 申明dram_data_ap
function void build_phase(uvm_phase phase);
super.build_phase(phase);
......
dram_data_ap = new("dram_data_ap", this); // 例化dram_data_ap
mst_monitor = sub_monitor_master::type_id::create("mst_monitor", this);
mst_checker = sub_checker_master::type_id::create("mst_checker", this);
mst_monitor.dram_data_ap = dram_data_ap; // 将例化的dram_data_ap的句柄赋值给mst_monitor
......
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
......
dram_data_ap.connect(mst_checker.dram_rdwr_data_imp); // 将dram_data_ap与mst_checker中的dram_rdwr_data_imp相连
......
endfunction
endclass
attern的形式发送给mst_checker,同时也发给sub_env_slave。
在sub_env_master中,需要做的事情是:
(1):申明dram_data_ap;
(2):在build_phase中例化dram_data_ap;
(3):由于是sub_env_master中的mst_monitor发送事务,而不是sub_env_master,因此需要将例化的dram_data_ap的句柄赋值给mst_monitor;
(4):在sub_env_master内部,需要mst_monitor需要将transaction传给mst_checker,因此需要将dram_data_ap与mst_checker中的dram_rdwr_data_imp相连;当然,在mst_checker中必定有function write_dram_rdwr_data(...)去接受transaction;
需要思考的是,明明是mst_monitor发送transaction,为什么要在sub_env_master中例化dram_data_ap,再赋值给mst_monitor?为什么不直接在mst_monitor中去例化dram_data_ap呢?
这是因为在top_env中,只会例化其下一层的组件,即sub_env_master和sub_env_slave;至于sub_env_master/sub_env_slave会不会例化其自己的agent/monitor/driver等组件,由sub_env_master/sub_env_slave自己决定,top_env并不知道;因此在top_env这层,只会将sub_env_master的ap和sub_env_slave的imp相连:sub_env_mst.dram_data_ap.connect(sub_env_slv.dram_wr_data_export),不会将sub_env_master的子组件与sub_env_slave相连。
因此作为transaction发送的起点,在mst_monitor的run_phase中,会通过dram_data_ap.write(m_trans)的方式将m_trans发送到与dram_data_ap相连接的imp端;
`uvm_analysis_imp_decl(_dram_wr_data) // 宏申明不同的imp方法
`uvm_analysis_imp_decl(_dram_rd_data)
class sub_env_slave extends uvm_env;
//variable define.
uvm_analysis_imp_dram_wr_data #(dram_intf_trans, sub_env_slave) dram_wr_data_export;
uvm_analysis_imp_dram_rd_data #(dram_intf_trans, sub_env_slave) dram_rd_data_export;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
......
dram_wr_data_export = new("dram_wr_data_export", this); // 申明并例化dram_wr_data_export
dram_rd_data_export = new("dram_rd_data_export", this); // 申明并例化dram_rd_data_export
......
endfunction
function void write_dram_wr_data(dram_intf_trans trans); // 用来接收transaction的imp方法的实现
// process write trans...
endfunction
function void write_dram_rd_data(dram_intf_trans trans); // 用来接收transaction的imp方法的实现
// process read trans...
endfunction
endclass
在sub_env_slave中,需要做的事情是:
(1):由于在sub_env_slave中需要实现多个write方法,因此需要用宏`uvm_analysis_imp_decl(...)区分出不同类型的imp,然后分别用这些类型的imp去声明export;至于为什么叫dram_wr_data_export而不是叫dram_wr_data_imp?是因为m_trans传到write_dram_wr_data这里可能还不是终点,也许dram_wr_data_export还会作为producer继续将m_trans发送到下一个组件中,因此将其命名成dram_wr_data_imp并不合理;当然,这个名字可以随意命名,无需与`uvm_analysis_imp_decl(...)中的申明保持一致;
(2):在build_phase中例化dram_wr_data_export和dram_rd_data_export,
(3):write_dram_wr_data()和write_dram_rd_data()方法的实现;
需要说明的是,一般不直接在sub_env_slave中实现imp方法,而是在sub_env_slave的子组件中实现,这里只是为了阐述方便在sub_env_slave中实现;如果要在sub_env_slave的子组件slv_checker中实现,需要做的是:在slv_checker中申明并创建dram_wr_data_imp和dram_rd_data_imp,并且在sub_env_slave的connect_phase中将dram_wr_data_export与dram_wr_data_imp相连,将dram_rd_data_export与dram_rd_data_imp相连,如下:
dram_wr_data_export.connect(slv_checker.dram_wr_data_imp);
dram_rd_data_export.connect(slv_checker.dram_rd_data_imp);
class top_env extends uvm_env;
......
function viod build_phase(uvm_phase phase);
......
sub_env_mst = sub_env_master::type_id::create("sub_env_mst", this);
sub_env_slv = sub_env_slave::type_id::create("sub_env_slv", this);
......
endfunction
function void connect_phase(uvm_phase phase);
......
sub_env_mst.dram_data_ap.connect(sub_env_slv.dram_wr_data_export);
sub_env_mst.dram_data_ap.connect(sub_env_slv.dram_rd_data_export);
......
endfunction
......
endclass
在top_env中,需要做的事情是:
将sub_env_mst的connect_phase中的dram_data_ap与sub_env_slv中的dram_wr_data_export和dram_rd_data_export相连接,如上所述,只连接sub_env_mst和sub_env_slv这一层次的analysis port,不要连接到sub_env_mst和sub_env_slv的子组件中去,如果连接的子组件没有创建对象,VCS会报No Object Access。