3 minutes reading time (675 words)

Effective usage of uvm_reg methods

Effective usage of uvm_reg methods

UVM register model is quite extensive and has many useful API that help query registers and fields based on their names. Typically, registers are accessed by hierarchical references and there may be a better alternative in cases where there is a consistent naming scheme applied to all registers in the model.

To give you a bit of a background on what I am talking about here, I had this design which had multiple register blocks with the same set of registers within each block that had to be written with random values.

The problem

I would normally write a sequence to program each flower which would look something like what's below.

// Defines for path to register model
`define RM_DAISY_A001   reg_model.m_daisy_reg_block.m_daisy_Main_CfgGen_A001
`define RM_DAISY_B001   reg_model.m_daisy_reg_block.m_daisy_Main_CfgGen_B001
`define RM_LILLY_A001   reg_model.m_daisy_reg_block.m_daisy_Main_CfgGen_A001
`define RM_TULIP     reg_model.m_tulip_reg_block
class m_daisy_seq extends uvm_sequence;
  task body ();
    `RM_DAISY_A001.write (status, value);
    `RM_DAISY_B001.write (status, value);
class m_lilly_seq extends uvm_sequence;
  task body ();
    `RM_LILLY.randomize ();
    `RM_LILLY.update (status);
class m_tulip_seq extends uvm_sequence;
  task body ();
    `RM_TULIP_A001.set (value);
    `RM_TULIP_B001.set (value);

But this would require quite a lot of code to be written for each flower, and all these sequences do the same functionality with the same set of values to be written to the same set of registers within multiple register blocks.

The solution

As far as readability goes, this is a good way but little difficult to scale if the number of flowers change. Instead we can use some really useful methods within uvm_reg_block class to make our code really concise.

First I created a generic sequence that will accept the flower name and it's register block so that you can write configurations into that particular flower. Because we are using a parent class handle of type uvm_reg_block to access any m_*_reg_block, we will not be able to reference its registers directly using . operator. So we need to loop through the available registers within the block and select the one we require. Luckily there is a standard method called get_reg_by_name() inside uvm_reg class that will return the handle to the register if we provide it with a name. I have done this in my custom function set_value_for_reg. After all the register configurations are done, I can call update() to update the design with new values all in one go.

// The main sequence to configure registers - replaces all the m_*_seq we saw above
class gen_seq extends uvm_sequence;
  string      flowerName;
  uvm_reg_block    tmp_blk;
  virtual function void set_value_for_reg (uvm_reg my_reg, string name, int val);
    uvm_reg tmp = tmp_blk.get_reg_by_name (name);
    tmp.set (val);
  virtual function void set_reference (uvm_reg_block tmp_blk, string name);
    this.tmp_blk = tmp_blk;
    this.flowerName = name;
  task body ();
    uvm_reg   tmp_reg;
    set_value_for_reg ({flowerName, "_Main_CfgGen_A001"}, val);
    set_value_for_reg ({flowerName, "_Main_CfgGen_B001"}, val);
    tmp_blk.update (status);

Now that my generic sequence is ready, I need to invoke it from another sequence or a virtual sequence. We have to provide a register block and name before this sequence is started with the start() method which is done using a custom function set_reference(). In this example a string array of flowers is created and looped through.

// Virtual sequencer where you can start gen_seq for a particular flower
class virtual_seq extends uvm_env;
  string   flower [4];
  task body();
    foreach (flower[i]) begin
      gen_seq   m_gen_seq = gen_seq::type_id::create ({flower[i], "_seq"});
      uvm_reg_block tmp_blk = getRegBlockForFlower (name);
      m_gen_seq.set_reference (tmp, name);
      m_gen_seq.start (p_sequencer);

The final step is to provide a definition for the custom function getRegBlockForFlower() which should return the correct reference to the register block for a particular flower.

// Function to return the correct register block for a particular name
function uvm_reg_block getRegBlockForFlower (string name);
  if (! uvm_config_db #(reg_model_class)::get (null, "uvm_test_top", "reg_model", reg_model))
    `uvm_fatal ("FATAL", "OOPS ! Did not get a register model")
  case (name)
    "daisy"   : return `RM_DAISY;
    "tulip"   : return `RM_TULIP;
    "lilly"   : return `RM_LILLY;

Feel free to comment below and maybe share how you might have used something similar !

that you have to reset your register model ?
how to create a singleton object

By accepting you will be accessing a service provided by a third-party external to https://www.chipverify.com/