Indirect Register Access
序
UVM register model为我们的寄存器验证提供了很大的帮助,多种集成register model的方法,尤其是使用layered register sequencer的方法,方便了间接访问寄存器的验证。
uvm_users_guide_1.1的例子分析
参考第5.9.2.3小节,
// translation sequence type
typedef uvm_reg_sequence #(uvm_sequence #(apb_rw)) reg2apb_seq_t;
class block_env extends uvm_env;
block_reg_model regmodel;
uvm_sequencer#(uvm_reg_item) reg_seqr;
apb_agent apb;
reg2apb_seq_t reg2apb_seq;
virtual function void connect();
if (regmodel.get_parent() == null) begin
regmodel.default_map.set_sequencer(reg_seqr,null);
reg2apb_seq = reg2apb_seq_t::type_id::create("reg2apb_seq",,get_full_name());
reg2apb_seq.reg_seqr = reg_seqr;
reg2apb_seq.adapter =
reg2apb_adapter::type_id::create("reg2apb",,get_full_name());
regmodel.set_auto_predict(1);
end
endfunction
virtual task run();
reg2apb_seq.start(apb.sequencer);
endtask
endclass
中间层sequencer上的sequence item类型是uvm_reg_item,也即是寄存器的读或者写操作了,其是class。 在test里,start translation sequence。
class my_test extends uvm_test;
block_env env;
virtual function void run();
my_reg_sequence seq = my_reg_sequence::type_id::create("seq",this);
seq.start(env.reg_seqr);
endfunction
endclass
实际问题
在我的实际应用中,是想把一个寄存器操作,转化为多个寄存器的读和写。这里,我把间接寄存器放在了一个独立的register map上(名为UnAddressedModules),而不是default map。
class wifi_t extends uvm_reg_block;
...
uvm_reg_map wifi_top;
uvm_reg_map UnAddressedModules;
...
virtual function void build();
wifi_top = create_map("wifi_top", 'h0, 4, UVM_LITTLE_ENDIAN, 1);
UnAddressedModules = create_map("UnAddressedModules", 'h0, 4, UVM_LITTLE_ENDIAN, 1);
default_map = wifi_top;
...
endclass
在env中,集成register model以及构建layer sequence。
virtual function void build_phase(uvm_phase phase);
reg_seqr = uvm_sequencer#(uvm_reg_item)::type_id::create("reg_seqr", this);
rdb = wifi_t::type_id::create("rdb", this);
rdb.configure(null,"");
rdb.build();
rdb.reset();
rdb.lock_model();
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
vsequencer.ahb_master_sqr = ahb_master_uvc.sequencer;
vsequencer.reg_seqr = reg_seqr;
rdb.default_map.set_sequencer(vsequencer.ahb_master_sqr, reg2ahb);
rdb.default_map.set_auto_predict(1);
rdb.UnAddressedModules.set_sequencer(reg_seqr,null); // note that do not set adapter here
rdb.UnAddressedModules.set_auto_predict(1);
reg_tl_seq = reg_tl_seq_t::type_id::create("reg_tl_seq",,get_full_name());
req_tl_seq.reg_seqr = reg_seqr; // set upstream sequencer
req_tl_seq.rdb = this.rdb;
req_tl_seq.adapter = reg2ahb;
reg_tl_seq.phy_bank_cfg = env_cfg.phy_bank_cfg;
endfunction
virtual task main_phase(uvm_phase phase);
super.main_phase(phase);
env_cfg.print();
reg_tl_seq.start(vsequncer.ahb_master_sqr);
endtask
回过头来,参考uvm_reg_sequence.svh。
virtual task body();
...
forever begin
uvm_reg_item reg_item;
reg_seqr.peek(reg_item);
do_reg_item(reg_item);
reg_seqr.get(reg_item);
#0;
end
endtask
所以,我自己的sequence为下面的代码,因为不是uvm_reg_sequence的子类,所以需要手动实现上面body类似代码。
virtual task body();
...
forever begin
uvm_reg_item req;
reg_seqr.peek(req);
select_bank(0);
if (req.kind == UVM_READ) begin
read_data(req);
end
else begin
write_data(req);
end
reg_seqr.get(req);
#0;
req.end_tr();
end
注意,reg_seqr没有对应的driver与之相连,所以要手动end_tr,否则sequence在发出第一个包后,卡住不动,因为没有consume掉这个transaction。可以参考代码uvm_reg_map.svh。
task uvm_reg_map::do_write(uvm_reg_item rw);
...
rw.parent.start_item(rw,rw.prior);
rw.parent.finish_item(rw);
rw.end_event.wait_on();// wait rw to be consumed
endtask
总结
层次化的集成register model的方法,处理间接访问寄存器,可行有效,但要注意一些小细节,比如关于transaction生命周期的。