Welcome ! This website will help YOU (recent graduates/professionals) learn verification languages like SystemVerilog and UVM. Register for free and access more content !

In Register Model, we have seen how to create a model that represents actual registers in a design. Now we'll look at the different components in a register environment required to perform register accesses such as read and write operations.

There are essentially four components required for a register environment :

  • A register model based on UVM classes that accurately reflect values of the design registers
  • An agent to drive actual bus transactions to the design based on some protocol
  • An adapter to convert the read and write statements from the model to protocol based bus transactions
  • A predictor to understand bus activity and update the register model to match the design contents

Register Adapter

Register model reads and writes are normally performed by calling appropriate read() and write() methods that belong to the uvm_reg class from where registers for the design are inherited.

class reg_ctl extends uvm_reg;
m_reg_ctl.write (status, 16'hcafe);
m_reg_ctl.read  (status, rdata);

But inorder to do a FRONTDOOR access, we need to convert the UVM write and read methods into real bus protocol transactions that can be driven to the DUT via its own bus interface. This conversion process is facilitated by the adapter via reg2bus() and bus2reg() functions. As the names imply, reg2bus() convert register level objects into a protocol transaction and bus2reg() vice versa. An example of how these methods work might make it more clear.

Based on the design protocol, we have to create and define a separate adapter that inherits from uvm_reg_adapter. The method reg2bus() accepts an item of type uvm_reg_bus_op and assigns appropriate address, data and direction values to a specified sequence item which gets returned when this function is called.

class reg2apb_adapter extends uvm_reg_adapter;
   `uvm_object_utils (reg2apb_adapter)
   // define new function here
   virtual function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);
      bus_pkt pkt = bus_pkt::type_id::create ("pkt");
      pkt.write = (rw.kind == UVM_WRITE) ? 1: 0;
      pkt.addr  = rw.addr;
      pkt.data  = rw.data;
      `uvm_info ("adapter", $sformatf ("reg2bus addr=0x%0h data=0x%0h kind=%s", pkt.addr, pkt.data, rw.kind.name), UVM_DEBUG) 
      return pkt; 
   virtual function void bus2reg (uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
      bus_pkt pkt;
      if (! $cast (pkt, bus_item)) begin
         `uvm_fatal ("reg2apb_adapter", "Failed to cast bus_item to pkt")
      rw.kind = pkt.write ? UVM_WRITE : UVM_READ;
      rw.addr = pkt.addr;
      rw.data = pkt.data;
      `uvm_info ("adapter", $sformatf("bus2reg : addr=0x%0h data=0x%0h kind=%s status=%s", rw.addr, rw.data, rw.kind.name(), rw.status.name()), UVM_DEBUG)

The method bus2reg() does the exact opposite by accepting a bus based packet and assigns appropriate address, data and direction information to an item of type uvm_reg_bus_op.

Register Predictor

The role of a predictor is to update the register model mirror values based on observed bus transactions. The adapter has already converted bus specific transaction to an item type that can be understood by the model. The predictor has to determine the register being accessed based on bus address, and then update the register's mirror value. This is protocol independent and hence we do not need to define a custom class. However, we'll have to create a parameterized version of a register predictor as shown below that can be integrated within our register environment.

   uvm_reg_predictor #(bus_pkt)   m_apb2reg_predictor;

Register environment integration

Let's use all the above components and integrate them in a separate register environment to make things more re-usable.

class reg_env extends uvm_env;
   `uvm_component_utils (reg_env)
   function new (string name="reg_env", uvm_component parent);
      super.new (name, parent);
   ral_my_design                  m_ral_model;         // Register Model
   reg2apb_adapter                m_reg2apb;           // Convert Reg Tx <-> Bus-type packets
   uvm_reg_predictor #(bus_pkt)   m_apb2reg_predictor; // Map APB tx to register in model
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_ral_model          = ral_sys_my_design::type_id::create ("m_ral_model", this);
      m_reg2apb            = reg2apb_adapter :: type_id :: create ("m_reg2apb");
      m_apb2reg_predictor  = uvm_reg_predictor #(bus_pkt) :: type_id :: create ("m_apb2reg_predictor", this);
      m_ral_model.build ();
      m_ral_model.lock_model ();
      uvm_config_db #(ral_my_design)::set (null, "uvm_test_top", "m_ral_model", m_ral_model);
   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase (phase);
      m_apb2reg_predictor.map       = m_ral_model.default_map;
      m_apb2reg_predictor.adapter   = m_reg2apb;

There are three components that we have to declare and create in the build_phase(). It is important to note that a register model has to be locked via invocation of its lock() function in order to prevent any other testbench component or part from modifying the structure or adding registers to it. The build() method of the register model is a custom function not a part of standard UVM library, simply to initiate building sub-blocks, maps and regsiters within the model. It's a good idea to place this model somewhere in the configuration database so that other components may access it.

Now we have to provide the predictor with a mapping scheme so that it can match the address values with those of the registers in the model, and also have a handle to the adapter so that it can take the converted bus values directly. This is best done in the connect_phase() method as shown above.

Click me to see a complete example !

Was this article helpful ?

We use cookies to personalize content and ads, to provide social media features and to analyze our traffic. You consent to our cookies if you continue to use our website. To find out more about the cookies we use and how to delete them, see our privacy policy.

  I accept cookies from this site.
EU Cookie Directive plugin by www.channeldigital.co.uk