I’m struggling since far too long and I’m running out of ideas, so I’d like to share my pain with you.
I am trying to launch a register sequence on a layered sequencer as recommended by the UVM User Guide 5.9.2.3. So I have the following elements:
- a uvm_reg_sequence wich performs register access to configure the dut
- a uvm_env extended class to instantiate the register model and a sub-environment containing the bus agent
- a top virtual sequence that starts the uvm_reg_sequence and other sequences
- a simple test that starts the virtual sequence
The trick is that my uvm_reg_sequence is parametrized with the register model type so that I can reuse it at top level by a new specialization with a different register block. I therefore believe I have some type issue related to that, let’s see.
Starting from the uvm_reg_sequence:
class reg_config_bringup_vseq #(type R=uvm_reg_block, type B=uvm_sequence #(uvm_reg_item)) extends reg_config_base_vseq #(R, B);
`uvm_object_param_utils(reg_config_bringup_vseq#(R, B))
function new(string name="reg_config_bringup_vseq");
super.new(name);
endfunction
virtual task body();
`uvm_info(get_type_name(), "Register configuration sequence started", UVM_NONE)
super.body();
write_reg(regmodel.apb.link_ctrl, status, data);
assert(status == UVM_IS_OK);
`uvm_info(get_type_name(), "Register configuration sequence ended", UVM_NONE)
endtask: body
endclass: reg_config_bringup_vseq
The reg_config_base_vseq class takes care of getting the register model from the config_db and store it in a handle called regmodel.
Then we have the uvm_env class which instantiates the register model, the adapter and predictor, an additional specialized register sequencer and a translation sequence:
class my_env extends uvm_env;
// ...
mm_reg_block regblock;
reg_model_cfg ral_cfg;
apb_mm_regblock_obj ral;
// virtual register sequencer
uvm_sequencer#(uvm_reg_item) reg_sqr;
// Translation sequence
typedef uvm_reg_sequence #(uvm_sequence #(apb_trans)) reg2apb_seq_t;
reg2apb_seq_t reg2apb_seq;
virtual function void build_phase(uvm_phase phase);
// ...
ral = apb_mm_regblock_obj::type_id::create("ral", this);
reg_predictor = reg_predictor_t::type_id::create("apb2reg", this);
reg_sqr = uvm_sequencer#(uvm_reg_item)::type_id::create("reg_sqr", this);
endfunction: build_phase
virtual function void connect_phase(uvm_phase phase);
ral.default_map.set_sequencer(reg_seqr,null); // !!! we do not pass the adapter to the address_map
reg2apb_seq = reg2apb_seq_t::type_id::create("reg2apb_seq"); // we create the translation sequence
reg2apb_seq.reg_seqr = reg_sqr; // we assign the translation seq own sequencer to our specialized one
reg2apb_seq.adapter = // we pass the adapter to the translation seq.
apb_mm_reg2apb_adapter::type_id::create("apb_mm_reg2apb_adapter", this);
sub_env.apb.mon.item_collected_port.connect(reg_predictor.bus_in);
endfunction: connect_phase
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
reg2apb_seq.start(sub_env.apb.sqr); // we start the translation seq. on the bus sequencer
endtask: run_phase
endclass: my_env
Up till now there should not be much of a problem (I believe), while now things may get a bit confusing when it comes to specialize the above reg_config_bringup_vseq, here it is:
class bringup_vseq extends base_vseq;
`uvm_object_utils(bringup_vseq)
// handles to sequences provided by the sequencer
typedef reg_config_bringup_vseq#(apb_mm_regblock_obj, apb_base_seq) reg_config_bringup_vseq_t; // !!! The uvm_reg_sequence is specialized with an
// apb_base_seq which is a uvm_sequence#(apb_trans)
reg_config_bringup_vseq_t cfg_vseq;
parallelRdAfterWrSeq pci_vseq;
function new(string name="bringup_vseq");
super.new(name);
endfunction
virtual task body();
`uvm_info(get_type_name(), "Top level sequence started", UVM_NONE)
super.body();
cfg_vseq = reg_config_bringup_vseq_t::type_id::create("cfg_vseq");
pci_vseq = parallelRdAfterWrSeq::type_id::create("pci_vseq");
// Reminder: here we rely on the virtual sequencer to have correctly
// configured the low level sequencers
if(!cfg_vseq.randomize()) `uvm_error("RAND","FAILED");
cfg_vseq.start(reg_sqr); // !!! Here we start the register sequence on the reg sequencer
if(!pci_vseq.randomize()) `uvm_error("RAND","FAILED");
pci_vseq.start(pci_sqr);
endtask
endclass: bringup_vseq
The base_vseq takes care of getting a virtual sequencer which takes care of passing the sequencer handles including the register sequencer reg_sqr. The cfg_vseq has been specialized with the correct register block and bus sequence.
Last but not least, the test class, which becomes very simple:
class bringup_test extends base_test;
`uvm_component_utils(rpcs_bringup_test)
bringup_vseq vseq;
// ... omitting other uvm tasks/functions
task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
vseq = bringup_vseq::type_id::create("vseq");
vseq.start(top_env.sqr); // Start the virtual sequence on the virtual sequencer
phase.drop_objection(this);
endtask: run_phase
endclass: bringup_test
The base_test class takes care of building the environment.
If my understanding is correct, when the specialized reg_config_bringup_vseq_t is started on the register sequencer, it sends a write_reg which will send a generic uvm_reg_item to the bus sequencer, by triggering the following piece of code in the uvm_reg_map:
if (adapter == null) begin
rw.set_sequencer(sequencer);
rw.parent.start_item(rw,rw.prior);
rw.parent.finish_item(rw);
rw.end_event.wait_on();
end
When start_item is executed the things become a little fuzzy to me. The sequencer passed with the set_sequencer is the bus sequencer, but then we are passing rw to the start_item, on a bus sequencer, which I’m not sure how it would interpret it since it cannot handle a generic uvm_reg_item. So the translation between the generic uvm_reg_item and the apb_trans (that our bus sequencer can handle), is not clear where it happens.
So eventually when I run I get a runtime fatal from the bus sequencer which reports the following:
UVM_FATAL @ 12.5ns: uvm_test_top.top_env.sub_env.apb.sqr [sqr] send_request failed to cast sequence item
I apologies for the length of this question, but I’m quite lost with it and can’t seem to understand the bottom of it.