Posted by
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.
Abbreviation
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.
This was a really good article. I also don’t like the naming conventions in the documentation. Desired Value and Mirrored Value are confusing.
I have a question that I can’t find the answer anywhere. It’s really what is the usage model for the API’s (when and where to use which API).
I can make a case for most of these, but not the get() and set().
Why would we want the Desired Value (What you are calling Main Value) Only? In What case would we want to use this.
Thanks for the comment. There are many scenarios where set() and get() API’s are used. For example, Say after an interrupt, you want to update the interrupt control register in the RTL design. So first you will call set() on that register to set the value you want to write and then call the write() method to initiate a transaction to write the register in the DUT. You can use get() API if you have to check some other register value before calling set() on the interrupt control register. Hope this helps.