5 minutes reading time (1002 words)

Working with register object configurations


What I like about the register layer in UVM is that it provides a very convenient interface to program registers in a design with minimal trouble. I said minimal because I feel that it is still under the process of evolvement to provide the user with a complete set of API, and hence might require a little work-around here and there - or well, it could be that I haven't found the right solution yet. So, I am going to describe an out of the way approach I used recently to randomize registers to my whims and selectively write certain registers.

Consider the flower register model I described in an earlier post where a single sequence is used to program all the registers within a single block. This is useful when we want all the registers to be updated in a single shot, but not necessarily when only selected registers are required to be written. If you are already thinking that we can always call the `define read or write version of the register object from the model, let me show you another possibility. We can also set an enable for each register such that a particular register be updated only when its enable is active.

Say that each register has a bunch of fields and they have to be randomized based on the constraints applied to other registers in the same block. This would mean that we have to create constraints at the block level such that inter-dependency of constraints between fields of different registers are possible. Not that this is not possible - just that I have been using a vendor tool to create my register model, and there didn't seem a way to insert something like similar. The design underwent multiple changes which changed the register layout and required to regenerate the register model quite too often.

I also had other requirements like to able to update only a single field randomly at a time, add functional coverage, and maybe write these values to an external file. The obvious solution is to create a custom class derived from uvm_object that can hold random variables for each of the fields. Let's assume that the previous flower class has the following three fields belonging to different registers - color, size, and type.

class flower_prop extends uvm_object;
  rand bit [1:0]   size;     // Categorized into four sizes
  rand bit [3:0]  type;     // 8 varities of each flower
  rand bit [7:0]   color;     // 8-bit color representation

Adding rand to each variable makes it randomizable, but as we saw before, there is also a need to have variable enables for each field with a default value.

class flower_prop extends uvm_object;
  bit   size_en = 1;
  bit   type_en = 0;
  bit   color_en = 1;

These new enables can be used in the general sequence that programs the registers. Well, this might clutter the configuraton object and be an overkill for large number of registers and fields within the same block, else but works well for small numbers.

task body ();
  // If the variable is active, write into the register
  if (size_en)
    set_value_for_reg ({flowerName, "_Main_CfgGen_A001"}, val);
  tmp_blk.update (status);

Now, there's a problem. Whenever a flower_prop object is created, all its variables will have zero, and not the initial values of the actual register. So we need to initialize the configuration object flower_prop with the reset values from the register model. This is not required if you know that you'll never use this configuration object without randomizing it first.

function new (string name = "flower_prop");
  super.new (name);
  init (name);
virtual function void init (string name);
  uvm_reg_block   tmp;
  uvm_reg         regs[$];    
  uvm_config_db #(uvm_reg_block) :: get( null, "uvm_test_top", {name, "_reg_block"}, tmp);
  regs = tmp.get_registers ();
  foreach (regs[i]) begin
    uvm_reg_field   field;
    field = regs[i].get_field_by_name ( ... );
    size = field.get_reset();

The life of this configuration object ends when the values in it are written into the registers, because after that the register model would have the new values in its mirrored storage, and there's no further use of this object anyway. If this object should have the previous values in the register, then you can use get() instead of get_reset() in the initialization function for each field.


If this configuration object is used as is, then every call to randomize() will do randomization to all the variables. If the requirement is such that only one variable be randomized, then we need to have another work-around in place - with pre and post randomization functions.

constraint c_size { ... };
constraint c_type { ... };
// Selectively turn off randomization for each variable
function void pre_randomize ();
  if (size_en)
    size.rand_mode (0);
  if (type_en)
    type.rand_mode (0);
// Turn ON all variables for randomization
function void post_randomize ();
  size.rand_mode (1);
  type.rand_mode (1);

Even for this configuration, you still have to set correct values to the enables before randomization. You can add another tweak to this by randomizing the enables in the pre_randomize() function. So var_enable is randomized first which gets a binray value of either 001, 010 or 100 which will randomize and write only that particular field to the register.

rand bit [2:0]   var_enable;
function void pre_randomize ();
  std::randomize (var_enable) with  { var_enable inside {3'h1, 3'h2, 3'h4}; };
  {size_en, type_en, color_en} = var_enable;
  if (size_en)

I like to put tweaks here and there in the testbench to enable such flexibility so that I don't have to create a whole new sequence or configuration object provided the design space state is small. It saved me some time and effort, but a little tricky to get these things working. This approach might not work for all, but is still good enough to get the job done. I would love to hear about other alternatives or suggestions, if you have any please post below in the comments below !

Using a central database to store parameters in SV
Practical example of polymorphism in UVM

Related Posts


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