You are on page 1of 93

@shraddha_pawankar Date:26/04/2024

RAL stands for Register Abstraction Layer

Introduction to Verification Environment:


Start with verifying a registers before moving on to memories due to their
complexity.
Implementation of Resistor:
Understand how to implement a registers in the verification environment.
Resistor Block Implementation:
Incorporate multiple registers with unique addresses into the verification
environment using a registers block.
Adapter Implementation:
Develop an adapter to convert transactions from the registers model to the bus
transaction format.
Predictor Implementation:
Implement a predictor to capture the response of the DUT and send it to the
registers model.
Understanding Resistor Methods:
Explore front door and back door access methods and learn about explicit predictors.
Coverage Computation:
Learn to compute coverage for a registers, which is essential for verification.
Memories Introduction:
Move on to understanding how to incorporate memories into the registers block.
Transaction Methods for Memories:
Explore single and bus transaction methods available for memories.
Coverage Strategies for Memories:
Understand strategies for computing coverage for memories.
Project Integration:
Finally, integrate all the learned fundamentals into a project to solidify
understanding.

Now, how does this relate to UVM RAL?


UVM RAL provides a systematic and efficient way to model and access registers in a design.

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.

Register Abstraction Layer(RAL) Model:


 Most digital controllers or blocks have registers that can be programmed by
software (commonly known as firmware).
 These registers are accessed by certain protocols like AXI, AHB, APB protocols, etc.
 Using these register software can control design behavior in a certain way.
 For example, design can have certain configurations which can be enabled or
disabled by programming registers.
 Registers are commonly known as SFR (Special Function Registers).
 The verification engineer is also responsible to check register accessibility and its
functionality by verifying all registers with possible value, Though the UVM RAL
model is not necessary for registers verification, RAL is a recommended technique
that does the job efficiently.
 32-bit registers are generally used in the designs.
 As mentioned above, software needs to do some transactions based on a supported
protocol to write/ read values to/ from registers.
 So, for this, we need a driver, sequencer to drive sequence_item.
 RAL model provides a set of methods and rules that make verification engineer job
easy,
 The register abstraction layer provides standard base class libraries. It is used to
create a memory-mapped model for registers in DUT using an object-oriented
model.
 The UVM RAL provides a set of classes that model DUT registers and memories.
 It generates stimulus to the DUT and covers some aspects of functional coverage.

For register access, can’t we proceed without RAL?


Yes, we can. But as mentioned above, RAL provides a set of base classes and
methods with a set of rules which easies the effort required for register access.

2|Page
@shraddha_pawankar Date:26/04/2024

Advantages of UVM RAL


Reusability: You can use the same RAL model in different testing environments for
the same design, like testing individual blocks or the entire system. This saves time
and effort because you don't have to create new models each time.
Simplified Access: Once you integrate the RAL model into your testing setup, you
can easily access registers using simple read/write commands. The model handles
the actual communication with the registers internally, so you don't have to worry
about the details.
Consistent Rules: The RAL model follows the same set of rules across the industry,
making it easy for verification engineers to understand and use, no matter where
they work

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.

Minimum Requirements for Including RAL


Existence of registers or Memory: The first requirement is that there should be at
least one registers r or memory in our design that we want to verify. This means we
need some component whose behavior we want to test using the RAL model.
Presence of Fields in registers: The second requirement is that the registers should
have at least one field. A field in a registers refers to different bits that serve
different purposes. For example, if we divide a registers into two parts where one
part holds an address and the other holds data, each part is considered a field. If a
registers doesn't have sub-parts, the entire registers itself can be considered a single
field.

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.

To put it simply, in order to use a RAL model for verification:

 Make sure there's at least one registers or memory in the design.


 Ensure the registers has at least one field, which could be the entire
resistor itself if it doesn't have sub-parts.
 Each registers should have a unique address for the model to access
its content.
Once these three requirements are met, we can use any RAL model to verify the behavior of
the registers in our design.

Components of Register Model

4|Page
@shraddha_pawankar Date:26/04/2024

Explanation of the verification environment in a registers model:

Replication of Components: We replicate the registers and memories from our


design in our verification environment. These replicated components behave just like
the real ones.
Creating Reg Sequences: To test the registers, we create sequences that specify the
type of operation (write or read), the address of th registers, and the data to be
written or read.
Reg Transaction Signals: Unlike the native bus signals used in the design, we work
with specific signals called reg transaction signals in the registers sequences.
These signals include the operation type, address, and data.
Adapter Block: The registers transaction signals need to be converted into bus
transactions that the DUT understands.
This conversion is done by an adapter block, which transforms the registers signals
into bus signals.
Sequencer and Driver: The bus transactions generated by the adapter are sent to a
sequencer, which organizes them, and then to a driver. The driver applies these
transactions to the duty using an interface.
Predictor Block: After applying the transactions, the DUT generates a response.
This response is sent to a monitor, which broadcasts it to the scoreboard for
comparison with expected data, and to the predictor.
The predictor converts the bus transactions back into reg transactions so they can be
stored in the register model.
Adapter in Predictor: Inside the predictor, there's another adapter that converts the
bus transactions back into reg transactions.
This allows the predictor to work with the data before storing it in the register
model.

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

"RAL Mastery Journey."


These names suggest a structured approach to mastering UVM RAL, guiding learners
through various stages and topics to achieve proficiency in the domain.

Register fields:

 understanding the smallest unit within a design: the register field.


 While a resistor itself may contain multiple fields, it's essential to recognize that
these fields are the foundational elements we need to focus on.

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:

 However, to fully integrate these components(Register + Memories) into our model,


we need to establish an equivalent address mapping, aligning each resistor and
memory with their unique addresses within the design.
 This crucial step occurs within the registers block, where we define and organize the
registers and memory properties along with their respective address mappings.
Register Sequence:

 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:

 To bridge this gap, we implement adapters, which transform registers transactions


into bus transactions, enabling seamless communication between the model and the
rest of the verification environment.
 Additionally, we incorporate predictors to capture and analyze the responses from
the monitor, facilitating comprehensive verification.
So, our journey in UVM RAL is all about putting together these different pieces—fields,
memories, blocks, sequences, adapters, and predictors—to create a powerful and effective
registers model for verification.

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.

Different Types Of Registers


In UVM RAL, we're diving into implementing different types of resistors in our verification
environment. Let's break down what each resistor looks like:

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).

Now, how do we implement these in our registers model?

 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.

RAL Model Structure:


The register model or RAL blocks consists of register files, registers, memories, maps, and
other blocks. Let’s understand each of them in detail and how the UVM RAL model provides
classes.

9|Page
@shraddha_pawankar Date:26/04/2024

The UVM library provides the base classes for each of them as mentioned below.

RAL blocks Corresponding RAL base classes

Memory uvm_mem

Address map uvm_reg_map

Register blocks uvm_reg_block

Register files uvm_reg_file

Registers uvm_reg

Register fields uvm_reg_field

UVM RAL Class Hierarchy

Register
The uvm register class is written by extending the uvm_reg.

