UVM RAL – Basic Register Implementation with Two Fields

Modeling a 32-bit register containing two 16-bit fields using UVM RAL.

Overview

In this example, we implement our second register using UVM RAL. This register contains two fields:

  • slv_cntrl → Bits [15:0] (16-bit)
  • slv_data → Bits [31:16] (16-bit)

Both fields are Read/Write, support reset, and allow randomization.

Step 1 – Extend uvm_reg

Every UVM register must extend uvm_reg and be registered with the UVM factory.


class reg2 extends uvm_reg;
  `uvm_object_utils(reg2)

Step 2 – Declare Two uvm_reg_field Variables


rand uvm_reg_field slv_cntrl;
rand uvm_reg_field slv_data;

We use the rand modifier to enable field randomization.

Step 3 – Constructor

The total register width is 32 bits (0–31). Even though each field is 16 bits, the register size must represent the total width.


function new (string name = "reg2");
  super.new(name,32,UVM_NO_COVERAGE); 
endfunction

Coverage is disabled using UVM_NO_COVERAGE.

Step 4 – Build and Configure Fields

Inside the build() function, we:

  1. Create each field using factory
  2. Configure size and bit position
  3. Define access policy and reset behavior

function void build; 

  slv_cntrl = uvm_reg_field::type_id::create("slv_cntrl");   

  slv_cntrl.configure(  
      .parent(this), 
      .size(16), 
      .lsb_pos(0), 
      .access("RW"),  
      .volatile(0), 
      .reset(16'h0), 
      .has_reset(1), 
      .is_rand(1), 
      .individually_accessible(1)); 


  slv_data = uvm_reg_field::type_id::create("slv_data");   

  slv_data.configure(  
      .parent(this), 
      .size(16), 
      .lsb_pos(16), 
      .access("RW"),  
      .volatile(0), 
      .reset(16'h0), 
      .has_reset(1), 
      .is_rand(1), 
      .individually_accessible(1));   

endfunction

Important: The LSB position must exactly match the DUT bit mapping.

Complete Working Example


`include "uvm_macros.svh"
import uvm_pkg::*;

class reg2 extends uvm_reg;
  `uvm_object_utils(reg2)

  rand uvm_reg_field slv_cntrl;
  rand uvm_reg_field slv_data;

  function new (string name = "reg2");
    super.new(name,32,UVM_NO_COVERAGE); 
  endfunction

  function void build; 

    slv_cntrl = uvm_reg_field::type_id::create("slv_cntrl");   

    slv_cntrl.configure(
      .parent(this), 
      .size(16), 
      .lsb_pos(0), 
      .access("RW"),  
      .volatile(0), 
      .reset(16'h0), 
      .has_reset(1), 
      .is_rand(1), 
      .individually_accessible(1)
    ); 

    slv_data = uvm_reg_field::type_id::create("slv_data");   

    slv_data.configure(
      .parent(this), 
      .size(16), 
      .lsb_pos(16), 
      .access("RW"),  
      .volatile(0), 
      .reset(16'h0), 
      .has_reset(1), 
      .is_rand(1), 
      .individually_accessible(1)
    );   

  endfunction

endclass


module tb;
  
  reg2 r2;
  
  initial begin
    r2 = new("r2");
    r2.build();
  end
  
endmodule
Common Error – Incorrect Register Size

If you change:


super.new(name,16,UVM_NO_COVERAGE);

The simulator will generate an error because the two fields together use 32 bits. The register size must match the total bit usage.

Run This Example on EDA Playground

Click below to open the exact working example.

▶ Open on EDA Playground
Summary
  • A single register can contain multiple fields.
  • The register size must equal total field width.
  • Each field must have correct LSB position.
  • Incorrect sizing leads to simulator errors.