本实验的目的是将generator重构成sequencer和sequence,其中,sequence是用来生成相应的sequence_item,sequencer用于发送sequence_item。
首先修改的是driver和generator之间的通信mailbox,我们不再使用mailbox进行transaction的传输。采用tlm端口进行,即在agent的connect_phase()方法中将agent的seq_item_port与sequencer的seq_item_export进行连接(由于chnl_package,reg_package以及fmt_package中generator是一致的,所以采用reg_package举例)。
driver.seq_item_port.connect(sequencer.seq_item_export);
这里之前报错,后来发现是因为参数化的类这个问题,我给driver声明的时候添加了#(reg_trans),但是sequener并没有添加#(reg_trans),这导致接口的类型不同,因此报错。
driver在run_phase()阶段调用get_next_item()方法获取sequencer上挂载的sequence产生的sequence_item。
seq_item_port.get_next_item(req);
driver在接收到sequence_item之后,对其进行克隆,调用核心基类的核心方法,产生的对象类型是uvm_object,需要通过cast将其转化为reg_trans这种sequence_item类型。之后通过调用item_done返回,在此之前,我们需要获得该Item所述的sequence的id,保证传输给相应的sequence。
rsp.set_sequence_id(req.get_sequence_id());
seq_item_port.item_done(rsp);
之后是对generator实现到sequence/sequencer的转化。首先是sequencer的声明,比较简单,就是利用`uvm_component_utils()完成sequencer的注册,并且声明new()函数。
class reg_sequencer extends uvm_sequencer #(reg_trans);
`uvm_component_utils(reg_sequencer)
function new(string name ="reg_sequencer",uvm_component parent);
super.new(name,parent);
endfunction
endclass:reg_sequencer
之后是sequence产生test中相应的sequence_item,首先声明了相应的成员变量,并加以约束,约束采用soft软约束,以便virtual_sequence对其进行随机化。
rand bit[7:0] addr = -1;
rand bit[1:0] cmd = -1;
rand bit[31:0] data = -1;
constraint cstr{
soft addr == -1;
soft cmd == -1;
soft data == -1;
}
sequence挂载在sequencer之后,会执行task body()方法。在task_body()阶段,产生相应的sequence_item,并利用sequence中的成员变量对sequence_item进行约束,采用uvm_do_with()方法
`uvm_do_with(req,{local::addr >= 0 -> addr == local::addr;
local::cmd >= 0 -> cmd == local::cmd;
local::data >= 0 -> data == local::data;})
并利用get_response()获得driver的rsp,这里也发生了报错,也是因为sequence没有添加参数化类#(reg_trans)
get_response(rsp);
基于reg_base_sequence,我们可以产生很多继承于其的子类,用于寄存器的读/写/无操作。对于读这一sequence,只需要将cmd修改成读,写也类似。对于无操作,需要将cmd改成`idle,data和addr改成0。
接下来是对mcdf_package中的env以及test的重构。
首先在env中,需要添加virtual_sequencer这一组件,并将其中包含的sequencer句柄与之前底层的sequencer组件进行连接。
我们首先声明virtual_sequencer,这是一个组件,其中的成员变量是各个sequencer句柄,包含1个reg_sequencer的句柄,1个fmt_sequencer的句柄和3个chnl_sequencer的句柄。之后是对virtual_sequencer的注册。
class mcdf_virtual_sequencer extends uvm_sequencer;
reg_sequencer reg_sqr;
fmt_sequencer fmt_sqr;
chnl_data_sequencer chnl_sqrs[3];
`uvm_component_utils(mcdf_virtual_sequencer)
function new(string name = "mcdf_virtual_sequencer", uvm_component parent);
super.new(name,parent);
endfunction
endclass: mcdf_virtual_sequencer
之后将virtual_sequencer句柄包含在env环境中,并在build_phase阶段创建virtual_sequencer组件,在connect_phase阶段将virtual_sequencer中包含的sequencer句柄,与底层的sequencer进行连接。
virt_sqr.reg_sqr = reg_agt.sequencer;
virt_sqr.fmt_sqr = fmt_agt.sequencer;
foreach(chnl_agts[i]) virt_sqr.chnl_sqrs[i] = chnl_agts[i].sequencer;
完成之后,我们需要将base_test拆分为base_sequence和base_test,这里,sequence用于产生相应需要的item,test作为一个空壳,在run_phase阶段实现virtual_sequence挂载在virtual_sequencer上。
对于virtual_sequence,包含的register的读/写/idle指令的sequence,以及fmt中对于fifo大小以及fifo带宽的sequence,以及产生三个channel数据的sequence句柄。
idle_reg_sequence idle_reg_seq;
write_reg_sequence write_reg_seq;
read_reg_sequence read_reg_seq;
fmt_sequence fmt_seq;
chnl_data_sequence chnl_data_seq[3];
同样,virtual_sequence挂载于virtual_sequencer上,会执行其body()方法,在body()方法中执行do_reg(),do_formatter(),do_data()三个方法,这三个方法由于在不同的test中执行不同,我们在base_sequence中采用虚方法,以方便子类继承。
不可忽略的是,由于virtual_sequence中的sequence需要挂载在virtual_sequencer上,所以采用宏声明,将m_sequencer转化为p_sequencer。
`uvm_declare_p_sequencer(mcdf_virtual_sequencer)
之后在base_test中,在build_phase阶段利用uvm_config_db#(T)::get(this," ",“interface”,interface)来获取config_db数据库中的接口。在run_phase执行run_virtual_sequence()方法,该方法用于将virtual_sequence进行创建,并将其挂载在virtual_sequencer上。在后续可见。
例如,我们在consistence_test中将其拆分为consistence_sequence和consistence_test,对于cosnistence_sequence中do_reg()等方法进行重写,根据要求产生相应的sequence,并挂载在virtual_sequencer中的句柄上。
`uvm_do_on_with(write_reg_seq,p_sequencer.reg_sqr,{addr ==`SLV0_RW_ADDR;data == wr_val;})
`uvm_do_on_with(read_reg_seq,p_sequencer.reg_sqr,{addr == `SLV0_RW_ADDR;})
这是写/读的sequence的产生与挂载,其余均采用`uvm_do_on_with()方法。
对于test,只需要注册和重写run_virtual_sequence()这一虚方法。
即创建相应的virtual_sequence,并挂载在virtual_sequencer上。
top_seq = mcdf_data_consistence_basic_virtual_sequence::type_id::create("top_seq");
top_seq.start(env.virt_sqr);
以上就完成了generator向sequence/sequencer的转变。