A register represents a set of fields that are accessible as a single entity.

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:

class my_reg extends uvm_reg;


// declaration for register fields

`uvm_object_utils(my_reg)
function new(string name = "my_reg");
super.new(name, ./*other arguments*/);
endfunction

virtual function void build();


// create and configure register fields
endfunction
endclass

Register Field:

Register fields are declared with uvm_reg_field class type.

uvm_reg_field reg_name;

 Register fields are declared in register class


 The field name must be unique within the scope of its declaration
 The access policy of a field is specified using the uvm_reg_field::configure() method
 Configure method has to be called from the build() method of the register that
instantiates it

11 | P a g e
@shraddha_pawankar Date:26/04/2024

Constructor syntax for Register


function new(string name = “”,
int unsigned n bits,

int has_coverage);

 1st argument: instance name


 2nd argument :no. of bits in a register
 3rd argument : Functional coverage for register
If we want coverage : UVM_CVR_ALL
No coverage : UVM_NO_COVERAGE

Configure of reg field:


Configure reg field according to DUT.
Configuration function has 9 arguments.

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

2) 2nd argument :int unsigned size


 Size of reg field(not register)

3) 3rd argument: int unsigned lsb_pos

12 | P a g e
@shraddha_pawankar Date:26/04/2024

 Lsb position of reg field

4) 4th argument: string access


 Access: rd,wr,wr

5) 5TH argument: bit volatile


 Volatile =1: field can changed between consecutive access
 Volatile =0: field cannot changed between consecutive access(default)
o Generally used

6) 6th argument: uvm_reg_data_t reset


 Power on reset value field
 Reset value defined in code itself

7) 7th argument : bit has_reset


 Has_reset = 1
 All field supports reset

8) 8th argument: bit is_random


 Fields supports randomization
 Access policy must support writing to register

9) 9th argument: bit individually_accessible


Can accessed independent of other field of register

Code for implementation of single reg field:

//Implemenation of single reg field

`include "uvm_macros.svh"

13 | P a g e
@shraddha_pawankar Date:26/04/2024

import uvm_pkg::*;

class reg0 extends uvm_reg;

`uvm_object_utils(reg0)

rand uvm_reg_field slv_reg0; //add reg fields present in registers


//rand allows us to generate random values for uvm_reg_field

function new(string name="reg0");


super.new(name,32,UVM_NO_COVERAGE);//3 arguments
// 1st argument: instance name
// 2nd argument :no. of bits in a register
//3rd argument : Functional coverage for register

endfunction

function void build; //user defined function

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

Adding register with two fields

Code :

`include "uvm_macros.svh"
import uvm_pkg::*;

class reg2 extends uvm_reg;


`uvm_object_utils(reg2)

rand uvm_reg_field slv_cntrl;

15 | P a g e
@shraddha_pawankar Date:26/04/2024

rand uvm_reg_field slv_data;

function new(string name="reg2");

super.new(name,32,UVM_NO_COVERAGE);
endfunction

function void build;

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::*;

class reg3 extends uvm_reg;


`uvm_object_utils(reg3)

17 | P a g e
@shraddha_pawankar Date:26/04/2024

rand uvm_reg_field en;


rand uvm_reg_field mode;
rand uvm_reg_field addr;

rand uvm_reg_field data;

function new(string name="reg3");


super.new(name,32,UVM_NO_COVERAGE);
endfunction

function void build;

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");

addr.configure(this, 8, 4, "RW", 0, 0, 1, 1, 1);

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

Important point from above codes:


1) We are configuring a register here,Configure the reg field according to the
information that we get from a DUT.
2) In majority of cases we will be finding the access type is Read-write.
3) Volatile: let us consider write operation.
So if we want reg field should change its value when we actually write the data or
when we perform a transaction with a reg field.
Whenever you want this kind of a behavior the volatile should be zero.
4) If you set volatile bit to 1, in that case your reg field might change the value between
the transaction also,which is not possible if we set volatile to zero.
5) Random: field support randomization,that is user allow to add random value to a
field.This is possible when access policy support write operation.

Access Policy in the Register


 In UVM RAL, the access policy in the configure() function determines how the
values of the desired value and mirrored value variables are updated when
performing read and write operations on hardware registers.
 These policies don't control the behavior of the hardware register itself but
rather dictate how the verification environment handles the register's values.
 These access types define how the desired and mirrored values are updated
based on read and write operations.
 These access policies are crucial for accurately simulating the behavior of
hardware registers in the verification environment.
 They ensure that the desired and mirrored values reflect the expected behavior
of the hardware, enabling effective verification tests.

uvm_reg_field access policies

Access policies Description

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.

W1S 2. Writing 0 has no effect.


3. Reads field value

Adding Memory in Verfication Environment


To understand memory we only require 4 things

1) What is an address range or memory location that are present in memory


2) Size of memory location
3) Data width of memory location
4) Base address of the memory

Implementing memories in a verification environment involves defining three key aspects:


address range, data width, and base address. Let's break down how we implement the three
memories we mentioned:

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.

 Address range: From 0 to 1023.


 Data width: Each element is 16 bits wide.
 Base address: Starting from address 0.

Memory 3: This memory stores 2048 elements, each of size 32 bits.

 Address range: From 0 to 2047.


 Data width: Each element is 32 bits wide.
 Base address: Starting from address 0.

Constructor for Memory

function new(string name,


longint unsigned size,
int unsigned n_bits,

string access = “rw”,


int has_coverage = uvm_no_coverage)

When we consider RAM = “RW”


When we consider ROM = “RO”

How to add memory:

21 | P a g e
@shraddha_pawankar Date:26/04/2024

class dut_mem1 extends uvm_mem;

`uvm_object_utils(dut_mem1)

function new(string name="dut_mem1");

super.new(name,16,8,"RW",UVM_NO_COVERAGE);// name: instance name

// 16: Memory location

//8 : size of each element

//"RW" : Access policy

//UVM_NO_COVERAGE

endfunction

endclass

////////////////////////////////////////////////////

class dut_mem2 extends uvm_mem;

`uvm_object_utils(dut_mem2)

function new(string name="dut_mem2");

super.new(name,1024,16,"RW",UVM_NO_COVERAGE);

endfunction

//////////////////////////////////////////////////

class dut_mem3 extends uvm_mem;

`uvm_object_utils(dut_mem3)

function new(string name="dut_mem3");

super.new(name,2048,32,"RW",UVM_NO_COVERAGE);

endfunction

endclass

22 | P a g e
@shraddha_pawankar Date:26/04/2024

Adding Register Block in Verification Environment


Register Block:
Keep the track of addresses of all the register as well as memories that exist in a DUT
Register block contains
1) Address map
2) Registers
3) Memories
We add address map for registers and Memories.
There are two scenarios in DUT
1) Series of registers with offset and memories present.
2) Series of register(SIZE and OFFSET) without memory present in DUT.
3) Address of peripheral = Base address + offset address
Uvm_reg_block methods:

Methods Description

New Creates a new instance.

configure Configuration for a specific instance.

create_map Create an address map in the block

set_default_map Defines the default address map

lock_model Lock the model and build the address map

 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.

Steps to write Register block:

23 | P a g e
@shraddha_pawankar Date:26/04/2024

Creating the Register Block:

 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

class reg1 extends uvm_reg;

`uvm_object_uttils(reg1)

