uvm中有很多使用到参数化类的地方,网上也有很多同行分享参数化的类的相关知识,但是都是只有部分内容,没有一个完整的vip展示。刚好工作中有遇到这样的场景:不同module的interface之间的protocol很类似但不完全一样,transaction也是几乎一致,只有个别field不同,如果各个module owner都各自开发一个interface vip,这样既浪费又容易遗漏,所以最好的办法实现一个参数化的vip,driver实现几乎90%+的common function,差异化的点通过config开关来分别实现 ,不同的transaction通过override basic的transaction来实现,现在简单分享一下其中的关键点,首先来看agent 部分。
class comm_agent `COMM_PARAM_DECL extends uvm_agent;
//`COMM_PARAM_DECL用来define transaction,方便不同的user 自己define
`uvm_component_param_utils(comm_agent `COMM_PARAM_INST)//注册也需要是参数化的
comm_config m_config;//config 文件,一些配置和开关
comm_sequencer `COMM_PARAM_INST m_sqr;
comm_master_driver `COMM_PARAM_INST m_mst_drv;
comm_salve_driver `COMM_PARAM_INST m_slv_drv;
comm_monitor `COMM_PARAM_INST m_mon;
function new(string name="comm_agent",uvm_component parent);
super.new(name,parent);
endfunction
function viod build_phase(uvm_Phase phase);
uvm_config_db#(comm_config)::get(this,"","m_config",m_config);
m_mon = comm_monitor `COMM_PARAM_INS::type_id::creat("m_mon",this);
if(m_config.m_mode == MASTER) begin
m_mst_drv = comm_master_driver `COMM_PARAM_INST::type_id::creat("m_dst_drv",this);
end
else begin
//slave driver creat
end
//sqr create
endfunction
function connect_phase(uvm_phase phase);
//tlm port connection
//config/vif etc connection
endfunction
endclass
comm_agent是参数化的class,参数用的define,为了方便阅读,注册的时候需要用`uvm_component_param_util宏来注册,注册时带上的参数需要跟定义一一对应。这里传入的参数其实就是后面driver需要用到的所有seq item,如果只有一个顶层的seq item,那就只需要一个参数。
//seq item包含两个channel,req和resp channel,选择传入三个class是为了driver里面的实现
`define COMM_PARAM_DECL #(type SEQ_ITEM = comm_seq_item,type REQ_ITEM = comm_req_seq_item,type RESP_ITEM= comm_resp_seq_item)
//跟agent的输入class一一对应
`define COMM_PARAM_INST #(SEQ_ITEM,REQ_ITEM,RESP_ITEM)
到这agent部分就完成了,选择从agent开始,是为了方便整体上看到需要哪些参数化的class已经定义使用方式,后续带来driver部分的大体实现。