How to implement UVM RAL? – Part 1

Hi Friends, in my previous posts on UVM RAL, we talked about “What is UVM RAL?” and “Why UVM RAL is needed?“. If you’ve not gone through those post, I’ll recommend you to visit those post if you are new to the UVM RAL domain.

Introduction

In this post, we will go through the process and procedure of creating Register Abstraction Model and integrating that generated register model with the UVM Testbench. We’ll understand to create the UVM base test, sequences with the register model. In the end, we’ll see how to create the register test as well?

Before going into any other detail related to UVM RAL, one of the most important thing is to understand some fundamental items related to UVM Register Models as given below. Lets understand the purpose of these jargons being used repeatedly at different places in the UVM RAL domain. Once these jargons are clear, it will help us to understand the topics ahead in the post. So lets start with these –

  1. Register fields
    • A collection of one or more consecutive bits holding some value.
  2. Register
    • A register is a collection of one or more register fields. It represents a single register in the DUT.
  3. Register file
    • A register file contains a collection of registers at different addresses. It represents a block of DUT registers.
  4. Register block
    • It may contain registers, memories, register files, and other register blocks. Register block corresponds to a hierarchical level in the DUT.
  5. Memory block
    • A memory contains a specified number of address locations. It represents a memory in the design.
  6. Address map
    • The address map contains the register files and the memory blocks(if any) in the address space.
  7. Register Model
    • The register model is composed of a hierarchy of register blocks that maps to the design hierarchy. It represents the root register block for all registers and memories in the DUT.
  8. Register operation
    • Register operations are the generic transactions that execute read and write transactions to registers and memories.
  9. Transaction adapter
    • It a UVM object placed in the Testbench to convert between generic register operation and the protocol-specific transactions used by the interface Agent.
  10. Register sequence
    • A UVM sequence of read/write register operations that references the register model and is applied to registers and memories. These sequences are executed on the interface UVM sequencer.
  11. Transaction Predictor
    • A UVM component i.e. uvm_reg_predictor which is placed in the testbench to receive bus transactions from the interface Agent’s monitor and play role in converting those transactions back to register operations. It also helps to either update the register model (for write operation) or compare and update the bus value to the register model expected value (on read operation).

Okay, well, since now we’re familiar with basic terminology being used all across UVM RAL domain, lets see what is the simplified high level UVM RAL flow.

High level UVM RAL flow

1. Register model creation

2. Register model integration with-in the testbench

3. Base test creation

5. UVM sequence creation with register model

6. Different register test creation

Now, Lets start with each point and explain the process of each step.

1. Register model creation

This is the 1st step in the process of implementing UVM RAL. During this step, a model of the DUT registers is created which would be a replica of the DUT registers. Many of the defined terms above are being used to create a register model. Now a days many register model generators are available by different EDA vendors like Synopsys, Mentor and Cadence. Apart from that, there are other vendors as well those provides register model generator utilities. Let me also introduce here, an approach which is getting popular now a days to generate the register model i.e. using the IP-XACT description. IP-XACT is Accellera XML standard format for capturing and integrating design IP. IP-XACT is structural in nature and describes a desired register model by name, address space, register files, register blocks and fields. These XML files can be read by any free or commercial editors those provide table view and extra editing features. IP-XACT Accellera standard provides possibilities of vendor extensions so that vendor may add additional capabilities e.g. if any vendor wants they may add coverage directives or constraints (for register randomization) and variable e.g. hdl_path to directly access the register signals.

Different vendors are having following utilities to generate register models to ease our work to write down register models manually which is very helpful to save time, efforts and to avoid coding errors.

  • Synopsys – ralgen
  • Mentor Graphics – register assistant
  • Cadence – irelGen
  • Agnisys – IDesignSpec

For most of these tools, input required is in form of 4 formats i.e. Excel spreadsheet, IP-XACT XML file, SystemRDL & RALF format register specification. As output, these tools generate complete UVM class based register model.

XML : eXtensible Markup language. It’s an extensible format for storing arbitrary information in ASCII text format. It is typically used behind the scenes by the tools and not meant for direct edit by the user.

IP-XACT : Its an industry standard created by Accellera for storing information about IP and SoC. Information can be about Registers.

SystemRDL : Its and industry standard created by Accellera for only register information about IP and SoC.

RALF : This is a register modeling standard created by Synopsys. It has a TCL–based syntax.

Synopsys based Register model generation flow is shown below –

 