rand uvm_reg_field ctrl;

function new(string name = "reg1");

super.new(name,32,UVM_NO_COVERAGE);
endfunction

function void build;

ctrl = uvm_reg_field::type_id::create("ctrl"); //configure

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

////////////////////////////////////////////////////////////////////

class reg2 extends uvm_reg;


`uvm_object_uttils(reg1)

rand uvm_reg_field data;

function new(string name = "reg2");


super.new(name,32,UVM_NO_COVERAGE);

endfunction

function void build;

data = uvm_reg_field::type_id::create("data"); //configure

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

////////////////////////////////////////////////////////////////////

//creating Register block

class top_reg_block extends uvm_reg_block;


`uvm_object_utils(top_reg_block)

rand reg1 reg1_inst; // instance of reg1 and reg2


rand reg2 reg2_inst;

function new(string name="top_reg_block");

super.new(name,UVM_NO_COVERAGE); //standard constructor(two arguments)


endfunction

function void build; // user defined build function

reg1_inst = reg1::type_id::create("reg1_inst"); // object creation


reg1_inst.build(); //manually calling build of reg
reg1_inst.configure(this);//configure instance of reg

reg2_inst = reg2::type_id::create("reg2_inst"); //object creation


reg2_inst.build(); //manually calling build of reg
reg2_inst.configure(this);//configure instance of reg

//add address map : There is a method called Create_map

27 | P a g e
@shraddha_pawankar Date:26/04/2024

default_map = create_map("default_map",0,4,UVM_LITTILE_ENDIAN);

// synatx : Instance,base_addr,size in byte,endian

default_map.add_reg(reg1_inst , 'h0,"RW"); // instance,offest,access


default_map.add_reg(reg2_inst , 'h4, "RW");//access policy should be same in reg block
and configuration

// add_reg is the method in register

//lock model is the mantatory for build the address map


//lock model restricts any structural changes,such as adding memories or registers
// not allow to unlock a model

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

 The component is called Adapter (converts reg transaction to bus transaction)


 We cant directly apply reg transaction to DUT.
Typical flow of implementing adapters in our verification environment:

Understanding the Purpose:

 we aim to convert transactions from the register-level (reg transactions) to the bus-
level transactions that the DUT understands.

Starting with the UVM Req Bus Structure:

 In the UVM register model, transactions are typically represented using a


standardized structure called the UVM Req bus.
 This structure contains members that represent various aspects of the transaction,
such as the address, data, read/write indication, etc.
Generating Reg Transactions:

 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:

 Once the bus-level transaction is constructed by the adapter, it can be applied


directly to the input ports of the DUT.
 This allows the DUT to receive and process the transaction as if it were coming from
an external bus or interface.
Interaction with the Testbench:

 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

Data: data which actually we want to write to DUT

 We do not need to provide an address because address map knows address of each
register

 We calling read method, here we will be storing status of transaction


Data : data which read from a DUT

 When we call read() or write() method,this automatically converts data to be


specified to the reg transaction.

Uvm_reg_bus_op(struct)

 Reg transaction worked on structure : uvm_reg_bus_op (struct)


 The UVM Rec Bus structure is used to represent the transactional data exchanged
between the register model (RAL) and the DUT.

 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.

Property Type Description


addr Uvm_reg_addr_t Address field,default 64 bits
data Uvm_reg_data_t Read or write data,defaults 64 bit
kind Uvm_access_e UVM_READ or UVM_WRITE
N_bits Unsigned int Numbers of bits being transferred.
Byte_en Uvm_reg_byte_en_t Byte enable
status Uvm_status_e UVM_IS_OK,UVM_IS_X,UVM_NOT_OK
UVM_IS_OK for successful transaction.

 From above table,all members present in structure.


 As soon as we add data to read or write method in a sequence.
 1 st target will be to update this members of structure.
 Once we update this,adapter will convert structure data to bus transaction.
 Once transaction data ready it will apply to 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

 i.e read or write method


Update struct

 value is specified in read and write method will update


uvm_reg_bus_op(struct)
Call Adapter Methods:

Converts reg transaction into bus transaction.

 When we writing a data from reg model to DUT.(reg2bus)


 The direction will be reg sequence to DUT.
 In that case we need to convert reg transaction to bus transaction.

32 | P a g e
@shraddha_pawankar Date:26/04/2024

Diagram:

 While reading a response from a DUT and bring it back to reg


model.(bus2reg)
 The direction will be DUT to reg sequence.
 In that case we need to convert bus transaction to reg transaction.

Diagram:

Update transaction

 Update transaction data member


 DUT understands transaction data member.

33 | P a g e
@shraddha_pawankar Date:26/04/2024

Driver apply transaction

 Sequencer sending transaction from reg sequence to a driver.

Monitor capture response

 Once response is ready.


 Monitor will capture the response
 It will send it to a predictor

Predictor

 Converts bus transaction to reg transaction with the help of an adapter.

Updates

 Updates mirror + desired values of reg model.

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:

Pure virtual function uvm_sequence_item reg2bus( const ref uvm_reg_bus_op rw);

Example:

function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw)


transaction tr;
tr=transaction::type_id::create(“tr”);
tr.wr = (rw.kind == uvm_write);
tr.addr = rw.addr;
if(tr.wr == 1’b1) tr.din = rw.data;
return tr;
endfunction

Note :

Reg2bus : we are least interested in reading


rw = instance name for the structure

Bus2reg :

35 | P a g e
@shraddha_pawankar Date:26/04/2024

Syntax:

function void bus2reg(uvm_sequence_item bus_item,ref uvm_reg_bus_op rw)

Example:

function void bus2reg(uvm_sequence_item bus_item,ref uvm_reg_bus_op rw);


// uvm_sequence_item bus_item : behave as parent class
transaction tr;//behave as child class

assert($cast(tr,bus_item));

rw.kind = tr.wr ? UVM_WRITE : UVM_READ;


rw.data = tr.dout;
rw.addr = tr.addr;
rw.status = UVM_IS_OK;
endfunction

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.

Note: We can read transaction with and without predictor also

Diagram:

36 | P a g e
@shraddha_pawankar Date:26/04/2024

How we add Adapter Code in Verification Environment:

Syntax:
class reg_axi_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg_axi_adapter)

function new(string name = "reg_axi_adapter");


super.new(name);
endfunction

//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

Example 1: To write Adapter Class


Example 1:

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

class adapter extends uvm_reg_adapter;


