Mastering UVM Register Model Simplification

VIP Expert

Jan 05, 2015 / 4 min read

When I began using UVM RAL, I could not understand what the UVM base class library had to say about updating the values of desired value and mirror value registers. I also felt that the terms used do not reflect the intent precisely. After spending some time, I came up with a table which helped me to understand the behavior of register model APIs, and how best they can be called.

Here’s where you can find more information on our Verification IP.

Before I introduce the table, let us take a look at the process of creating the register model:

  1. Creating the register format specification
  2. Converting the specification into UVM register model
  3. Using the register model

Creating the register format specification

There are many register formats available to  describe the designer’s register specification.  You are perhaps familiar with the widely used Synopsys RALF format. The figure below illustrates the flow to convert the RALF format into the register model using Synopsys Ralgen tool. The dotted lines indicate that you can generate  register models for different methodologies:

UVM register model access diagram

Using the register model

The register model has a set of variables for desired and mirror register values. The document uses the terms desired and mirror, but I call them as Main and Mirror below to avoid confusion. The intent of the mirror variable is to hold or represent the RTL’s value all the time so that it can be used as Scoreboard. There are a bunch of API’s available to operate on these variables. The intent here is to clarify what happens to the main and mirror variables when any of these API’s are called during simulation.

UVM register model usage diagram

Let us take a look at the available API’s. I classify them into three groups: active,  passive and indirect.

Active:  Physical transactions go out on the bus to do the read and write operation. Read(), write(), update()and mirror() are active API’s which  operate on the DUT using the physical interface. You can optionally use backdoor mechanism in which case it will not consume simulation cycles. You can expect the same RTL register behavior which would have happened using the front door access.

Passive: Only operates with the register model.  set(), get() and  predict() are passive API’s which directly operate on the model. I also call peek()  passive as this will not alter the register value during the read process. For instance, read to clear register – will not be cleared when peek() is executed.

Indirect: There are a set of API’s which indirectly operate on the DUT and they are peek() and  poke().  Please note that peek() and poke() API’s are backdoor access only.  Though poke can update the RTL register, it can’t mimic the actual register behavior which might happen during the physical read. For instance, write one to clear.

Let us take a brief look at the widely used API definitions. You can find more details in the UVM Class Reference Guide.

Read(): Read the value form the DUT register using front-door or backdoor access.

Write(): Update the DUT register using front-door or backdoor access.

Update(): If you have changed any values in the main register variables using set(), you can write all those registers in the DUT using use this one method (batch update). You can call individual write() method to achieve the same results.

Mirror(): Mirror maintains a copy of the DUT register value. Mirror() method reads the register and optionally compares the read back value with the current mirrored value if check is enabled.  The mirroring can be performed using the physical interfaces (front door) or peek() (backdoor) mechanism.

Peek(): Read the value form the DUT register using the backdoor access mechanism.

Poke(): Write the DUT register with the specified value using the backdoor access mechanism.

Predict(): You can use this method to change the mirror variable value with the expected values.

I ran a few experiments and the following table shows what happens in the register model and the DUT when any of these API’s are executed from the test bench.

UMV –  Update Main Variable,  UMrV – Update Mirror Variable,  AP – Auto predict
RDR – Read DUT Register,  UDR – Update DUT Register,  RMV – Read Main Variable
FD – frontdoor,   BD – Backdoor,   *  – check if UVM_CHEK is used,   NA – Not Applicable

UVM register model example diagram

A few points to keep in mind

I didn’t expect peek() and poke() methods  to update the mirror value unconditionally. After looking into the UVM source code, I found that the do_predit() method is called unconditionally inside peek() and poke() methods. I also noticed that the write() and read() methods using backdoor mechanism would update the mirror register as the do_predict()  is called without checking the output of this  get_auto_predict() method. The only place where I see this conditionally called is the write () and read() method with frontdoor access.

After discussing with experts, I understand that the intended functionality is to make sure the mirror variable has the most up-to-date register value in it. Similarly read()/write() using backdoor access update the mirror register — this too is intentional. Because the backdoor is used, there won’t be a transaction on the physical interface that will be observed (when auto-predict is turned OFF) to update the register model. It must thus be updated in all cases.

Continue Reading