Reg model handle for all reg sequences

I have sequences (reg_seq_A,reg_seq_B,reg_seq_C,…,reg_seq_Z) extending from a base sequence (req_seq_base) itself extending from uvm_reg_sequence.
reg_seq_base has a reference to my register model which is used in reg_seq_A/B/C/…/Z

What is the best way to set the handle to the register model for all these sequences without need to specify it for each sequence separately?

code snippet:


class my_reg_block extends uvm_reg_block;
   //....
endclass

class reg_seq_base extends uvm_reg_sequence;
   my_reg_block regs;
   //....
endclass

class reg_seq_A extends reg_seq_base;
   //....
   virtual task body();
     regs.reg_x1.write(...
     regs.reg_x2.write(...
     regs.reg_x3.read(...
   endtask
   //....
endclass

class reg_seq_B extends reg_seq_base;
   //....
   virtual task body();
     regs.reg_x3.write(...
     regs.reg_x1.write(...
     regs.reg_x2.read(...
   endtask
   //....
endclass
//......
class reg_seq_Z extends reg_seq_base;
   //....
   virtual task body();
     regs.reg_x6.read(...
     regs.reg_x2.write(...
     regs.reg_x6.write(...
   endtask
   //....
endclass

class top_vseq_base extends mvc_sequence;
    reg_seq_A my_seq_a0;
    reg_seq_B my_seq_b0;
    reg_seq_C my_seq_c0;
   //....
    reg_seq_Z my_seq_z0;
   //....

   function new ( string name = "top_vseq_base" );
        super.new(name);
        my_seq_a0= reg_seq_A::type_id::create("my_seq_a0");
        my_seq_b0= reg_seq_B::type_id::create("my_seq_b0");
        my_seq_c0= reg_seq_C::type_id::create("my_seq_c0");
        //....
        my_seq_z0= reg_seq_Z::type_id::create("my_seq_z0");
   endfunction

   task body();
     my_seq_b0.start(null);
     repeat (10) my_seq_a0.start(null);
     my_seq_z0.start(null);
     my_seq_c0.start(null);
   endtask

endclass

class top_test_base extends uvm_test;
   //....
  my_reg_block regs;
   //....
   extern function void init_vseq(top_vseq_base vseq);
   extern function void build_phase(uvm_phase phase);
   // to hook up register adapter and sequencer 
   extern function void connect_phase(uvm_phase phase);
   extern task run_phase(uvm_phase phase);

function void top_test_base::init_vseq(top_vseq_base vseq);
    vseq.my_reg_itf_sqr   = env.my_reg_itf.m_sequencer;

    //TODO implement in a generic way
    vseq.my_seq_a0.m_config = env_cfg.my_reg_itf_cfg;
    vseq.my_seq_b0.m_config = env_cfg.my_reg_itf_cfg;
    //...
    vseq.my_seq_z0.m_config = env_cfg.my_reg_itf_cfg;

    vseq.my_seq_a0.regs = regs;
    vseq.my_seq_b0.regs = regs;
    //...
    vseq.my_seq_z0.regs = regs;

endfunction: init_vseq

function void top_test_base::build_phase(uvm_phase phase);
    env_cfg = top_env_config::type_id::create("env_cfg");
    env_cfg.initialize();
    //REG
    my_reg_itf_cfg = my_reg_itf_cfg_t::type_id::create("my_reg_itf_cfg" );
    if ( !uvm_config_db #(my_reg_itf_bfm_t)::get(this, "", "my_reg_itf", my_reg_itf_cfg.m_bfm) )
        `uvm_error("build_phase", "Unable to get virtual interface my_reg_itf for my_reg_itf_cfg from uvm_config_db")
    my_reg_itf_config_policy::configure(my_reg_itf_cfg, env_cfg.reg_map);
    env_cfg.my_reg_itf_cfg = my_reg_itf_cfg;
    
    // Once the agent configuration objects are done build the env
    env = top_env::type_id::create("env", this);
    env.cfg = env_cfg;
    
    // create the register model
    regs = my_reg_block::type_id::create("regs");
    regs.build();
    
    // make available to sequences
    //uvm_resource_db #(my_reg_block)::set("*","regs", regs, this);//not working!
    
    // adapter for register bus
    reg2bus = reg2bus_adapter_t::type_id::create("reg2bus");
    // BUS register predictor
    reg_predictor = bus_reg_predictor_t::type_id::create("reg_predictor", this); 
endfunction: build_phase

// Function: connect_phase
//
// provide the register model map with a sequencer and a register bus adaptor
//
function void top_test_base::connect_phase(uvm_phase phase);
	// direct connection to bus VIP
	regs.map.set_sequencer(env.my_reg_itf_cfg.m_sequencer,reg2bus);
	regs.map.set_auto_predict(0);
	reg_predictor.map     = regs.map;
	reg_predictor.adapter = reg2bus;
	// Connect the predictor to the bus agent monitor analysis port
	env.my_reg_itf.ap["burst_transfer"].connect(reg_predictor.bus_item_export);
endfunction

task top_test_base::run_phase(uvm_phase phase);
    top_vseq_base vseq;
    //
    //....
    //....    
    //The sequence is OK to run
    `uvm_info(get_type_name(), {"Running virtual sequence '",sequence_name,"'"}, UVM_LOW)
    //
    phase.raise_objection(this);
    init_vseq(vseq);
    vseq.start(null);
    phase.drop_objection(this);
    
endtask: run_phase

endclass


In reply to bassem_b:

Hello, bassem_b.
To operate with N similar sequences (I mean sequences that have the same parent) you could create a container (for example, SystemVerilog queue) typed like parent sequence.
Example:


class top_vseq_base extends mvc_sequence;
  reg_seq_A my_seq_a0;
  reg_seq_B my_seq_b0;
  //....
  reg_seq_Z my_seq_z0;
  //....
  reg_seq_base seqs[$];
  my_reg_block reg_block_inst;
  // and other attributes for configuration

  function new ( string name = "top_vseq_base" );
    super.new(name);

    my_seq_a0= reg_seq_A::type_id::create("my_seq_a0");
    seqs.push_back(my_seq_a0);
    my_seq_b0= reg_seq_B::type_id::create("my_seq_b0");
    seqs.push_back(my_seq_b0);
    //....
    my_seq_z0= reg_seq_Z::type_id::create("my_seq_z0");
    seqs.push_back(my_seq_z0);
  endfunction

  task pre_body();
    super.pre_body();
    if(reg_block_inst == null && !uvm_config_db#(my_reg_block)::get(null, this.get_full_name(), "reg_block_inst", reg_block_inst))
      `uvm_fatal(get_full_name(), $sformatf("Reg block must be set for %s.reg_block_inst", get_full_name()))
    foreach(seqs[i]) begin
      seqs[i].regs = reg_block_inst;
      // and other configuration
      // seqs[i].some_attribute = some_attribute;
    end
  endtask

  task body();
    // Start sequences as you want
      my_seq_b0.start(null);
      repeat (10) my_seq_a0.start(null);
      my_seq_z0.start(null);
    // or maybe that way
    foreach(seqs[i]) begin
      repeat (10) seqs[i].start(null);
    end
   endtask

endclass

As you can see from the code above, there are two opportunities to set the instance of class my_reg_block in virtual sequence: via uvm_config_db or directly. Other processing for configuration each small register sequence inside virtual sequence will perform of its own. If some necessary attributes were not set for the virtual sequence, it reports with fatal.

According to UVM concept (Please, correct me if I am wrong!) uvm_test is a completely unreusable piece of code in contrast to uvm_sequence that is reusable. So test writer has freedom do everything he wants, but transporting well-debugged code to new tests/environments is possible only when that code is in some sequence, not in some test. For instance, a test could provide all necessary staff for the virtual sequence, which knows how to use it to create/config/run all smaller register sequences correctly.
Hope I could help you. Best regards, Maksim.

In reply to maksim_kobzar:

I try to understand what the intention is for this line of code:

reg_seq_base seqs[$];

To handle sequences with single commands the UVM is providing the the sequence library construct.
This gives you the possibility to start a set of sequences with 1 command. You can select the execution order as random or set any other arbitration mode.
I’d recommend to use this construct.