`uvm_object_utils(adapter)

function new(string name="adapter");


super.new(name);
endfunction

//reg2bus method

function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);


// return uvm_seq_item
//method name : reg2bus
// it is generic method. predefine in svh file
//uvm_reg_bus_op : it is a structure we will updating the data
transaction tr;

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

function void bus2reg(uvm_sequence_item bus_item,ref uvm_reg_bus_op rw);


transaction tr;

assert($cast(tr,bus_item);//gives us correct child class


rw.kind = (tr.wr == 1'b1) ? UVM_WRITE : UVM_READ;
rw.data = tr.dout;//reading back from memory
rw.addr = tr.addr;
rw.status = UVM_IS_OK;
endfunction

endclass

https://edaplayground.com/x/TaVv

Example 2: How to write Adapter code

Example 2: Protocol specific requirements

DUT

module apb_ram(

input wire presetn,


input wire pclk,
input wire psel,
input wire penable,
input wire pwrite,
input wire [31:0] paddr;
input wire [31:0] pwdata;
input reg [31:0] prdata;
output reg pready;
output reg pslverr

);

39 | P a g e
@shraddha_pawankar Date:26/04/2024

--------
----------
-------

endmodule

TESTBENCH

class adapter extends uvm_reg_adapter;


`uvm_object_utils(adapter)

function new(string name="adapter");


super.new(name);
endfunction

//reg2bus

virtual function uvm_sequence_item reg2bus (uvm_reg_bus_op rw);


apb_item apb;

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

virtual function void bus2reg(uvm_sequqence_item bus_item,uvm_reg_bus_op


rw);
apb_item apb;
if(!$cast(apb,bus_item)) begin
`uvm_fatal("Convert_apb2reg","Bus item is not of type apb_item)
end

rw.kind = apb.op == apb::READ ? UVM_READ : UVM_WRITE;


rw.addr = apb.addr;
rw.data = apb.data;
rw.status = UVM_IS_OK;
endfunction
endclass

40 | P a g e
@shraddha_pawankar Date:26/04/2024

https://edaplayground.com/x/MAEi

Predictor

Different Types Of Prediction:

41 | P a g e
@shraddha_pawankar Date:26/04/2024

IMPLICIT PREDICTION

 Not mandatory to add predictor explicitely.

 Without a predictor,desired and mirror value will be updated.

42 | P a g e
@shraddha_pawankar Date:26/04/2024

Implicit predictor working

 When we are working with sequencer and Driver,


 They have bidirectional TLM Port.
 We basically captured the response,
 Sequence item we get from a driver and analyzing the value of response,
 We update desired and miroor value
 Hence in case of Implicit predictor, we are not required predictor explicitely.
 Only main thing is ,we get response from a driver
 It is possible to update desired and mirror value without predictor
 We need to turn on auto-prediction

Entire process:

43 | P a g e
@shraddha_pawankar Date:26/04/2024

1) Generate reg data/reg transaction


2) Adapter
Convert reg-tx to bus-tx
3) Sent to driver
4) Driver apply stimuli to DUT
5) Driver collect response of transaction
Send it to sequencer
6) Reg model use that response to update Desired and Mirror value.

EXPLICIT PREDICTION:

44 | P a g e
@shraddha_pawankar Date:26/04/2024

PASSIVE PREDICTION

1) Reg model wont generating any reg-transaction.


2) We have series of bus sequence which sent to sequencer.
3) In passive predictor,we are just sampling the response, Not generating any stimuli
to a DUT.

Desired Value and Mirror Value:


Desired Value

 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.

Register Access Methods


UVM RAL library classes have builtin methods implemented in it, these methods can be used
for accessing the registers.
These methods are referred to as Register Access Methods.
The register model has methods to read, write, update and mirror DUT registers and register
field values, these methods are called API (application programming interface).
APIs can either use front door access or back door access to DUT registers and register
fields.

 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:

read and write

 read() returns and updates the value of the DUT register.


 write() writes and updates the value of the DUT register.
 both read and write can be used for front door access or back door access.
 In read or write value will be updated by the bus predictor on completion of the
front door read or write cycle and automatically in back door read or write cycle.

Frontdoor Access

Backdoor Access

47 | P a g e
@shraddha_pawankar Date:26/04/2024

peek and poke

 peek() reads the DUT register value using a backdoor


 poke() writes a value to DUT register using backdoor

UVM RAL PEEK and POKE

set and get

 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.

UVM RAL Set and Get method

48 | P a g e
@shraddha_pawankar Date:26/04/2024

update

 if there is a difference between desired value and mirrored value,update() will


initiate a write to register.update method can be used after the set method.

UVM RAL update method

mirror

 mirror() reads the updated DUT register values.


 The mirroring can be performed in the front door or back door( peek() ).

UVM RAL Mirror:

49 | P a g e
@shraddha_pawankar Date:26/04/2024

randomize

 randomize() randomizes register or field values with or without constraints.as per


the requirement register values can be modified in post_randomize().after
randomization update() can be used to update the DUT register values.

UVM RAL RANDOMIZE:

reset

reset() sets the register desired and mirrored value to the pre-defined reset value.

Methods Before Transaction


1) Desired Value
a) Set
b) Get

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

 both desired as well as mirror value will be updated.

Methods After Transaction:


Desired Value + Mirrored value

Frontdoor access:
a) Write
b) Read
c) Update
d) Mirror
e) Predict
f) Randomize

Backdoor access
g) Peek
h) Poke

Working with Desired value:

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

// We need to add one variable to store the status of a transaction.


All the methods which work on duty require an argument as a status, so status should be of
this type.

bit [7:0] rdata;

// The primary reason being our resistor is of size 8 bit.


So both the variable that is desired and mirrored will be having a same size as that of an
hardware resistor in our resistor model.
Hence we have declared bits [7:0].

51 | P a g e
@shraddha_pawankar Date:26/04/2024

////////////////////////initial value

rdata = regmodel.temp_reg_inst.get();

`uvm_info("SEQ", $sformatf("Desired Value : %0d", rdata), UVM_NONE);

////////////////// update desire value

