Professional Documents
Culture Documents
Uvm Ral
Uvm Ral
Resistor and Memory Models: Registers in a design can be likened to the behavior
of resistors and memories. UVM RAL facilitates the creation of register models that
accurately represent these components.
Address Mapping: The unique addresses of resistors and memories can be managed
using UVM RAL's address map features.
1|Page
@shraddha_pawankar Date:26/04/2024
Predictor and Adapter: UVM RAL can be utilized to develop predictors and adapters
for translating transactions between the DUT and the verification environment.
Coverage Collection: UVM RAL includes features for tracking and collecting
coverage, which is crucial for verifying both registers and memories.
2|Page
@shraddha_pawankar Date:26/04/2024
Customizable Reset Values: You can configure the reset values of registers according
to the specific requirements of your design.
Object-Oriented Access: Because the RAL model uses Object-Oriented Programming
(OOP) concepts, you can access registers using their names and call methods
directly, which simplifies the testing process.
Coverage Tracking: The RAL model provides a way to track coverage by connecting
to sample coverage bins, helping you ensure that you've tested all the necessary
scenarios.
Built-in Comparison: It comes with a built-in method to compare the expected
values with the actual values of registers, making it easier to identify any
discrepancies.
Efficient Sequencing: The internal sequencer manages multiple read/write requests
efficiently, even when they happen simultaneously, ensuring smooth operation
during testing.
The UVM RAL model makes testing registers in digital designs easier and more efficient by
providing a standardized, reusable, and easy-to-use framework. It simplifies access to
registers, allows for customization, and helps ensure thorough testing and coverage.
3|Page
@shraddha_pawankar Date:26/04/2024
Address Mapping for registers: The third requirement is that the registers should be
address mapped. This means each registers should have a unique address that the
model can use to access its content. For example, when we want to read or write
data to a specific registers, we need to specify its address.
4|Page
@shraddha_pawankar Date:26/04/2024
Note:
Extending Base Classes: We extend the base classes to create our own registers and memory
classes. This involves adding custom functionality to meet our specific requirements.
5|Page
@shraddha_pawankar Date:26/04/2024
Register fields:
Register + Memories:
We start by constructing registers fields, which will later come together to form
complete registers.
Alongside registers, we introduce memories into the mix, each with its own set of
properties and behaviors.
Register block+Address Map:
With the registers block in place, we progress to building registers sequences, which
dictate the operations to be performed on the registers.
These sequences interact with the sequencer but operate on resistor transactions
rather than bus transactions, presenting a unique challenge.
6|Page
@shraddha_pawankar Date:26/04/2024
Adapter+Predictor:
Note:
We start from an registers where we will be specifying all the registers field and then we
proceed for next block.
So even though the smallest entity is registers field,but we do not implement an individual
class for registers field,instead we start with a registers and include all the registers field in
it.
7|Page
@shraddha_pawankar Date:26/04/2024
Single Field registers: This register has only one field, and its size is 32 bits. We'll name it
"siv_reg0". Even though we didn't specify a field explicitly, the entire register acts as a single
field.
Two-Field registers: This register has two fields: "siv_control" (bits 0-15) and "siv_data" (bits
16-31).
Four-Field registers: In this case, we have four fields: "enable" (1 bit), "mode" (3 bits),
"address" (8 bits), and "data" (20 bits).
Reserved Bits: Sometimes, certain bits in a register might be reserved for future use. In this
example, we have four fields, but the last 11 bits are reserved.
8|Page
@shraddha_pawankar Date:26/04/2024
Mixed registers: Another example includes fields like "mode" (1 bit), "address" (4 bits),
"data" (16 bits), and "reserved" (11 bits).
We'll start by defining classes for each type of register, ensuring that the fields and
their sizes match the design specifications.
For registers without explicitly defined fields, we'll treat the entire register as a single
field.
If some bits are reserved, we'll set them to a default value, like all zeros, so they
don't affect our model's behavior.
Each registers class will inherit from a base class provided by UVM RAL, making it
easy to manage and work with in our verification environment.
With this approach, we can effectively replicate the behavior of different types of registers in
our UVM RAL model, ensuring accurate verification of our design's functionality.
9|Page
@shraddha_pawankar Date:26/04/2024
The UVM library provides the base classes for each of them as mentioned below.
Memory uvm_mem
Registers uvm_reg
Register
The uvm register class is written by extending the uvm_reg.
Each register contains any number of fields, which mirror the values of the corresponding
elements in hardware.
10 | P a g e
@shraddha_pawankar Date:26/04/2024
Example:
`uvm_object_utils(my_reg)
function new(string name = "my_reg");
super.new(name, ./*other arguments*/);
endfunction
Register Field:
uvm_reg_field reg_name;
11 | P a g e
@shraddha_pawankar Date:26/04/2024
int has_coverage);
Syntax:
function void configure ( uvm_reg parent,
int unsigned size,
int unsigned lsb_pos,
string access,
bit volatile
uvm_reg_data_t reset,
bit has_reset,
bit is_rand,
bit individually_accessible)
Description:
1) 1st argument: uvm_reg parent
Specify parent for reg_field
12 | P a g e
@shraddha_pawankar Date:26/04/2024
`include "uvm_macros.svh"
13 | P a g e
@shraddha_pawankar Date:26/04/2024
import uvm_pkg::*;
`uvm_object_utils(reg0)
endfunction
slv_reg0 = uvm_reg_field::type_id::create("slv_reg0");
slv_reg0.configure ( .parent(this),
.size(32),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset('h0),
.has_reset(1),
.is_rand(1),
.individually_accessible(1));
endfunction
14 | P a g e
@shraddha_pawankar Date:26/04/2024
endclass
module tb;
reg0 r1;
initial begin
r1=new("r1");
r1.build();
end
endmodule
https://edaplayground.com/x/byVX
Code :
`include "uvm_macros.svh"
import uvm_pkg::*;
15 | P a g e
@shraddha_pawankar Date:26/04/2024
super.new(name,32,UVM_NO_COVERAGE);
endfunction
slv_cntrl = uvm_reg_field::type_id::create("slv_cntrl");
slv_cntrl.configure(.parent(this),
.size(16),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset('h0),
.has_reset(1),
.is_rand(1),
.individually_accessible(1));
slv_data = uvm_reg_field::type_id::create("slv_data");
slv_data.configure(.parent(this),
.size(16),
.lsb_pos(16),
.access("RW"),
.volatile(0),
.reset(16'h0),
.has_reset(1),
16 | P a g e
@shraddha_pawankar Date:26/04/2024
.is_rand(1),
.individually_accessible(1));
endfunction
endclass
module tb;
reg2 r2;
initial begin
r2=new("r2");
r2.build();
end
endmodule
https://edaplayground.com/x/hXqF
Adding Registers with reserved bits
Code:
`include "uvm_macros.svh"
import uvm_pkg::*;
17 | P a g e
@shraddha_pawankar Date:26/04/2024
en = uvm_reg_field::type_id::create("en");
en.configure(this, 1, 0, "RW", 0, 0, 1, 1, 1);//mapping by position
mode = uvm_reg_field::type_id::create("mode");
mode.configure(this, 3, 0, "RW", 0, 0, 1, 1, 1);
addr = uvm_reg_field::type_id::create("addr");
data = uvm_reg_field::type_id::create("data");
data.configure(this, 16, 12, "RW", 0, 0, 1, 1, 1);
endfunction
endclass
module tb;
reg3 r3;
initial begin
r3=new("r3");
18 | P a g e
@shraddha_pawankar Date:26/04/2024
r3.build();
end
endmodule
RO Read Only. It can simply read the register field value. Writing a field value has no
RW Read Write: Field value can be written/modified and read the field value.
19 | P a g e
@shraddha_pawankar Date:26/04/2024
Write 1 to clear:
1. Writing 1 clears the corresponding bit.
W1C
2. Writing 0 has no effect.
3. Reads field value
Write 1 to set:
1. Writing 1 set the corresponding bit.
Memory 1: This memory stores 16 elements, each of size 8 bits. To implement this memory
in our verification environment, we need to define:
Address range: Typically, memory addresses range from 0 to N-1, where N is the
number of elements. So, for Memory 1, the address range would be from 0 to 15.
Data width: Each element in Memory 1 is 8 bits wide.
20 | P a g e
@shraddha_pawankar Date:26/04/2024
Base address: This specifies the starting address of the memory. Since we're not
given a specific base address, we can assume it starts at address 0.
Memory 2: This memory can store 1024 elements, each of size 16 bits.
21 | P a g e
@shraddha_pawankar Date:26/04/2024
`uvm_object_utils(dut_mem1)
//UVM_NO_COVERAGE
endfunction
endclass
////////////////////////////////////////////////////
`uvm_object_utils(dut_mem2)
super.new(name,1024,16,"RW",UVM_NO_COVERAGE);
endfunction
//////////////////////////////////////////////////
`uvm_object_utils(dut_mem3)
super.new(name,2048,32,"RW",UVM_NO_COVERAGE);
endfunction
endclass
22 | P a g e
@shraddha_pawankar Date:26/04/2024
Methods Description
In these classes, configure the registers and their fields, ensuring each has the
correct offset address and other properties.
Each register class should have its own build function to configure its fields
accordingly.
By implementing these classes, we create a register block representing the resistors
in our verification environment, accurately modeling their behavior and properties.
23 | P a g e
@shraddha_pawankar Date:26/04/2024
We start by defining a class for our register block, named TopRecBlock, which
extends the uvm_reg_block class.
We register our class to a factory using UVMObjectUtils.
We add instances of the registers we want to include in our block, in this case, Reg1
and Reg2.
The standard constructor of a uvm_reg_block requires the instance name and
whether it supports coverage. We choose not to include coverage.
We define a user-defined function called build where we create instances of our
registers and configure them.
Adding Registers to the Block:
In the build function, we create instances of Reg1 and Reg2 and call their respective
build methods.
After building each register, we call the configure function for each register instance,
passing this as the parent register block.
Creating an Address Map:
We create an address map within our register block using the create_map method.
The create_map method requires several arguments:
Instance name of the map.
Base address of the map (usually 0).
Byte width of the bus (usually 4 for a 32-bit bus).
Endian type (e.g., little endian).
For each register, we specify:
The register instance.
The offset address.
The access rights (usually RW for read-write).
This process ensures that each register is mapped to its respective address in the
register block.
Locking the Model:
Finally, we lock the model using the lock_model method.
Locking the model finalizes the address map and prevents any structural
changes.
Once the model is locked, no new registers or memories can be added, and
the model cannot be unlocked.
In summary, we create a register block, add registers to it, define an address map to map
registers to specific addresses, and lock the model to finalize the structure. This ensures that
our verification environment accurately represents the hardware design we're testing.
CODE:
DUT:
module top();
24 | P a g e
@shraddha_pawankar Date:26/04/2024
//base: 0
reg[31:0] reg1; ---------------> offset : 0
reg[31:0] reg2; ---------------> offset : 4
endmodule
TB.SV
`uvm_object_uttils(reg1)
super.new(name,32,UVM_NO_COVERAGE);
endfunction
ctrl.configure( .parent(this),
.size(32),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset('h0),
.has_reset(1),
.is_rand(1),
.individually_accessible(1));
25 | P a g e
@shraddha_pawankar Date:26/04/2024
endfunction
endclass
////////////////////////////////////////////////////////////////////
endfunction
ctrl.configure( .parent(this),
.size(32),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset('h0),
.has_reset(1),
.is_rand(1),
.individually_accessible(1));
endfunction
26 | P a g e
@shraddha_pawankar Date:26/04/2024
endclass
////////////////////////////////////////////////////////////////////
27 | P a g e
@shraddha_pawankar Date:26/04/2024
default_map = create_map("default_map",0,4,UVM_LITTILE_ENDIAN);
lock_model();
endfunction
endclass
https://edaplayground.com/x/wCab
-------------------------------------------------------------------------------------------------------------------------------------
Adapter
The UVM register model (RAL) methods like the write() and read() deal with register
transactions (i.e. register sequence item) and DUT accepts or sends signal level
transactions (bus sequence item) from/to the testbench by an interface.
Hence, there is a requirement to convert the register transactions to bus transactions
and vice-versa.
This is fulfilled by the “Register Adapter”.
The user-defined adapter class is derived from the uvm_reg_adpater base class.
Register model work with reg transaction and DUT can understand only bus
transaction.
We require mechanism to convert reg transaction to bus transaction.
28 | P a g e
@shraddha_pawankar Date:26/04/2024
we aim to convert transactions from the register-level (reg transactions) to the bus-
level transactions that the DUT understands.
When we interact with the register model in our testbench, for example, by calling
methods like write() or read(), these actions generate reg transactions.
Role of the Adapter:
The adapter comes into play to bridge the gap between req transactions and the bus
transactions that the DUT understands.
Conversion Process:
The adapter accesses the members of the UVM Reg bus structure to extract
information about the transaction, such as the address and data.
29 | P a g e
@shraddha_pawankar Date:26/04/2024
Using this information, the adapter then constructs the corresponding bus-level
transaction, which typically includes forming the appropriate signals or protocol-
specific data required by the DUT.
Applying to the DUT:
On the other hand, when the DUT responds with bus-level transactions (such as read
data or acknowledge signals), the adapter intercepts these transactions.
It then performs the reverse operation, converting the bus-level transactions back
into req transactions, which can be processed by the testbench to verify the
behavior of the DUT.
Customization and Integration:
The adapter is customized to handle the specific data formats, address mappings,
and protocol intricacies of the DUT's bus interface.
Once implemented, the adapter is integrated into the testbench environment,
where it seamlessly facilitates communication between the testbench and the DUT
by handling the translation of transactions.
In reg sequence we will be typically calling write method to write the data to a dut.
Write(status,data) : Two arguments
Status: status of transaction
30 | P a g e
@shraddha_pawankar Date:26/04/2024
We do not need to provide an address because address map knows address of each
register
Uvm_reg_bus_op(struct)
Address (addr):
o Represents the address of the register being accessed.
o Default size: 64 bits.
Data (data):
o Stores the read or write data.
o Default size: 64 bits.
Operation Type (kind):
o Indicates the type of operation being performed, whether it's a read or a
write.
o Can have values UVM_READ or UVM_WRITE.
o During a write operation, it will have a value of UVM_WRITE, and during a
read operation, it will have a value of UVM_READ.
Optional Members:
o There may be additional optional members not used initially, depending on
the specific requirements of the verification environment.
Status (status):
o Represents the status of the current transaction.
o Can have values UVM_IS_OK, UVM_NOT_OK, or UVM_IS_X.
o Typically, UVM_IS_OK indicates a successful transaction.
When data is added to the UVM Rec Bus structure using the write or read methods in a
sequence, the first step is to update these members of the structure with the relevant
information. Once these members are updated, the adapter converts this structured data
into a bus transaction.
The bus transaction contains the same data members as the DUT's ports. Each member of
the UVM Rec Bus structure corresponds to a data member of the bus transaction class.
31 | P a g e
@shraddha_pawankar Date:26/04/2024
Once the transaction data is ready, it can be applied to the DUT, allowing for communication
between the testbench and the DUT.
Entire process:
In the process of communication between the register model (RAL) and the DUT, we use
methods like read and write to initiate transactions
Calling reg methods
32 | P a g e
@shraddha_pawankar Date:26/04/2024
Diagram:
Diagram:
Update transaction
33 | P a g e
@shraddha_pawankar Date:26/04/2024
Predictor
Updates
Reg2bus Transaction:
Example :
//write 4 to register
34 | P a g e
@shraddha_pawankar Date:26/04/2024
Regmodel.temp_reg_inst.write(status,4’h4);
Kind : UVM_WRITE(write)
Addr : Address of register from address map(0)
Data : Value (4)
Status = status of transaction(OK)
Note: it leads to updation of struct
Syntax:
Example:
Note :
Bus2reg :
35 | P a g e
@shraddha_pawankar Date:26/04/2024
Syntax:
Example:
assert($cast(tr,bus_item));
Explanation:
bus_item: This parameter represents the bus transaction that needs to be converted into a
register transaction. It behaves as the parent class, meaning it's a base class from which the
actual transaction is derived.
rw: This parameter represents the register bus operation (uvm_reg_bus_op). It holds
information about the register operation to be performed, such as the kind of operation
(read or write), data to be written, address, and status.
Transaction Creation:
Inside the function, a new transaction object tr is created. This transaction object serves as
the child class, derived from the base class represented by bus_item.
The $cast function is used to attempt to cast bus_item into the type of transaction expected
(tr). If successful, tr will hold the data from the bus transaction.
Diagram:
36 | P a g e
@shraddha_pawankar Date:26/04/2024
Syntax:
class reg_axi_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg_axi_adapter)
//reg2bus
virtual function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);
...
...
endfunction
//bus2reg
virtual function void bus2reg (uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
...
...
37 | P a g e
@shraddha_pawankar Date:26/04/2024
endfunction
endclass
DESIGN CODE
module top(
input clk,rst,
input wr,addr, //wr = mode control pin
//wr=1 writing the data
// for writing the data consider din
// for reading the data consider dout
input [3:0] din,
output [3:0] dout);
---------------
---------------
---------------
endmodule
SV CODE
//reg2bus method
38 | P a g e
@shraddha_pawankar Date:26/04/2024
tr=transaction::type_id::create("tr");
tr.wr = (rw.kind == UVM_WRITE) ? 1'b1 : 1'b0;
tr.addr = rw.addr;
if(tr.wr==1'b1) tr.din = rw.data;
return tr;
endfunction
//bus2reg method
endclass
https://edaplayground.com/x/TaVv
DUT
module apb_ram(
);
39 | P a g e
@shraddha_pawankar Date:26/04/2024
--------
----------
-------
endmodule
TESTBENCH
//reg2bus
apb = apb_item::type_id::create("apb_item");
apb.op = (rw.kind == UVM_READ) ? apb::READ : apb::WRITE;
apb.addr = rw.addr;
apb.data = rw.data;
return apb;
endfunction
//bus2reg
40 | P a g e
@shraddha_pawankar Date:26/04/2024
https://edaplayground.com/x/MAEi
Predictor
41 | P a g e
@shraddha_pawankar Date:26/04/2024
IMPLICIT PREDICTION
42 | P a g e
@shraddha_pawankar Date:26/04/2024
Entire process:
43 | P a g e
@shraddha_pawankar Date:26/04/2024
EXPLICIT PREDICTION:
44 | P a g e
@shraddha_pawankar Date:26/04/2024
PASSIVE PREDICTION
The desired value in a register model represents the value that we intend to write to
the register in the next transaction.
It allows us to specify the value we want to see in the register before actually
performing any transaction with the DUT (Device Under Test).
For example, before executing a write operation to the DUT, we can set the desired
value to the data we want to write into the register.
45 | P a g e
@shraddha_pawankar Date:26/04/2024
Mirror Value:
The mirror value in a register model holds the current known state of the hardware
register.
After performing transactions with the DUT, the mirror value reflects the actual
value of the register as observed by the register model.
In simple terms, the desired value is like your shopping list, and the mirror value is like
checking what's in your kitchen.
They help you keep track of what you planned versus what actually happened.
Front door access involves using the bus interface and it is associated with the
timing
46 | P a g e
@shraddha_pawankar Date:26/04/2024
Back door access uses simulator database access routines and this happens in zero
simulation time
API Methods:
Frontdoor Access
Backdoor Access
47 | P a g e
@shraddha_pawankar Date:26/04/2024
set() and get() writes and reads directly to the desired value.
set and get methods operates on the register model desired value, not accesses to
DUT register value. The desired value can be updated to the DUT using the update
method.
48 | P a g e
@shraddha_pawankar Date:26/04/2024
update
mirror
49 | P a g e
@shraddha_pawankar Date:26/04/2024
randomize
reset
reset() sets the register desired and mirrored value to the pre-defined reset value.
2) Mirror Value
a) get_mirrored_value
The method which belongs to desired value are set and get method set.
Allow us to set the value of desired variable to any user defined value while get allow
us to retrieve the value of desired variable.
Whereas when we consider mirror value, we do not need to write anything to this.
It will automatically update it as soon as we perform any transaction.
Only thing that we require is to retrieve the value that is present in this variable.
Hence we only have a single method get mirrored value.
These are the methods which specifically belongs to this two variables.
Whereas once we perform a transaction, both of these variables will be updated with
each transaction,
50 | P a g e
@shraddha_pawankar Date:26/04/2024
Frontdoor access:
a) Write
b) Read
c) Update
d) Mirror
e) Predict
f) Randomize
Backdoor access
g) Peek
h) Poke
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
uvm_status_e status;
51 | P a g e
@shraddha_pawankar Date:26/04/2024
////////////////////////initial value
rdata = regmodel.temp_reg_inst.get();
regmodel.temp_reg_inst.set(8'h11);
rdata = regmodel.temp_reg_inst.get();
// So this won't be reading the current hardware resistor value only it will read the desired
value that exists in our resistor model.
And this do not require any status argument.
regmodel.temp_reg_inst.update(status);
endtask
endclass
Explaination:
This is something that we need to remember set and get method predominantly
work with the desired variable of our resistor model.
They do not actually perform any transaction with dut
Once we call a set method, this will actually update the value of desired variable.
Now we'll try to check whether we actually have the successful update and how we
do that.
We again try to read back the value of a desired variable.
Then when we call an update method, it will first check whether the desired variable
value and the mirrored value are equal or not.
If they are equal, the update method won't initiate any write transaction.
52 | P a g e
@shraddha_pawankar Date:26/04/2024
If both the values are unequal, then only write method will be called and we actually
see update the resistor value
Code: https://www.edaplayground.com/x/abTU
Output:
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test test...
# KERNEL: UVM_INFO /home/runner/testbench.sv(214) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Desired Value : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(221) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Desired Value : 17
# KERNEL: UVM_INFO /home/runner/testbench.sv(65) @ 30:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 17
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271)
@ 230: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-
1.2/src/base/uvm_report_server.svh(869) @ 230: reporter [UVM/REPORT/SERVER]
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
uvm_status_e status;
////////////////////////initial value
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
53 | P a g e
@shraddha_pawankar Date:26/04/2024
regmodel.temp_reg_inst.set(8'h11);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
`uvm_info("SEQ", $sformatf("After Set -> Desired Value : %0d and Mirrored Value :
%0d", rdata, rdata_m), UVM_NONE);
regmodel.temp_reg_inst.update(status);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
endtask
endclass
Explaination:
////////////////////////initial value
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
54 | P a g e
@shraddha_pawankar Date:26/04/2024
Let us try to incorporate the methods that allow us to work with the mirror variable.
First, we are accessing the value of a desired variable, and then we are also accessing
the value of a Mirror variable at an initial state.
So here we haven't performed any transaction.
We are just analyzing what are the default value that we get for both desired as well
as Mirror variable.
///bit[7:0] rdata_m
We have also added one more variable over here to hold the value of mirror
variable.
Then we are calling a reporting macro to print the value of both desired as well as
Mira value.
Then we call a set method.
After we call the set method, we know we will be updating the value of a desired
variable.
Here we will analyze what will be the effect on a Mirror variable.
We already know that Mira value won't be affected with the set method.
After we call set method, we are again accessing the value of desired variable as well
as a mirror variable
and then we are calling a reporting macro to print both the values.
Finally, we call update method.
This will now perform a write transaction to a duty and then we are printing the
value of desired and Mira value.
Now remember, in our entire code we haven't added any predictor block And for
registered model to see the current state of an hardware register predictor is
mandatory.
We know that we have a three different kinds of a predictor.
So if we have not added any predictor BLOCK but still want to see the current state
of an hardware register, then in that case, we need to work with an implicit
prediction.
And for an implicit prediction, we need to turn on an auto predict method.
So let us try to go ahead and execute this code without turning on an auto
prediction.
You will able to see update in the desired value after each step, but you won't see
update in a MIRA value.
So this suggests is that even though we have perform a transaction but our
registered model won't able to predict the current known state of a register, and this
is because we haven't added any predictor in our code and we have also not
specified that we plan to use auto predict or an implicit prediction.
So how we do that, we go to our register block because there we have our map,
right?
So we move to our register block class and we access the map because the set auto
predict method belongs to a map.
Syntax: default_map.set_auto_predict(1);
55 | P a g e
@shraddha_pawankar Date:26/04/2024
The method that we have discussed that is get set and get mirror value are specific
to the variables of a resistor model.This won't be performing any transaction to a
DUT.
The only method that we have used to perform a transaction to a dut is update
method, and this is usually used when we use the set method for updating the
desired value.
Code: https://www.edaplayground.com/x/wgRd
Output:
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test test...
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_spell_chkr.svh(123)
@ 0: reporter [UVM/CONFIGDB/SPELLCHK] include_coverage not located, did you mean
tif
# KERNEL: UVM_INFO /home/runner/testbench.sv(220) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Initial Value -> Desired Value : 0
and Mirrored Value : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(228) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] After Set -> Desired Value : 17 and
Mirrored Value : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 30:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 17
# KERNEL: UVM_INFO /home/runner/testbench.sv(234) @ 30:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] After Tx to DUT -> Desired Value : 17
and Mirrored Value : 17
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271)
@ 230: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-
1.2/src/base/uvm_report_server.svh(869) @ 230: reporter [UVM/REPORT/SERVER]
56 | P a g e
@shraddha_pawankar Date:26/04/2024
So what check means is it will read the data from the hardware register in the
current transaction and it will also compare the value that we read from an
hardware register with an existing mirror value.
If both are equal, then in that case it will simply update the mirror value.
Else if the existing mirror value do not match with the data that we read from an
hardware register,you'll be getting an error message.
Predict value:
57 | P a g e
@shraddha_pawankar Date:26/04/2024
Mirror Value:
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
58 | P a g e
@shraddha_pawankar Date:26/04/2024
uvm_status_e status;
regmodel.temp_reg_inst.write(status,5'h10);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
regmodel.temp_reg_inst.predict(5'h05);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
regmodel.temp_reg_inst.mirror(status,UVM_NO_CHECK);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
endtask
endclass
Explaination:
First, we are calling a write method.This will be writing the value that we specified in
an argument to an hardware resistor.Along with that, it will also update both the
desired as well as some mirrored variable.
So what we are doing we are calling a get method to retrieve the value of desired
variable and we are calling get mirrored value function to retrieve the value of
mirrored variable.
59 | P a g e
@shraddha_pawankar Date:26/04/2024
Write:
60 | P a g e
@shraddha_pawankar Date:26/04/2024
Read:
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
uvm_status_e status;
regmodel.temp_reg_inst.write(status,5'h05);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
`uvm_info("SEQ", $sformatf("Write Tx to DUT -> Des : %0d and Mir : %0d ", rdata,
rdata_m), UVM_NONE);
61 | P a g e
@shraddha_pawankar Date:26/04/2024
regmodel.temp_reg_inst.read(status,dout_t);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
`uvm_info("SEQ", $sformatf("Read Tx from DUT -> Des : %0d and Mir : %0d Data read
: %0d", rdata, rdata_m, dout_t), UVM_NONE);
endtask
endclass
Explaination:
The next two methods that we're going to understand are write and read method.
Write.
Write Method:
Method is used to write the specified value in a DUT register.
And once we perform write transaction, the mirror value will be updated using reg
predict method.
And we already know that once we perform a transaction desired as well as mirror
will hold the same value or play the same role.
The series of arguments that we need to provide to this(write) method include
status.
This is used to store the status of a transaction.
And second one is the value.
The value that we actually want to update in a hardware register.
These two are the mandatory arguments in write method.
While the rest of the arguments could work with a default values.
Read Method:
Similar to write, we have a read method which is used to read the value of an
hardware register from a dut.
Now, once we perform the read transaction to a dut again, it will call a predict
method and update mirror as well as desired value.
This is something that is common whenever we perform any transaction related
method to the dut.
The series of an argument that read method have are status and second one is the
variable which will store the value that we read from the hardware register.
The first argument again will be used to store the status of a transaction.
Let us go through a code to understand more.
We call a write method like this.
We have our model, then the specific register instance, and then we need to call
write method.
Write will have two arguments.
First will be the status of a transaction.
Second one will be the value that we actually want to write in an hardware register.
62 | P a g e
@shraddha_pawankar Date:26/04/2024
Code: https://www.edaplayground.com/x/gAUe
Output:
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test test...
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_spell_chkr.svh(123)
@ 0: reporter [UVM/CONFIGDB/SPELLCHK] include_coverage not located, did you mean
tif
# KERNEL: UVM_INFO /home/runner/testbench.sv(65) @ 50:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 7
# KERNEL: UVM_INFO /home/runner/testbench.sv(244) @ 50:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Write Tx to DUT -> Data Write : 7
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 110:
uvm_test_top.env.agent_inst.d [DRV] Data Read -> Rdata : 7
# KERNEL: UVM_INFO /home/runner/testbench.sv(248) @ 110:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Read Tx from DUT -> Data read : 7
# KERNEL: UVM_INFO /home/runner/testbench.sv(65) @ 170:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 18
# KERNEL: UVM_INFO /home/runner/testbench.sv(244) @ 170:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Write Tx to DUT -> Data Write : 18
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 230:
uvm_test_top.env.agent_inst.d [DRV] Data Read -> Rdata : 18
# KERNEL: UVM_INFO /home/runner/testbench.sv(248) @ 230:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Read Tx from DUT -> Data read : 18
# KERNEL: UVM_INFO /home/runner/testbench.sv(65) @ 290:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 14
# KERNEL: UVM_INFO /home/runner/testbench.sv(244) @ 290:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Write Tx to DUT -> Data Write : 14
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 350:
uvm_test_top.env.agent_inst.d [DRV] Data Read -> Rdata : 14
# KERNEL: UVM_INFO /home/runner/testbench.sv(248) @ 350:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Read Tx from DUT -> Data read : 14
# KERNEL: UVM_INFO /home/runner/testbench.sv(65) @ 410:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 17
# KERNEL: UVM_INFO /home/runner/testbench.sv(244) @ 410:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Write Tx to DUT -> Data Write : 17
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 470:
uvm_test_top.env.agent_inst.d [DRV] Data Read -> Rdata : 17
# KERNEL: UVM_INFO /home/runner/testbench.sv(248) @ 470:
63 | P a g e
@shraddha_pawankar Date:26/04/2024
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
uvm_status_e status;
regmodel.temp_reg_inst.write(status,din_temp);
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
64 | P a g e
@shraddha_pawankar Date:26/04/2024
regmodel.temp_reg_inst.read(status,dout_t);
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
end
endtask
endclass
Explaination:
1. Generating Random Transactions:
We want to perform multiple transactions (reads and writes) to the hardware
register with random data each time.
We use a temporary variable (let's call it data) to hold the random value we
generate.
2. For Loop for Multiple Transactions:
We use a for loop to execute a sequence of transactions.
Inside the loop, we generate a random value and then perform a write
operation followed by a read operation.
3. Generating Random Values:
We use a function (e.g., u_random) to generate random values.
These values are restricted to a specific range, like 5 to 20 in this case.
4. Performing Write Transactions:
For each iteration of the loop, we write the generated random value to the
hardware register.
We print out the value we're writing to the register.
5. Performing Read Transactions:
After writing the random value, we immediately read the value back from the
register.
We print out the value we read from the register.
6. Comparing Written and Read Values:
By printing both the written and read values, we can ensure that the value
we wrote to the register is the same as the value we read back.
This ensures that our transactions are successful and the register is behaving
as expected.
65 | P a g e
@shraddha_pawankar Date:26/04/2024
Randomize Method:
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
uvm_status_e status;
begin
regmodel.temp_reg_inst.randomize();
regmodel.temp_reg_inst.write(status, regmodel.temp_reg_inst.temp.value);
End
endtask
endclass
66 | P a g e
@shraddha_pawankar Date:26/04/2024
Explaination:
We already know for each resistor we have four different variables for keeping the
track of the current state of hardware resistor.
We already discussed mirror variable and desired variable.
The third variable on which randomization work is a value.
So whenever you call the randomized method for the specific instance of a resistor,
in that case,the variable value will get the random value and it's your dut to send
that value in the write method.
So when you call a randomized method, the value won't be automatically applied to
a dut.
Instead, you have random value present in the value variable.
You need to call a write method and add the value to it.
Let us try to understand the sequence where we call a randomized method and use
the random value to
apply ten random stimuli to a dut.
We start again with class.
We named our class as top reg sequence.
We built it by extending an UVM sequence.
We register our class to a factory.
Here we have our reg block, the standard constructor of an UVM sequence, and in
the main task of a body we have used the for loop.
Here we are planning to write ten random stimuli to a dut.
First, we need to call a randomized method.
So in our case we only have a single resistor present in the resistor block.
So we are calling the specific resistor instance for which we plan to generate the
random value.
So reg block Then the resistor instance.
And finally we call the randomize method.
Code : https://www.edaplayground.com/x/tVqk
Output:
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test test...
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-
1.2/src/base/uvm_spell_chkr.svh(123) @ 0: reporter [UVM/CONFIGDB/SPELLCHK]
include_coverage not located, did you mean tif
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 30:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 91
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 30:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 91
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 70:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 88
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 70:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 88
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 110:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 39
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 110:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 39
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 150:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 209
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 150:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 209
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 190:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 246
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 190:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 246
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 230:
67 | P a g e
@shraddha_pawankar Date:26/04/2024
Reset Methods:
1) has_reset
2) get_reset
3) set_reset
4) reset
has_reset:
This will check whether we have the specific reset value for the register.
For example, when we configure our register we do specify the reset value.
So if this reset value is specified, this will return true, else it will return false.
this is again a function and it returns a bit.
o So bit could either have a value of 1 or 0. So if you specify the reset value while
configuring a register or register field, it will return true.
o Else it will return zero.
o Now this have two arguments.
o First one represent the type of an reset.
o It could either be an hard reset or a soft reset.
o And the second argument is the delete parameter by default delete is zero, so
it won't be deleting the value that you specified for the register.
68 | P a g e
@shraddha_pawankar Date:26/04/2024
o But if you want to forcefully remove the reset value that you have added for a
register, you could provide an argument as a one.
o So this will then remove the reset value that you have specified while
configuration.
get_reset:
set_reset:
We could also update this reset value by calling set reset method.
set reset is used to modify thereset value of this resistor.
Reset:
69 | P a g e
@shraddha_pawankar Date:26/04/2024
This predominantly work with the register model and do not perform an actual reset
to an hardware.
Code:
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
uvm_status_e status;
bit rst_status;
rst_status = regmodel.temp_reg_inst.has_reset();
rst_reg = regmodel.temp_reg_inst.get_reset();
70 | P a g e
@shraddha_pawankar Date:26/04/2024
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
regmodel.temp_reg_inst.reset();
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
`uvm_info("SEQ", $sformatf("After Reset -> Mir : %0d Des : %0d ", rdata_m,
rdata), UVM_NONE);
regmodel.temp_reg_inst.set_reset(8'hff);
regmodel.temp_reg_inst.reset();
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
`uvm_info("SEQ", $sformatf("After Reset -> Mir : %0d Des : %0d ", rdata_m, rdata),
UVM_NONE);
endtask
endclass
71 | P a g e
@shraddha_pawankar Date:26/04/2024
Explaination:
Check Reset Availability:
o First, we check if the specified register has a reset value set.
o We use the has_reset method to verify if the register has a reset value.
o The result is stored in the reset_status variable, which is then printed using
reporting macros.
Retrieve Default Reset Value:
o If the register has a reset value, we retrieve it using the get_reset method.
o The reset value is stored in the reset_reg variable and printed out.
Check Desired and Mirror Values Before Reset:
o Before applying the reset, we check the values of both the desired and mirror
variables.
o We use the get method to retrieve the values and print them out.
Apply Reset:
o Next, we apply the reset to the register using the reset method.
o After the reset, both the desired and mirror values will be updated to the
reset value.
Check Values After Reset:
o We again retrieve the values of the desired and mirror variables after the
reset and print them out.
o This confirms that the reset operation was successful and both variables were
updated.
Change Reset Value:
o We demonstrate changing the reset value by using the set_reset method to
set a new reset value for the register.
o After updating the reset value, we apply the reset again and check the values
of the desired and mirror variables to ensure they reflect the new reset value.
Verification in Console:
o By observing the messages printed in the console, we can verify the behavior
of the register model and the effects of applying the reset.
o The console messages show the reset status, default reset value, values
before and after reset, and the updated reset value.
Code : https://www.edaplayground.com/x/u3c9
Output:
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test test...
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_spell_chkr.svh(123)
@ 0: reporter [UVM/CONFIGDB/SPELLCHK] include_coverage not located, did you mean
tif
# KERNEL: UVM_INFO /home/runner/testbench.sv(223) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Reset Value added : 1
# KERNEL: UVM_INFO /home/runner/testbench.sv(227) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Register Reset Value : 17
# KERNEL: UVM_INFO /home/runner/testbench.sv(232) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Before Reset -> Mir : 0 Des : 0
# KERNEL: --------------Applying Reset to register model ---------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(239) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] After Reset -> Mir : 17 Des : 17
# KERNEL: --------------Updating register reset value and applying Reset ----------
72 | P a g e
@shraddha_pawankar Date:26/04/2024
-----
# KERNEL: UVM_INFO /home/runner/testbench.sv(249) @ 0:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] After Reset -> Mir : 255 Des : 255
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271)
@ 200: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-
1.2/src/base/uvm_report_server.svh(869) @ 200: reporter [UVM/REPORT/SERVER]
1. Front door access: This is the conventional method where you need to follow the
proper protocol. It's like using the main entrance of a building. You have to go
through the necessary steps, such as providing valid signals (like address, reset, and
write signals) to perform read or write transactions to the register. Think of it as
following the standard procedures and protocols.
2. Backdoor access: This is a more direct method, similar to finding a hidden or
alternate entrance to a building. With backdoor access, you don't need to follow all
the usual protocols. Instead, you just specify the path to the register directly, and
you can access it without providing all the usual signals. In the example you
mentioned, even though you haven't specified all the signals like address or reset,
you can still access the register directly.
In summary, front door access requires following the standard procedures and providing
valid signals, while backdoor access allows for direct access to the register without needing
to follow all the usual protocols. It's like taking a shortcut to get to the register's value.
Frontdoor Access:
Code:
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
super.new(name);
endfunction
task body;
73 | P a g e
@shraddha_pawankar Date:26/04/2024
uvm_status_e status;
regmodel.temp_reg_inst.write(status,5'h05, UVM_FRONTDOOR);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
`uvm_info("SEQ", $sformatf("Write Tx to DUT -> Des : %0d and Mir : %0d ", rdata,
rdata_m), UVM_NONE);
regmodel.temp_reg_inst.read(status,dout_t, UVM_FRONTDOOR);
rdata = regmodel.temp_reg_inst.get();
rdata_m = regmodel.temp_reg_inst.get_mirrored_value();
`uvm_info("SEQ", $sformatf("Read Tx from DUT -> Des : %0d and Mir : %0d Data
read : %0d", rdata, rdata_m, dout_t), UVM_NONE);
endtask
endclass
Explaination:
74 | P a g e
@shraddha_pawankar Date:26/04/2024
we are utilizing an implicit predictor, we need to also set auto predict method to one
and we need to lock the model in the reg sequence when we call any of the
method.[Refer Register Block].
So we already discussed write and read method if you go through this method.
You could find in a case of a right method.
We have a first argument as a status of a transaction.Then the actual data that we
want to write to the register, then we have a path.Path could either be and backdoor
access path or front door access path.If we do not specify the path it takes the
default argument as UVM default path.[Refer Syntax of Write Method]
Likewise, if we go through a read method here also we have a third argument as
path which could again be front door or back door.
Likewise, if you analyze the different transaction method wherever you see path, this
is used to specify whether you want to perform front door or back door access to a
register.We haven't specified any path in our previous example.
Hence you could see third argument is empty.
But as we plan to use front door access in our current case, we could specifically
mention that in the write method itself.[Refer Syntax of Read Method]
So to specify that we want to perform a front door access, we need to provide the
argument as UVM front
door.
Likewise for read method.Also we could specify the access path to be front door.
So if we do not specify any argument, it will take an default path and that way it will
perform frontdoor access.
But the good idea will be to specifically mention access path that you are utilizing for
a method.
Here we plan to use UVM front door and we have added UVM front door argument
for both read and write method.
Now if you try to execute the code you could actually see that to perform a write
operation, we need to apply validate signals to the ports of a dut.
Likewise, to read the data from a register, we again need to provide the valid signals
to a dut.
Then only we will be able to access the content of a register.
First, we are writing a data to a register here we have specified the data on in bus.
Then we make reset zero.
We also need to make write signal high.
We need to apply a clock and then you could observe that our register is updated to
a value of five.
So to update the content of a register, we need to apply the respective valid signals
to the port of
a duty.
Likewise, to read the content back from a register we have to make write to be
equals to zero.
We need to provide an address of a register, provide the clock as well as reset value,
and then you will able to read back the data from a register.
75 | P a g e
@shraddha_pawankar Date:26/04/2024
Backdoor Access:
Code:
////////////////////creating reg block
`uvm_object_utils(top_reg_block)
super.new(name, build_coverage(UVM_NO_COVERAGE));
endfunction
temp_reg_inst = temp_reg::type_id::create("temp_reg_inst");
temp_reg_inst.build();
temp_reg_inst.configure(this,null);
default_map.set_auto_predict(1);
lock_model();
76 | P a g e
@shraddha_pawankar Date:26/04/2024
endfunction
endclass
Explaination:
To enable backdoor access in an EDA playground, you just need to add the "+w"
switch to your run options. This switch allows both read and write operations to your
variables. By default, without the "+w" switch, you're only allowed to perform read
operations. So, to perform both read and write operations in a backdoor access
scenario, simply include the "+w" switch in your run options.
`uvm_object_utils(top_reg_seq)
77 | P a g e
@shraddha_pawankar Date:26/04/2024
top_reg_block regmodel;
uvm_reg_data_t ref_data;
super.new(name);
endfunction
task body;
uvm_status_e status;
///////frontdoor write
ref_data = regmodel.temp_reg_inst.get_mirrored_value();
////////////////backdoor read
`uvm_info("REG_SEQ",$sformatf("Backdoor read",rdata),UVM_LOW);
///////////////////backdoor write
ref_data = regmodel.temp_reg_inst.get_mirrored_value();
78 | P a g e
@shraddha_pawankar Date:26/04/2024
endtask
endclass
79 | P a g e
@shraddha_pawankar Date:26/04/2024
`uvm_object_utils(top_reg_block)
super.new(name, build_coverage(UVM_NO_COVERAGE));
endfunction
temp_reg_inst = temp_reg::type_id::create("temp_reg_inst");
temp_reg_inst.build();
temp_reg_inst.configure(this,null);
default_map.set_auto_predict();
80 | P a g e
@shraddha_pawankar Date:26/04/2024
lock_model();
endfunction
endclass
/////////Sequence Class
`uvm_object_utils(top_reg_seq)
top_reg_block regmodel;
uvm_reg_data_t ref_data;
super.new(name);
endfunction
task body;
uvm_status_e status;
regmodel.temp_reg_inst.poke(status, 4'hf);
des = regmodel.temp_reg_inst.get();
mir = regmodel.temp_reg_inst.get_mirrored_value();
$display("-----------------------------------------------");
regmodel.temp_reg_inst.peek(status, rdata);
`uvm_info(get_type_name(),$sformatf("READ : %0d",rdata),UVM_LOW);
des = regmodel.temp_reg_inst.get();
81 | P a g e
@shraddha_pawankar Date:26/04/2024
mir = regmodel.temp_reg_inst.get_mirrored_value();
endtask
endclass
Explaination:
82 | P a g e
@shraddha_pawankar Date:26/04/2024
In summary, peek and poke methods are essential tools for performing backdoor access
operations on registers in the verification environment. They allow direct manipulation of
register values without relying on DUT port transactions, making them useful for testing and
debugging purposes.
Note:
In the case of a backdoor access, auto predict is not required in a case of a backdoor
access, the register model will be updated without implicit predictor or auto predict.
So you could just ignore it or you could add the default value of zero to it.
`uvm_object_utils(temp_reg)
////////////////////////adding coverpoints
covergroup temp_cov;
option.per_instance = 1;
coverpoint temp.value[7:0]
83 | P a g e
@shraddha_pawankar Date:26/04/2024
endgroup
super.new(name,8,UVM_CVR_FIELD_VALS);
if(has_coverage(UVM_CVR_FIELD_VALS))
temp_cov = new();
endfunction
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
temp_cov.sample();
endfunction
super.sample_values();
temp_cov.sample();
endfunction
84 | P a g e
@shraddha_pawankar Date:26/04/2024
temp = uvm_reg_field::type_id::create("temp");
// Configure
temp.configure( .parent(this),
.size(8),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(1));
endfunction
endclass
Explaination:
85 | P a g e
@shraddha_pawankar Date:26/04/2024
1. Constructor Setup:
The constructor function new is defined within the temp_reg class. It takes a
string argument name, which defaults to "temp_reg".
The super.new() function is called to invoke the constructor of the base class
(uvm_reg), passing the name argument and two additional arguments:
The second argument specifies the size of the register field, which is
set to 8 bits in this case.
The third argument indicates the type of coverage computation to be
performed for the register field. Here, UVM_CVR_FIELD_VALS is used,
which instructs the system to compute coverage for individual field
values.
2. Coverage Handling:
After creating the register using super.new(), the code checks if coverage
computation is enabled for the UVM_CVR_FIELD_VALS type. This is done
using the has_coverage() method.
If coverage is enabled for field values (UVM_CVR_FIELD_VALS), the code
proceeds to create a new instance of the temp_cov cover group.
This ensures that coverage data will be collected for the field values of the
temp register field.
3. ample Function Implementation:
The sample function is defined as a virtual function within the temp_reg
class.
It takes several arguments: data, byte_en, is_read, and map. However, in
this implementation, these arguments are not used.
Inside the sample function, the sample method of the temp_cov cover group
is called. This is done using the syntax temp_cov.sample().
This function is intended to be called whenever a front door access is
performed on the register, allowing the coverage data to be sampled.
4. Sample Values Function Implementation:
Similarly, the sample_values function is defined as a virtual function within
the temp_reg class.
This function is called by the base class's sample_values function (using
super.sample_values()), ensuring that any default behavior defined in the
base class is preserved.
After calling super.sample_values(), the sample method of the temp_cov
cover group is called to sample the coverage data for the register field.
5. User-Defined Build Function:
The build function is user-defined within the temp_reg class.
It is responsible for creating an instance of the temp register field
(uvm_reg_field) and configuring its properties.
86 | P a g e
@shraddha_pawankar Date:26/04/2024
Overall, these functions together ensure that coverage data is properly sampled and
collected for the temp register field. The sample and sample_values functions are
responsible for triggering the coverage sampling at appropriate times, while the build
function handles the setup and configuration of the register field.
`uvm_object_utils(top_reg_block)
super.new(name, build_coverage(UVM_NO_COVERAGE));
endfunction
uvm_reg::include_coverage("*", UVM_CVR_ALL);
temp_reg_inst = temp_reg::type_id::create("temp_reg_inst");
temp_reg_inst.build();
temp_reg_inst.configure(this);
lock_model();
endfunction
endclass
87 | P a g e
@shraddha_pawankar Date:26/04/2024
Explaination:
1. Constructor Modification:
In the constructor of the top_reg_block class, the build_coverage function is
used to set the coverage option for the block.
The coverage option is set to UVM_NO_COVERAGE by default, indicating
that no coverage computation will be performed for the block as a whole.
2. Adding Coverage to Specific Resistor Instances:
Inside the build method of the top_reg_block class, coverage is enabled for
specific resistor instances.
The include_coverage function is called with "*" as the argument to specify
that coverage should be included for all instances.
The coverage type specified is UVM_CVR_ALL, which enables all types of
coverage (e.g., statement, branch, expression, etc.).
For the temp_reg_inst resistor instance, coverage is explicitly set to
UVM_CVR_FIELD_VALS. This indicates that coverage should be computed for
the individual field values within the resistor instance.
3. Creating and Configuring Resistor Instances:
An instance of the temp_reg class (temp_reg_inst) is created using create
and build methods.
The build method is called to create instances of the rec fields within the
resistor and configure them accordingly.
The resistor instance is then configured within the block using the configure
method.
Coverage is enabled for this specific resistor instance using the set_coverage
method with UVM_CVR_FIELD_VALS as the argument.
4. Default Map Creation:
A default memory map (default_map) is created for the block.
The resistor instance (temp_reg_inst) is added to the default map with the
appropriate offset and access permissions.
5. Locking the Model:
Finally, the lock_model method is called to lock the model, preventing
further modifications.
With these modifications, coverage computation is enabled for specific resistor instances
within the top_reg_block class. The coverage option for the block as a whole remains set to
UVM_NO_COVERAGE by default. Additionally, the run.do file is created to enable analysis
of coverage reports on the console, allowing users to observe the coverage results during
simulation.
88 | P a g e
@shraddha_pawankar Date:26/04/2024
In the context of memory, such as RAM or ROM, the primary difference compared to
registers is that memory consists of a series of locations where data can be stored.
Unlike registers, where desired and mirror values can be useful for tracking changes
and verifying behavior, implementing desired and mirror values for each memory
location would be inefficient and consume significant resources.
Since memory can have a large number of locations, storing desired and mirror
values for each location would unnecessarily burden the simulator and consume
memory. Therefore, in the case of memory, desired and mirror values are not
typically utilized or implemented.
Instead, when you need to access the current status or content of a specific memory
location, you can directly perform a backdoor access. Backdoor access allows you to
read or write data to memory locations without consuming simulation time. This
approach is efficient and suitable for accessing memory contents during verification.
In summary, for memory instances, desired and mirror values are not applicable or
implemented. Backdoor access provides an efficient way to read from or write to
memory locations during simulation, without the need for maintaining additional
desired and mirror values.
Code: https://www.edaplayground.com/x/k__n
89 | P a g e
@shraddha_pawankar Date:26/04/2024
This explanation provides a simplified overview of how read and write operations are
performed using the APB protocol within the dut's environment.
90 | P a g e
@shraddha_pawankar Date:26/04/2024
Register Size:
91 | P a g e
@shraddha_pawankar Date:26/04/2024
Output:
92 | P a g e
@shraddha_pawankar Date:26/04/2024
93 | P a g e