文章目录
在之前的中学习中,通过TLM端口/TLM FIFO实现了monitor、reference model与checker之间的通信,相较于之前的mailbox句柄连接,更加容易定制,也使得组件的独立性提高。
本次实验需要实现:
- 将产生transaction并且发送至driver的generator组件,拆分为sequence与sequencer。
- 在拆分的基础上,实现底层的sequence。
- 完成sequencer与driver的连接和通信工作。
- 构建顶层的virtual sequencer。
- 将原有的mcdf_base_test拆分为mcdf_base_virtual_sequence与mcdf_base_test,前者发挥产生序列的工作,后者只完成挂载序列的工作。
- 将原有的mcdf_data_consistence_basic_test和mcdf_full_random_test继续拆分为对应的virtual sequence和轻量化的顶层test。
最终将generator、driver与test的关系移植为sequence、sequencer、driver和test的关系。
driver与sequence的改建
- 移除原有在各个driver中的mailbox句柄,以及在do_driver()任务中使用mailbox句柄通信的方式,用uvm_driver::seq_item_port进行通信,同时定义对应的uvm_sequencer。
task do_drive();
reg_trans req, rsp;
@(posedge intf.rstn);
forever begin
//Use seq_item_port to get request item
seq_item_port.get_next_item(req);
this.reg_write(req);
void'($cast(rsp, req.clone()));
rsp.rsp = 1;
//Use seq_item_port to put response item
rsp.set_sequence_id(req.get_sequence_id());
seq_item_port.item_done(rsp);
end
endtask
底层sequence的提取
- 将原来在各个generator中发送transaction的任务,提取为各个对应的底层sequence。在各个agent中声明、创建对应的sequencer,并且将其与driver通过TLM port连接起来。
// migrate the generator as uvm_sequencer and uvm_sequence
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
// extract chnl_data_sequence from the reg_generator
class reg_base_sequence extends uvm_sequence #(reg_trans);
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;
}
`uvm_object_utils_begin(reg_base_sequence)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(cmd, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_object_utils_end
`uvm_declare_p_sequencer(reg_sequencer)
function new (string name = "reg_base_sequence");
super.new(name);
endfunction
task body();
send_trans();
endtask
// generate transaction and put into local mailbox
task send_trans();
reg_trans req, rsp;
`uvm_do_with(req, {local::addr >= 0 -> addr == local::addr;
local::cmd >= 0 -> cmd == local::cmd;
local::data >= 0 -> data == local::data;
})
`uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
get_response(rsp);
`uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
if(req.cmd == `READ)
this.data = rsp.data;
assert(rsp.rsp)
else $error("[RSPERR] %0t error response received!", $time);
endtask
function void post_randomize();
...
endclass: reg_base_sequence
// migrate the methods in mcdf_base_test to the reg sequences
// -idle_reg() -> idle_reg_sequence
// -write_reg() -> write_reg_sequence
// -read_reg() -> read_reg_sequence
class idle_reg_sequence extends reg_base_sequence;
constraint cstr{
addr == 0;
cmd == `IDLE;
data == 0;
}
`uvm_object_utils(idle_reg_sequence)
function new (string name = "idle_reg_sequence");
super.new(name);
endfunction
endclass: idle_reg_sequence
class write_reg_sequence extends reg_base_sequence;
constraint cstr{
cmd == `WRITE;
}
`uvm_object_utils(write_reg_sequence)
function new (string name = "write_reg_sequence");
super.new(name);
endfunction
endclass: write_reg_sequence
class read_reg_sequence extends reg_base_sequence;
constraint cstr{
cmd == `READ;
}
`uvm_object_utils(read_reg_sequence)
function new (string name = "read_reg_sequence");
super.new(name);
endfunction
endclass: read_reg_sequence
移除generator和uvm_base_test中的transaction发送方法
- 将uvm_base_test中的方法idle_reg()、write_reg()和read_reg()移除并移植到reg_pkg中。由此uvm_base_test只变为了容器的性质,在它内部主要由mcdf_env、mcdf_config配置对象以及被用来挂载的顶层sequence构成。
task run_phase(uvm_phase phase);
phase.raise_objection(this);
this.run_top_virtual_sequence();
phase.drop_objection(this);
endtask
virtual task run_top_virtual_sequence();
// User to implement this task in the child tests
endtask
添加顶层的virtual sequencer
- 由于MCDF验证环境中存在多个底层的sequencer和sequence,因此这就需要有顶层的virtual sequencer与virtual sequence统一调度。在定义了mcdf_virtual_sequencer之后,在mcdf_env中声明、例化,并且完成其与底层sequencer的句柄连接。
// MCDF top environment
class mcdf_env extends uvm_env;
...
// declare the virtual sequencer handle
mcdf_virtual_sequencer virt_sqr;
`uvm_component_utils(mcdf_env)
function new (string name = "mcdf_env", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
...
// instantiate the virtual sequencer
virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
...
// connect the sequencer handles of the virtual sequencer to those dedicated sequencer objects inside the agents
virt_sqr.reg_sqr = reg_agt.sequencer;
virt_sqr.fmt_sqr = fmt_agt.sequencer;
foreach(virt_sqr.chnl_sqrs[i]) virt_sqr.chnl_sqrs[i] = chnl_agts[i].sequencer;
endfunction
endclass: mcdf_env
重构mcdf_base_test和mcdf_data_consistence_basic_test
- 原有的mcdf_base_test除了承担其容器的功能,还在其run_phase阶段中实现了sequence的分阶段发送功能。在添加了顶层的virtual sequencer之后,需要将所有发送序列的顺序和组织等内容均移植到mcdf_base_virtual_sequence,因此需要将mcdf_base_test::run_phase()发送序列的功能移植到定义的mcdf_base_virtual_sequence一侧,而在移植后,mcdf_base_test::run_phase()只需要挂载对应的顶层virtual sequence即可。
- 将其产生和发送transaction的任务,都移植到mcdf_data_consistence_basic_virtual_sequence,而进一步减轻mcdf_data_consistence_basic_test的代码量。测试的动态场景往往都是由virtual sequence统一组织的,而test层往往之后做run_phase前的一些验证环境的配置。
task do_reg();
bit[31:0] wr_val, rd_val;
// slv0 with len=8, prio=0, en=1
wr_val = (1<<3)+(0<<1)+1;
`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;})
rd_val = read_reg_seq.data;
void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));
// slv1 with len=16, prio=1, en=1
// slv2 with len=32, prio=2, en=1
...
// send IDLE command
`uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
endtask
task do_formatter();
`uvm_do_on_with(fmt_config_seq, p_sequencer.fmt_sqr, {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;})
endtask