For Project purposes, most likely such utilities from any of the chosen vendor is used to save time and effort plus re-using the automated approach to generate the register model classes. But you must understand the skeleton and internal details of the generated register model which would be helpful to you to debug and make any modification (if required). Though it is suggested not to change things manually until highly desired because it may break up the consistency between what generator produced verses the changes made by you. In case, any update is to be made in the register model, its a good practice to update the inputs provided to the generators and re-run the tool to re-generate the updated model.

Now lets, see what is contained with-in the register models? How the information is organized with-in it structurally?

In the picture above, we can see different UVM classes being used to create a register model –

  • uvm_reg_field
  • uvm_reg
  • uvm_reg_map
  • uvm_mem
  • uvm_reg_block

Lets see, some example code on how to use these UVM classes to build the register model –

====================================
class my_reg extends uvm_reg;
  `uvm_object_utils(my_reg)

   rand uvm_reg_field A;

   function new (string name = "")
      super.new(name, 8, UVM_NO_COVERAGE)
   endfunction: new

   virtual function void build();
      A = uvm_reg_field::type_id::create("A");
      A.configure(this, 8, 0, "RW", 1, 8'h00, 1, 1, 1);
   endfunction: build
endclass: my_reg
====================================

Notice the build() method, its different than build_phase() method which is used for uvm_component class.

Using this code, we declared a register named “my_reg” using the uvm_reg base class and registered it with factory.

There is a field in the register declared as “A” and “my_reg” class builds the declared fields and configure those as well (as shown in the code above).

The configure function is having following arguments –

configure(this, size, lsb, access, volatile, reset value, has reset, rand, accessible)

Now, lets see – how this coded “my_reg” register can be used to declare a register block –

=====================================
class my_reg_block extends uvm_reg_block;
   `uvm_object_utils(my_reg_block)

    rand my_reg reg1;
    uvm_reg_map rmap;
    
    function new (string name = "");
      super.new(this, UVM_NO_COVERAGE);
    endfunction: new

    virtual function void build();
      reg1 = my_reg::type_id::create("reg1");
      reg1.configure(this);
      reg1.build();
      rmap = create_map("rmap", 'h00, 1, UVM_BIG_ENDIAN); // (baseaddress, bytewidth, endianess)
      default_map = rmap;
      rmap.add_reg(reg1, 'h4, "RW"); // (offset, access rights)
      lock_model();
    endfunction: build
endclass: my_reg_block
======================================    

Here in the code above, we can see the usage of adding “my_reg” register into the register block called “my_reg_block”. Important aspect is to see how an address map is created with defining base address of the register block, byte width and endianness. Also notice, how the “my_reg” register is added to the address map by defining offset and access rights.

Great! we now covered what is required in elementary form to create a register model. But imagine If there is a hierarchy of register blocks at different offsets, we may have number of “my_reg_block” gets instantiated into another block e.g. “my_top_reg_block”. In that case, lets see what  the code would look like –

======================================
class my_top_reg_block extends uvm_reg_block;
    `uvm_object_utils(my_top_reg_block)

    my_reg_block R1;
    my_reg_block R2;

    uvm_reg_map top_map;

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

    virtual function void build();
      R1 = my_reg_block::type_id::create("R1");
      R2 = my_reg_block::type_id::create("R2");
      top_map = create_map("top_map", 'h00, 1, UVM_LITTLE_ENDIAN);
      default_map = top_map;
      top_map.add_submap(R1.rmap, 'h00);
      top_map.add_submap(R2.rmap, 'h10);
      lock_model();
    endfunction: build
=========================================

As per the above code, we’ve 2 sub reg blocks at different offsets i.e. ‘h00 and ‘h10. We defined a top level register block containing two register blocks with a top address map and added two sub maps at the desired offsets.

This concludes the post here, with explaining the UVM RAL flow and first step in the UVM RAL flow i.e. Register model creation. Here I tried to cover necessary aspects of register model creation using two approaches i.e. RAL generator and writing register model code manually. In real project situations – there might be thousands of registers distributed in multiple hierarchies with lots many different access rights, reset conditions etc but principles will remain same.

I hope and believe, this post provided you with required details of the UVM RAL based register creation.  In my upcoming post, we’ll cover with other steps in the UVM RAL flow. Till then, take care and stay safe! See you again..soon. If you find it interesting and helpful, please feel free to keep sharing your observations/thoughts in form of comments below.