When I was preparing for a customer presentation on UVM RAL, I could not understand what the UVM base class library is saying about updating the values of desired value and the mirror value registers. Also I felt that the terms used are not exactly reflecting the intent. After spending some time, I came up with a table which will help to understand the behavior when the register model APIs are called. Before I introduce the table, let us take a look at the process about how to create the register model. I would describe that in 3 steps.
Creating the register format specification: There are many register formats available to describe the designer’s register specification. I’m sure all of you are familiar with the widely used Synopsys RALF format. Following is the flow to convert the RALF format into the register model using Synopsys ralgen tool. The dotted lines indicate that you can generate the register models for different methodologies.
Using the register model: The register model has a set of variables for desired and mirror register values. The document uses “desired” and “mirror” but I call them as Main and Mirror 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 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 the simulation.
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. Ex: read to clear register – will not be cleared when peek() is executed.
Indirect: There are 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. Ex: 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 the front-door or backdoor access.
Write(): Update the DUT register using the front-door or backdoor access.
Update(): If you have changed any values in the main register variables using set(), then 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 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
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 have found that the do_predit() method is called unconditionally inside peek() and poke() methods. Also I have 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 the frontdoor access.
After discussing with experts, that is the intended functionality to make sure the mirror variable has the most up-to-date register value in it. Similarly read()/write() using backdoor access would update the mirror register and that too is intentional. Because the backdoor is used, there won’t be a transaction on the physical interface that will be observed (should auto-predict be OFF) to update the register model. It must thus be updated in all cases.
I hope you find this post useful. I welcome any questions or comments on this.
You must be logged in to post a comment.