regmodel.temp_reg_inst.set(8'h11);

///////////////// get desire value

rdata = regmodel.temp_reg_inst.get();

`uvm_info("SEQ", $sformatf("Desired Value : %0d", rdata), UVM_NONE);

// 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.

///////////////// call write method

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]

Working with Mirrored Value

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

bit [7:0] rdata,rdata_m;

////////////////////////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

`uvm_info("SEQ", $sformatf("Initial Value -> Desired Value : %0d and Mirrored


Value : %0d", rdata, rdata_m), UVM_NONE);

////////////////// update desire value

regmodel.temp_reg_inst.set(8'h11);

///////////////// get desire value

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);

///////////////// call write method

regmodel.temp_reg_inst.update(status);

rdata = regmodel.temp_reg_inst.get();

rdata_m = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("SEQ", $sformatf("After Tx to DUT -> Desired Value : %0d and Mirrored


Value : %0d", rdata, rdata_m), UVM_NONE);

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]

Predict and Mirror Value:.


Now, when we consider mirror why we are understanding them together, because when we
call mirror, the mirror value is updated with the help of an predict method.
So internally it triggers a predict method.
 First, we'll be understanding a predict method.
 The predict method will update the mirror value of a resistor and it will also return
true.
 So if you analyze virtual function bit, this function actually returns bit.
 If prediction was successful for each field in the resistor then it will return true.
 Else it will return false.
 Here we do not need to provide any argument, we just need to call a predict method
and then it will automatically update the mirror value.In fact, it will also update the
desired value.
 So if you remember, set method specifically deals with the desired value.
 Whereas when we consider predict this update boot mirror as well as desired value.
 Predict method:This won't be performing any transaction to a duty.
 Next will consider mirror.This is actually perform read transaction to a duty and then
it updates mirror as well as desired value.
 We already know whenever we perform a transaction both mirror as well as desired
value will be updated with the same value mirror have series of an argument.
 Two arguments are mandatory.
 First one is the status of a transaction and second one is whether we want to
perform a check.

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:

In Mirror Value only two argument is important.

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

58 | P a g e
@shraddha_pawankar Date:26/04/2024

uvm_status_e status;

bit [7:0] rdata,rdata_m;

regmodel.temp_reg_inst.write(status,5'h10);

rdata = regmodel.temp_reg_inst.get();

rdata_m = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("SEQ", $sformatf("Mir : %0d Des : %0d ", rdata_m, rdata),


UVM_NONE);

regmodel.temp_reg_inst.predict(5'h05);

rdata = regmodel.temp_reg_inst.get();

rdata_m = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("SEQ", $sformatf("Mir : %0d Des : %0d ", rdata_m, rdata),


UVM_NONE);

regmodel.temp_reg_inst.mirror(status,UVM_NO_CHECK);

rdata = regmodel.temp_reg_inst.get();

rdata_m = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("SEQ", $sformatf("Mir : %0d Des : %0d ", rdata_m, rdata),


UVM_NONE);

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

 .Then we call predict method.Now we already know predict method won't be


performing any transaction to a DUT.Instead, it will predominantly work on an
register model.
 When we call predict method, this will overwrite the current mirror as well as
desired value.From this transaction, we could guess the value of both mirror as well
as desired.Variable will be 10 once we call a predict method, both desired as well as
mirrored value will have a value of 05.
 Then we are calling a mirror method.Once we call a mirror value, we need to provide
two arguments.First one is the status of an transaction and second one is whether
we want to perform a check.So here we have added an argument as UVM check.So
this will check whether the current mirror value matches to the value that we read
from hardware register.
 And when we call Mirror, it will compare the current mirror value with the data that
we read from anhardware register.And you could guess one zero is not equals to
zero five.Hence it should throw an error.
 Now, remember, Predict works predominantly with the register model, whereas
Mirror actually called read transaction.
Code: https://www.edaplayground.com/x/J2Yq
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 : 16
# KERNEL: UVM_INFO /home/runner/testbench.sv(220) @ 50:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Mir : 16 Des : 16
# KERNEL: UVM_INFO /home/runner/testbench.sv(226) @ 50:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Mir : 5 Des : 5
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 110:
uvm_test_top.env.agent_inst.d [DRV] Data Read -> Rdata : 16
# KERNEL: UVM_INFO /home/runner/testbench.sv(231) @ 110:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Mir : 16 Des : 16
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271)
@ 310: 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) @ 310: reporter [UVM/REPORT/SERVER]

Single Read and Write Transactions:

Write:

60 | P a g e
@shraddha_pawankar Date:26/04/2024

Read:

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

bit [7:0] rdata,rdata_m;

bit [7:0] dout_t;

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

 Status variable need to be of UVM underscore status underscore e type.


 After we perform a write transaction, what we are doing is to retrieve the value of
both desired as well as mirror value.
 Both of these variable will have a same value whenever we perform transaction to a
duty, so they only have an independent existence when we do not perform any
transaction.
 But as soon as we perform any transaction, both of them will be updated to a same
value.
 We are also calling a reporting macro to print the value of both desired as well as
mirror variable.
 This is how we write single value to an hardware register.
 And then here we have a read the only difference being this write should be
replaced with a read.This again have two arguments.
 First argument will be the status of a transaction and second argument will be the
variable where wewant to store the data that we read from an hardware register.
 Since our register is of size 8 bit, we have declared bit [7:0] out here.
 We will be storing the data that this method read from the dut.
 Now remember this is task.Hence do not return anything again.
 We are calling get and get mirror value to observe the value that we have in and
desired as well as mirror variable.
 This is how you perform single write transaction to a dut and single read transaction
to a dut

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_test_top.env.agent_inst.seqr@@trseq [SEQ] Read Tx from DUT -> Data read : 17


# KERNEL: UVM_INFO /home/runner/testbench.sv(65) @ 530:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 20
# KERNEL: UVM_INFO /home/runner/testbench.sv(244) @ 530:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Write Tx to DUT -> Data Write : 20
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 590:
uvm_test_top.env.agent_inst.d [DRV] Data Read -> Rdata : 20
# KERNEL: UVM_INFO /home/runner/testbench.sv(248) @ 590:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Read Tx from DUT -> Data read : 20
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271)
@ 790: 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) @ 790: reporter [UVM/REPORT/SERVER]

Multiple Read and Write Transaction:

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

bit [7:0] rdata,rdata_m;

bit [7:0] dout_t;

bit [7:0] din_temp;

for(int i = 0; i < 5; i ++) begin

din_temp = $urandom_range(5, 20);

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

`uvm_info("SEQ", $sformatf("Write Tx to DUT -> Data Write : %0d",


rdata_m),UVM_NONE);

regmodel.temp_reg_inst.read(status,dout_t);

rdata_m = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("SEQ", $sformatf("Read Tx from DUT -> Data read : %0d",


dout_t),UVM_NONE);

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

7. Execution and Observation:


 When we execute the code, we see multiple transactions being performed.
 Each time, a random value is written to the register, and then the same value
is read back.
 The values printed on the console show that the written and read values
match, confirming the success of the transactions.

Randomize Method:

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

bit [7:0] rdata;

for(int i = 0; i < 10; i++)

begin

regmodel.temp_reg_inst.randomize();

regmodel.temp_reg_inst.write(status, regmodel.temp_reg_inst.temp.value);

`uvm_info("SEQ", $sformatf("Random Value :


%0d",regmodel.temp_reg_inst.temp.value), UVM_NONE);

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

uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 40


# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 230:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 40
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 270:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 123
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 270:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 123
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 310:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 213
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 310:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 213
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 350:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 128
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 350:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 128
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 390:
uvm_test_top.env.agent_inst.d [DRV] Data Write -> Wdata : 210
# KERNEL: UVM_INFO /home/runner/testbench.sv(218) @ 390:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Random Value : 210
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-
1.2/src/base/uvm_objection.svh(1271) @ 590: 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) @ 590: reporter [UVM/REPORT/SERVER]

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:

 The second method that we have is get reset.


 This will return the reset value that we specified for that register.
 For example, our temp rec.
 The reset value is 11.
 When we call get reset, this will return us the value of 11.
 .Get reset is used to retrieve the reset value for the register

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:

 The last method that we have is on reset.


 This is used to actually reset our model.
 So when we say resetting a model, what will happen is it will update the value of
both desired as well as mirror variable to the register reset value.
 Let us assume in our current case we have set the reset value to be 811.
 So when we call the reset method, both mirror as well as desired value will be
updated with this value.
 So these are the four methods which belongs to the reset operation.

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:

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

bit [7:0] rdata,rdata_m;

bit [7:0] rst_reg;

bit rst_status;

//////Check if register has reset value

rst_status = regmodel.temp_reg_inst.has_reset();

`uvm_info("SEQ", $sformatf("Reset Value added : %0b ", rst_status), UVM_NONE);

//////accessing default reset value

rst_reg = regmodel.temp_reg_inst.get_reset();

`uvm_info("SEQ", $sformatf("Register Reset Value : %0d ", rst_reg), UVM_NONE);

70 | P a g e
@shraddha_pawankar Date:26/04/2024

////////////////accessing mir and des before rst

rdata = regmodel.temp_reg_inst.get();

rdata_m = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("SEQ", $sformatf("Before Reset -> Mir : %0d Des : %0d ",


rdata_m_rdata), UVM_NONE);

///////////////mir and des value after rst

$display("--------------Applying Reset to register model ---------------");

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);

/////////////updating rst value

$display("--------------Updating register reset value and applying Reset --------------");

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]

Frontdoor and Backdoor Access


When we talk about accessing registers in a system, there are two main methods: front door
access and backdoor access.

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:

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

73 | P a g e
@shraddha_pawankar Date:26/04/2024

uvm_status_e status;

bit [7:0] rdata,rdata_m;

bit [7:0] dout_t;

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:

 start with front door Access


 First, we will only be going through implementation of our reg block and a
sequence because this is where we you will be finding the differences between front
door and a back door access.
 Rest of the code remains as it is what we've been using right from the start of the
methods.
 So in a case of a front door method, we have already added our register.
 In our design we only have a single register which is of size eight bit.
 We utilize configure function to match the property of a register that we have
implemented in our verification environment to the dut register.[Refer Register
class]
 Then we have an reg block which also remains same.Here we add the instance of a
register called the Build method.It will automatically configure different reg fields
that are present in a register.Then we need to call a configure method For each
instance of a register.We need to create a map and add the register to a map.Since

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

 So this is why we referred it as an Frontdoor Access, because we are accessing the


register content from the front of our dut or by applying the valid signals to the ports
of a dut back door will able to access the register content without applying a valid
signals to a duty.

Backdoor Access:

Code:
////////////////////creating reg block

class top_reg_block extends uvm_reg_block;

`uvm_object_utils(top_reg_block)

rand temp_reg temp_reg_inst;

function new (string name = "top_reg_block");

super.new(name, build_coverage(UVM_NO_COVERAGE));

endfunction

function void build;

add_hdl_path ("dut", "RTL");

temp_reg_inst = temp_reg::type_id::create("temp_reg_inst");

temp_reg_inst.build();

temp_reg_inst.configure(this,null);

temp_reg_inst.add_hdl_path_slice("tempin",0, 4); //reg name in rtl,starting


position,no.of bits wide

default_map = create_map("default_map", 0, 4, UVM_LITTLE_ENDIAN,0); //


name, base, nBytes

default_map.add_reg(temp_reg_inst, 'h0, "RW"); // reg, offset, access

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.

From Register Block Code:


Building the Path:
 To access the register, you need to specify its path, which includes the
instance name and the register name.
 You use two functions: "add_path" and "add_path_slice".
 For "add_path", you provide the instance name of your system and "RTL" as
the default argument.
 For "add_path_slice", you provide the register name, starting position, and size
of the register.

From Sequence Code:

class top_reg_seq extends uvm_sequence;

`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;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

bit [3:0] rdata;

///////frontdoor write

regmodel.temp_reg_inst.write(status, 4'hf, UVM_FRONTDOOR);

ref_data = regmodel.temp_reg_inst.get(); ///get the desired value

`uvm_info("REG_SEQ", $sformatf("Desired Value backdoor: %0d", ref_data),


UVM_NONE);

ref_data = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("REG_SEQ", $sformatf("Mirror Value backdoor: %0d", ref_data),


UVM_NONE);

////////////////backdoor read

regmodel.temp_reg_inst.read(status, rdata, UVM_BACKDOOR);

`uvm_info("REG_SEQ",$sformatf("Backdoor read",rdata),UVM_LOW);

///////////////////backdoor write

regmodel.temp_reg_inst.write(status, 4'he, UVM_BACKDOOR);

ref_data = regmodel.temp_reg_inst.get(); ///get the desired value

`uvm_info("REG_SEQ", $sformatf("Desired Value backdoor: %0d", ref_data),


UVM_NONE);

ref_data = regmodel.temp_reg_inst.get_mirrored_value();

78 | P a g e
@shraddha_pawankar Date:26/04/2024

`uvm_info("REG_SEQ", $sformatf("Mirror Value backdoor: %0d",


ref_data),UVM_NONE);

endtask

endclass

1. Declaration and Initialization:


 The top_reg_seq class extends uvm_sequence, indicating that it's a
sequence used for register access operations.
 It contains a reference to a top_reg_block object named regmodel, which
represents the register block being accessed.
2. Front Door Write:
 The sequence starts with a front door write operation, where data 4'hf is
written to the register instance temp_reg_inst.
 This is achieved using the write method of the register instance, specifying
the data and the access path (UVM_FRONTDOOR).
 The get method is then used to retrieve the value written to the register, and
it's printed along with its mirrored value.
3. Backdoor Read:
 Next, a backdoor read operation is performed using the read method of the
register instance.
 Unlike front door access, backdoor access does not require applying signals
to the DUT ports. Instead, it directly reads the data from the register.
 The read data is stored in the rdata variable and printed using a reporting
macro.
4. Backdoor Write:
 Following the backdoor read, a backdoor write operation is executed by
writing 4'he to the register instance.
 Again, this is achieved using the write method with the access path specified
as UVM_BACKDOOR.
 The written value is retrieved and printed, demonstrating successful
backdoor write access.
5. Explanation in UVM RAL Terms:
 In UVM RAL, front door operations involve accessing registers through the
DUT's ports, mimicking real-world interactions.
 Backdoor operations, on the other hand, directly access registers without
utilizing the DUT ports, which is useful for testing and debugging.
 In the sequence, the regmodel.temp_reg_inst represents the register
instance being accessed within the register block.
 By specifying the access path as either UVM_FRONTDOOR or
UVM_BACKDOOR, the sequence determines whether to use front door or
backdoor access.
 The sequence demonstrates that backdoor access allows reading and writing
to registers without applying valid signals to the DUT ports, making it
convenient for debugging and testing purposes.

79 | P a g e
@shraddha_pawankar Date:26/04/2024

PEEK and Poke Method:


Code:

//////////////////////////////////////creating reg block

class top_reg_block extends uvm_reg_block;

`uvm_object_utils(top_reg_block)

rand temp_reg temp_reg_inst;

function new (string name = "top_reg_block");

super.new(name, build_coverage(UVM_NO_COVERAGE));

endfunction

function void build;

temp_reg_inst = temp_reg::type_id::create("temp_reg_inst");

temp_reg_inst.build();

temp_reg_inst.configure(this,null);

temp_reg_inst.add_hdl_path_slice("tempin",0, 4); //reg name in rtl,starting


position,no.of bits wide

default_map = create_map("default_map", 0, 4, UVM_LITTLE_ENDIAN,0); // name,


base, nBytes

default_map.add_reg(temp_reg_inst, 'h0, "RW"); // reg, offset, access

default_map.set_auto_predict();

add_hdl_path ("dut", "RTL");

80 | P a g e
@shraddha_pawankar Date:26/04/2024

lock_model();

endfunction

endclass

/////////Sequence Class

class top_reg_seq extends uvm_sequence;

`uvm_object_utils(top_reg_seq)

top_reg_block regmodel;

uvm_reg_data_t ref_data;

function new (string name = "top_reg_seq");

super.new(name);

endfunction

task body;

uvm_status_e status;

bit [3:0] rdata;

bit [3:0] des, mir;

regmodel.temp_reg_inst.poke(status, 4'hf);

des = regmodel.temp_reg_inst.get();

mir = regmodel.temp_reg_inst.get_mirrored_value();

`uvm_info("SEQ", $sformatf("Write -> Des: %0d Mir: %0d", des, mir),


UVM_NONE);

$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();

`uvm_info("SEQ", $sformatf("Des: %0d Mir: %0d", des, mir), UVM_NONE);

endtask

endclass

Explaination:

1. Backdoor Access and Switch Plus W:


 Before utilizing peek and poke methods for backdoor access, it's essential to
ensure that the verification environment is enabled to access the registers.
This is typically done by adding the switch "+w" to the simulation run options.
2. Implementation of Registers:
 Registers are implemented with their associated fields, and their properties
are configured based on the hardware register specifications.
3. Register Block Setup:
 In the register block setup, each register instance is created, configured, and
added to the register block.
 The backdoor path for register access is specified using methods like
add_path_slice and add_path. This ensures that the register can be accessed
directly during backdoor operations.
4. Sequence Implementation:
 In the sequence, temporary variables are declared to store the desired and
mirrored values of the register after performing peek and poke operations.
 Poke Method: The poke method is used to write data to a register without
generating any transaction on the input or output ports of the DUT. This
method typically involves specifying the register instance and the value to be
written.
 After performing the poke operation, the sequence retrieves the
desired and mirrored values of the register and prints them for
verification.
 Peek Method: Conversely, the peek method is used to read data from a
register without generating any transaction on the DUT ports. Similar to
poke, it involves specifying the register instance and providing a variable to
store the read data.
 After peeking the register, the sequence retrieves the read data and
compares it with the desired and mirrored values for validation.
5. Difference Between Poke and Peek:
 The key difference between poke and peek lies in their operation:
 Poke: Writes data to a register without generating any transaction on
the DUT ports.
 Peek: Reads data from a register without generating any transaction
on the DUT ports.
6. Arguments of Poke and Peek:

82 | P a g e
@shraddha_pawankar Date:26/04/2024

 Both poke and peek methods typically require two arguments:


 The status of the transaction (e.g., success or failure).
 The data to be written (for poke) or the variable to store the read
data (for peek).

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.

COVERAGE COMPUTATION IN RAL MODEL


Code:

//////////////////////building reg model////////////////////////

class temp_reg extends uvm_reg;

`uvm_object_utils(temp_reg)

rand uvm_reg_field temp;

////////////////////////adding coverpoints

covergroup temp_cov;

option.per_instance = 1;

coverpoint temp.value[7:0]

bins lower = {[0:63]};

bins mid = {[64:127]};

bins high = {[128:255]};

83 | P a g e
@shraddha_pawankar Date:26/04/2024

endgroup

////////////////checking coverage and adding new method to covergroup

function new (string name = "temp_reg");

super.new(name,8,UVM_CVR_FIELD_VALS);

if(has_coverage(UVM_CVR_FIELD_VALS))

temp_cov = new();

endfunction

////////////////////////////// implementation of sample and sample_Values

virtual function void sample(uvm_reg_data_t data,

uvm_reg_data_t byte_en,

bit is_read,

uvm_reg_map map);

temp_cov.sample();

endfunction

virtual function void sample_values();

super.sample_values();

temp_cov.sample();

endfunction

84 | P a g e
@shraddha_pawankar Date:26/04/2024

///////////////user defined build function

function void build;

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:

1. Cover Group Setup:


 A cover group named temp_cov is defined within the temp_reg class. Cover
groups are used to collect coverage data for specific aspects of the design.
 The option.per_instance = 1; line specifies that each instance of temp_reg
will have its own instance of the temp_cov cover group.
 Within the cover group, a cover point is added for the temp field's value.
 The temp.value[7:0] syntax specifies that the cover point will cover the 8-bit
value of the temp field.
 Three bins are defined within the cover point:
 Lower: Covers values ranging from 0 to 63.
 Mid: Covers values ranging from 64 to 127.
 High: Covers values ranging from 128 to 255.

85 | P a g e
@shraddha_pawankar Date:26/04/2024

Manual Specification of Cover Points:


 It's emphasized that cover points need to be manually specified for each field (in this
case, the temp field) for which coverage computation is desired.

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

 In this implementation, the temp field is configured with a size of 8 bits,


read/write access, and other properties such as reset behavior and
randomization.

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.

/////////////////////creating reg block////////////////////

class top_reg_block extends uvm_reg_block;

`uvm_object_utils(top_reg_block)

rand temp_reg temp_reg_inst;

function new (string name = "top_reg_block");

super.new(name, build_coverage(UVM_NO_COVERAGE));

endfunction

function void build;

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);

temp_reg_inst.set_coverage(UVM_CVR_FIELD_VALS); //////enabling coverage for


specific reg instance

default_map = create_map("default_map", 'h0, 1, UVM_LITTLE_ENDIAN);


/name,base, nBytes

default_map.add_reg(temp_reg_inst, 'h0, "RW"); // reg, offset, access

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.

Working With Memories:


Important Note:

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

# 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
vif
# KERNEL: UVM_INFO /home/runner/testbench.sv(49) @ 10:
uvm_test_top.env.agent_inst.d [DRV] wr : 1 addr : 0 din : 4 dout : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(102) @ 50:
uvm_test_top.env.agent_inst.m [MON] Wr :1 Addr : 0 Din:4 Dout:0
# KERNEL: UVM_INFO /home/runner/testbench.sv(130) @ 50: uvm_test_top.env.s [SCO] Wr
:1 Addr : 0 Din:4 Dout:0
# KERNEL: UVM_INFO /home/runner/testbench.sv(133) @ 50: uvm_test_top.env.s [SCO]
Data Stored -> addr : 0 data : 4
# KERNEL: UVM_INFO /home/runner/testbench.sv(58) @ 70:
uvm_test_top.env.agent_inst.d [DRV] wr : 0 addr : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(102) @ 110:
uvm_test_top.env.agent_inst.m [MON] Wr :0 Addr : 0 Din:4 Dout:4
# KERNEL: UVM_INFO /home/runner/testbench.sv(130) @ 110: uvm_test_top.env.s [SCO]
Wr :0 Addr : 0 Din:4 Dout:4
# KERNEL: UVM_INFO /home/runner/testbench.sv(138) @ 110: uvm_test_top.env.s [SCO]
Test Passed
# KERNEL: UVM_INFO /home/runner/testbench.sv(249) @ 110:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Data read : 4
# KERNEL: -----------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(253) @ 110:
uvm_test_top.env.agent_inst.seqr@@trseq [SEQ] Data read : 18
# KERNEL: -----------------------------------------
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271)
@ 160: 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) @ 160: reporter [UVM/REPORT/SERVER]

Working on APB Protocol


1. Introduction to the Project Duties:
 Your project involves working with a series of registers within a duty.
 The goal is to use a register model to write data to all the registers and then
read back the data.

89 | P a g e
@shraddha_pawankar Date:26/04/2024

2. Utilizing the APB Protocol:


 Instead of using native ports commonly found with memory, you're utilizing
the Advanced Peripheral Bus (APB) protocol.
 With APB, you cannot directly read or write data to registers. Transactions are
required to update or retrieve data.
3. Description of the Duty:
 The duty consists of input ports and a single output port.
 Key signals include reset n (active low), clock select enable, and pwrite (mode
control pin).
 Address and data buses are both 32-bit wide.
4. Write Operation:
 To perform a write operation:
 Set select and write to 1.
 Specify the address of the register and the data to be written on the data
bus.
 In the next clock tick, set penable to 1 to update the register with the
specified data.
5. Read Operation:
 To perform a read operation:
 Set select to 1 and write to 0.
 Specify the address of the register to be read.
 In the next clock tick, set penable to 1.
 Wait for one clock tick to retrieve the requested data from the dut.
6. Handling Output:
 If there's a read operation in the output, wait until pready becomes high to
sample the read data.
7. Conclusion:
 This logic ensures that APB transactions are performed effectively on the
duty, allowing for the manipulation and retrieval of data from registers.

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:

1. reg [3:0] cntrl = 0; /// cntrl : [reg4 reg3 reg2 reg1]


2. reg [31:0] reg1 = 0; // datainput 1
3. reg [31:0] reg2 = 0; /// datainput 2
4. reg [31:0] reg3 = 0; /// datainput 3
5. reg [31:0] reg4 = 0; // datainput 4

Register Offset Address:

1. 'h0 : cntrl <= pwdata;


2. 'h4 : reg1 <= pwdata;
3. 'h8 : reg2 <= pwdata;
4. 'hc : reg3 <= pwdata;
5. 'h10 : reg4 <= pwdata;

Eda playground code: https://www.edaplayground.com/x/DVKX

91 | P a g e
@shraddha_pawankar Date:26/04/2024

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 vif
# 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 vif
# 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 vif
# 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 vif
# 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 vif
# 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 vif
# KERNEL: UVM_INFO /home/runner/testbench.sv(59) @ 30:
uvm_test_top.env.agent_inst.d [DRV] Mode : Write WDATA : 2 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(144) @ 70:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 2 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(184) @ 70: uvm_test_top.env.s
[SCO] DATA Stored Addr : 0 Data :2
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(59) @ 90:
uvm_test_top.env.agent_inst.d [DRV] Mode : Write WDATA : 15 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(144) @ 130:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 15 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(184) @ 130: uvm_test_top.env.s
[SCO] DATA Stored Addr : 0 Data :15
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(59) @ 150:
uvm_test_top.env.agent_inst.d [DRV] Mode : Write WDATA : 3 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(144) @ 190:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 3 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(184) @ 190: uvm_test_top.env.s
[SCO] DATA Stored Addr : 0 Data :3
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(59) @ 210:
uvm_test_top.env.agent_inst.d [DRV] Mode : Write WDATA : 14 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(144) @ 250:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 14 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(184) @ 250: uvm_test_top.env.s
[SCO] DATA Stored Addr : 0 Data :14
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(59) @ 270:
uvm_test_top.env.agent_inst.d [DRV] Mode : Write WDATA : 5 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(144) @ 310:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 5 ADDR : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(184) @ 310: uvm_test_top.env.s
[SCO] DATA Stored Addr : 0 Data :5
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(73) @ 330:
uvm_test_top.env.agent_inst.d [DRV] Mode : Read WDATA : 5 ADDR : 0 RDATA : 0
# KERNEL: UVM_INFO /home/runner/testbench.sv(150) @ 370:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 5 ADDR : 0 RDATA :
5
# KERNEL: UVM_INFO /home/runner/testbench.sv(192) @ 370: uvm_test_top.env.s
[SCO] Test Passed -> Addr : 0 Data :5
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(73) @ 390:
uvm_test_top.env.agent_inst.d [DRV] Mode : Read WDATA : 5 ADDR : 0 RDATA : 5
# KERNEL: UVM_INFO /home/runner/testbench.sv(150) @ 430:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 5 ADDR : 0 RDATA :
5
# KERNEL: UVM_INFO /home/runner/testbench.sv(192) @ 430: uvm_test_top.env.s
[SCO] Test Passed -> Addr : 0 Data :5
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(73) @ 450:

92 | P a g e
@shraddha_pawankar Date:26/04/2024

uvm_test_top.env.agent_inst.d [DRV] Mode : Read WDATA : 5 ADDR : 0 RDATA : 5


# KERNEL: UVM_INFO /home/runner/testbench.sv(150) @ 490:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 5 ADDR : 0 RDATA :
5
# KERNEL: UVM_INFO /home/runner/testbench.sv(192) @ 490: uvm_test_top.env.s
[SCO] Test Passed -> Addr : 0 Data :5
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(73) @ 510:
uvm_test_top.env.agent_inst.d [DRV] Mode : Read WDATA : 5 ADDR : 0 RDATA : 5
# KERNEL: UVM_INFO /home/runner/testbench.sv(150) @ 550:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 5 ADDR : 0 RDATA :
5
# KERNEL: UVM_INFO /home/runner/testbench.sv(192) @ 550: uvm_test_top.env.s
[SCO] Test Passed -> Addr : 0 Data :5
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/runner/testbench.sv(73) @ 570:
uvm_test_top.env.agent_inst.d [DRV] Mode : Read WDATA : 5 ADDR : 0 RDATA : 5
# KERNEL: UVM_INFO /home/runner/testbench.sv(150) @ 610:
uvm_test_top.env.agent_inst.m [MON] Mode : Write WDATA : 5 ADDR : 0 RDATA :
5
# KERNEL: UVM_INFO /home/runner/testbench.sv(192) @ 610: uvm_test_top.env.s
[SCO] Test Passed -> Addr : 0 Data :5
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-
1.2/src/base/uvm_objection.svh(1271) @ 790: 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) @ 790: reporter [UVM/REPORT/SERVER]

93 | P a g e

You might also like