Universal verification
methodology(UVM)
What is verification methodology
• Methodology is a systematic way of doing things with a set of standard rules and
guidelines
• Verification methodology means while doing verification we have to follow some
rules and regulations
• Different verification methodologies
• AVM-Advanced verification methodology
• RVM-Reference verification methodology
• OVM-Open verification methodology
• VMM-Verification methodology manual
• UVM-Universal verification methodology
What is Universal verification methodology
• It is a methodology for functional verification using system verilog
• It complete with a supporting library of sv code
• UVM was created by Accellera based on OVM version (Open verification methodology)
• UVM library contains: which can be used for shorthand notation of complex implementation
• Component classes for building testbench components like generator/driver/monitor etc.
• Reporting classes for logging, Factory for object substitution.
• Synchronization classes for managing concurrent process.
• Policy classes for printing, comparing, recording, packing, and unpacking of uvm_object based.
• TLM Classes for transaction level interface.
• Sequencer and Sequence classes for generating realistic stimulus.
• And Macros
Introduction
• UVM is a methodology based on SystemVerilog language and is not a language on its own.
• It enables efficiency in terms of reuse and is also currently part of IEEE 1800.2 working
group.
• Modularity and Reusability – The methodology is designed as modular components
(Driver, Sequencer, Agents , env etc) which enables reusing components across unit level to
multi-unit or chip level verification as well as across projects.
• Separating Tests from Testbenches – Tests in terms of stimulus/sequencers are kept
separate from the actual testbench hierarchy and hence there can be reuse of stimulus across
different units or across projects
• Simulator independent – The base class library and the methodology is supported by all
simulators and hence there is no dependence on any specific simulator
Continu,,,
• Sequence methodology-- gives good control on stimulus generation. There are several ways in
which sequences can be developed which includes randomization, layered sequences, virtual
sequences etc which provides a good control and rich stimulus generation capability.
• Config mechanisms--simplify configuration of objects with deep hierarchy. The configuration
mechanism helps in easily configuring different testbench components based on which
verification environment uses it and without worrying about how deep any component is in
testbench hierarchy
• Factory mechanisms--simplifies modification of components easily. Creating each components
using factory enables them to be overridden in different tests or environments without changing
underlying code base.
Difference sv and uvm
• SystemVerilog classes allow Object Orientated Programming (OOP) techniques to be applied to
testbenches. The UVM itself is a library of base classes which facilitate the creation of
structured testbenches.
• reporting mechanism- helps in debugging environment with many agents.here we can filter
and log messages with the help of verbosity .by changing the verbosity , printing of messages
can be controlled.severity of messages are fatal , error , warning , info , file name and line
number where file and line are default. Like sv we don't have to give display command again
and again within a class.
• Base class library - here we have inbuilt methods like compare, copy , print , reporting
mechanism , macros which extended class inherits since all the extended class are derived from
same base class.
Continu,,,
• Configuration class - config class is a database where we configure all the parameters needed
throughout the hierarchy using set and get .sv we don't not have configuration facility.
• Phasing - since all the components are derived from uvm_ component , phasing helps in
synchronisation of each and every component before proceeding to next phase.
• TLM - in SV mailbox is used for passing the message between component but here tlm that is
transaction level modelling which supports multiple languages and it is port to port connection
not like mailbox which is component to component connection.
Continu,,,
• Factory - here factory is class that manufactures components and objects which gives the ability
to modify and no of objects that makes TB hierarchy in more predictable manner.overriding of
components or objects becomes much more easy using factory concept using create() method
instead of new() method
• Virtual sequencer and sequences - for keeping the independency between TB writer and test
case writer .here test case writer don't have to worry about the path in order to start a sequence
which can not be done in SV.
• Reusable and more portable . No need to be dependent on test.able to change the objects from
top level .
UVM class hierarchy
uvm_void:
• It is the base class for all UVM classes.
• This class is the top of the class inheritance hierarchy.
• System verilog has no need for a class that serves as a base of all classes, but specman e need to
have a common root base class, so they added it to the UVM library, but it turns out it is not
needed in the UVM
uvm_object:
• The uvm_object class is the base class for all UVM data and hierarchical classes.
• Its primary role is to define a set of methods for such common operations
as create, copy, compare, print, and record
Contin…
uvm_transaction:
• The ~uvm_transaction~ is the root base class for UVM transactions, which, unlike
~uvm_components~, are transient in nature.
• It extends <uvm_object> to include a timing and recording interface.
• Simple transactions can derive directly from ~uvm_transaction~, while sequence-enabled
transactions derive from ~uvm_sequence_item~
uvm_report_object:
• The uvm_report_object provides an interface to the UVM reporting facility.
• Through this interface, components issue the various messages that occur during simulation.
• A report consists of an id string, severity, verbosity level, and the textual message itself.
• They may optionally include the filename and line number from which the message came.
uvm_transaction vs uvm_sequence_item
• uvm_transaction is the base class for modeling any transaction which is derived from
uvm_object .
• uvm_sequence_item is an extension of uvm transaction class.
• A sequence item is nothing but a transaction that groups some information together and also
adds some other information like: sequence id (provide id for sequence_item then easily
identify by the driver), and transaction id, etc.
• It is recommended to use uvm_sequence_item for implementing sequence based stimulus.
• Proper sequencer and Driver communication won't Happen if it is not extending from
sequence_item.
UVM ARCHITECTURE
UVM Architecture
Sequence_item:
• The sequence-item is written by extending the uvm_sequence_item, uvm_sequence_item inherits from the
uvm_object via the uvm_transaction class.
• Therefore uvm_sequence_item is of an object type.
• The sequence-item consist of data fields required for generating the stimulus.
• In order to generate the stimulus, the sequence items are randomized in sequences.
• Therefore data properties in sequence items should generally be declared as rand and can have constraints
defined.
• Data fields represent the following types of information,
1. Control Information – a type of transfer, transfer size, etc ex:rand bit wr;
2. Payload Information – data content of the transfer ex: rand bit [7:0] wdata
3. Configuration Information – mode of operation, error behavior, etc
4. Analysis Information – fields used to capture information from DUT, ex: output bit[7:0]rdata;
as analysis information fields will be used for capturing response, except these fields the other fields
can be declared as rand and can have constraints associated with it.
class mem_seq_item extends uvm_sequence_item;
//Control Information
rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
//Payload Information
rand bit [7:0] wdata;
//Analysis Information
bit [7:0] rdata;
//Utility and Field macros,
`uvm_object_utils_begin(mem_seq_item)
`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_int(wr_en,UVM_ALL_ON)
`uvm_field_int(rd_en,UVM_ALL_ON)
`uvm_field_int(wdata,UVM_ALL_ON)
`uvm_object_utils_end
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
//constaint, to generate any one among write and read
constraint wr_rd_c { wr_en != rd_en; };
endclass
Sequence_item example
Sequence:
• A sequence generates a series of sequence_item’s and sends it to the driver via sequencer,
Sequence is written by extending the uvm_sequence
• A uvm_sequence is derived from an uvm_sequence_item
• a sequence is parameterized with the type of sequence_item, this defines the type of the item
sequence that will send/receive to/from the driver.
class write_sequence extends uvm_sequence
#(mem_seq_item);
....
....
endclass
class mem_sequence extends uvm_sequence#(mem_seq_item);
`uvm_object_utils(mem_sequence)
//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
virtual task body();
req = mem_seq_item::type_id::create("req"); //create the req (seq item)
begin
(); start_item
assert(req.randomize()); //randomize the req
finish_item(req);
end
endtask
endclass
Sequence example
Sequencer
• The sequencer controls the flow of request and response sequence items between sequences and
the driver
• Sequencer and driver uses TLM Interface to communicate transactions
• uvm_sequencer and uvm_driver base classes have seq_item_export and seq_item_port defined
respectively, User needs to connect them using TLM connect method
driver.seq_item_port.connect(sequencer.seq_item_export);
• A sequencer can be written by extending the uvm_sequencer parameterized with the seq_item
type.
class mem_sequencer extends uvm_sequencer#(mem_seq_item);
`uvm_sequencer_utils(mem_sequencer)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_sequencer
class mem_sequencer extends uvm_sequencer#(mem_seq_item);
`uvm_object_utils(mem_sequencer)
//Constructor
function new(string name = "mem_sequencer");
super.new(name);
endfunction
endclass
Sequencer example
UVM Driver
• A driver is written by extending the uvm_driver
• uvm_driver is inherited from uvm_component, Methods and TLM port (seq_item_port) are defined for
communication between sequencer and driver
• The uvm_driver is a parameterized class and it is parameterized with the type of the request
sequence_item and the type of the response sequence_item
• UVM_Driver Methods
• get_next_item
• This method blocks until a REQ sequence_item is available in the sequencer.
• try_next_item
• This is a non-blocking variant of the get_next_item() method. It will return a null pointer if there is no
REQ sequence_item available in the sequencer.
• item_done
• The non-blocking item_done() method completes the driver-sequencer handshake and it should be
called after a get_next_item() or a successful try_next_item() call.
class mem_driver extends uvm_driver #(mem_seq_item);
virtual mem_if vif;
`uvm_component_utils(mem_driver) // Constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))
`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase // run phase
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); //respond_to_transfer(req);
drive();
seq_item_port.item_done();
end
endtask : run_phase// drive
virtual task drive();
req.print();
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
`DRIV_IF.addr <= req.addr;
if(req.wr_en) begin
`DRIV_IF.wr_en <= req.wr_en;
`DRIV_IF.wdata <= req.wdata;
end
end
endtask : drive
endclass : mem_driver
Driver example
Output Monitor
• The user-defined monitor is extended from uvm_monitor, uvm_monitor is
inherited by uvm_component
• Output monitor is a passive entity that samples the DUT signals through the virtual
interface and converts the signal level activity to the transaction level
• Monitor samples DUT signals but does not drive them
• The monitor should have an analysis port (TLM port) and a virtual interface
handle that points to DUT signals.
class mem_monitor extends uvm_monitor;
// Virtual Interface
virtual mem_if vif;
uvm_analysis_port #(mem_seq_item) item_collected_port;
// Placeholder to capture transaction information.
mem_seq_item trans_collected;
`uvm_component_utils(mem_monitor)
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
trans_collected = new();
item_collected_port = new("item_collected_port", this);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
// run phase
virtual task run_phase(uvm_phase phase);
item_collected_port.write(trans_collected);
endtask : run_phase
endclass : mem_monitor
Monitor class example
Agent
• a user-defined agent is extended from uvm_agent, uvm_agent is inherited by uvm_component
• An agent typically contains a driver, a sequencer, and a monitor
• Agents can be configured either active or passive
Active agent
• Active agents generate stimulus and drive to DUT
• An active agent shall consists of all the three components driver, sequencer, and monitor
Passive agent
• Passive agents sample DUT signals but do not drive them
A passive agent consists of only the monitor
• An agent can be configured as ACTIVE/PASSIVE by using a set config method, the default agent will be
ACTIVE. the set config can be done in the env or test.
// connect_phase
function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
class mem_agent extends uvm_agent;
//declaring agent components
mem_driver driver;
mem_sequencer sequencer;
mem_monitor monitor;
// UVM automation macros for general components
`uvm_component_utils(mem_agent)
// constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(get_is_active() == UVM_ACTIVE) begin
driver = mem_driver::type_id::create("driver", this);
sequencer = mem_sequencer::type_id::create("sequencer", this);
end
monitor = mem_monitor::type_id::create("monitor", this);
endfunction : build_phase
// connect_phase
function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
endclass : mem_agent
Agent with example
Scoreboard
• The user-defined scoreboard is extended from uvm_scoreboard, uvm_scoreboard is inherited by
uvm_component.
• the scoreboard will check the correctness of the DUT by comparing the DUT output with the
expected values
• the scoreboard will receive the transactions from the Monitors implemented inside agents
• Monitor and scoreboard will communicate via TLM ports and exports
• Scoreboard shall compare the DUT output values with,
• The golden reference values
• The values Generated from the reference model
Environment
• The user-defined environment is derived from uvm_env, uvm_env is inherited from
uvm_component.
• The environment is the container class, It contains one or more agents, as well as other
components such as the scoreboard, top-level monitor, and checker.
class mem_scoreboard extends uvm_scoreboard;
`uvm_component_utils(mem_scoreboard)
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
item_collected_export = new("item_collected_export", this);
endfunction: build_phase
// write
virtual function void write(mem_seq_item pkt);
$display("SCB:: Pkt recived");
pkt.print();
endfunction : write
endclass : mem_scoreboard
Scoreboard with example
class mem_model_env extends uvm_env;
mem_agent mem_agnt;
`uvm_component_utils(mem_model_env)
// new - constructor
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);
mem_agnt = mem_agent::type_id::create("mem_agnt", this);
endfunction : build_phase
endclass : mem_model_env
Environment class example
Test
• The user-defined test is derived from uvm_test, uvm_test is inherited from uvm_component.
• The test defines the test scenario for the testbench
• test class contains the environment, configuration properties, class overrides etc
• A sequence/sequences are created and started in the test
• The UVM testbench is activated when the run_test() method is called, the global run_test() task
should be specified inside an initial block.
• There can be many user-defined test cases.Among multiple test cases, a particular test case can be
selected and execute on two methods,
1. by specifying the test name as an argument to run_test();
example: run_test("mem_model_test");
2. by providing the UVM_TESTNAME command line argument
example: <SIMULATION_COMMANDS> +UVM_TESTNAME=mem_model_test
initial begin
run_test();
end
class mem_model_test extends uvm_test;
`uvm_component_utils(mem_model_test)
mem_model_env env;
mem_sequence seq;
function new(string name = "mem_model_test",uvm_component parent=null);
super.new(name,parent);
endfunction : new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = mem_model_env::type_id::create("env", this);
seq = mem_sequence::type_id::create("seq");
endfunction : build_phase
task run_phase(uvm_phase phase);
seq.start(env.mem_agnt.sequencer);
endtask : run_phase
endclass : mem_model_test
Test class example
Top
TestBench top is the module, it connects the DUT and Verification environment
components.
Typical Testbench_top contains,
• DUT instance
• interface instance
• run_test() method
• virtual interface set config_db
• clock and reset generation logic
• wave dump logic
module tbench_top;
//clock and reset signal declaration
bit clk;
bit reset;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
//creatinng instance of interface, inorder to connect DUT and testcase
mem_if intf(clk,reset);
//DUT instance, interface signals are connected to the DUT ports
memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata) );
//enabling the wave dump
initial begin
uvm_config_db#(virtual mem_if)::set(uvm_root::get(),"*","mem_intf",intf);
end
initial begin
run_test();
end
endmodule
Top_module example
uvm_component
• Quasi Static Entity (after build phase
it is available throughout the
simulation)
• Always tied to a given hardware(DUT
Interface) Or a TLM port
• Having phasing mechanism for
control the behavior of simulation
• uvm_component have two arguments,
a name and a uvm_component
parent.
• Components are instantiated at start
of simulation
uvm_object
• Dynamic Entity (create when needed,
transfer from one component to
other & then dereference)
• Not tied to a given hardware or any
TLM port
• No phasing mechanism
• uvm_object have one arguments i.e
name
• Objects are instantiated at run time
Difference between uvm_component and uvm_object
UVM Class Hierarchy
Message Report
Hierarchy and Phases
Name
UVM Architecture
UVM PHASES
Phases
• Depending of the testbench architecture, there will be several components and there needs to be
some ordered way of constructing these as well as executing
• Phases are not used in objects because objects wont present throughout the simulation, they just
created when they needed.
• Each components goes through predefined set of phases and it cannot proceed to the next phase
until all components finish their execution in current phase
• Phases are represented by callback methods, A set of predefined phases and corresponding
callbacks are provided in uvm_component. The Method can be either a function or task.
• Any class deriving from uvm_component may implement any or all of these callbacks, which
are executed in a particular order
• So, uvm phases acts as a synchronizing mechanism in the life cycle of simulation
• All phases can be grouped into 3 categories
1.Build_phase 2. Run-time phase 3.Clean-up phase
Phases Top down
execution
Paraalel
execution
Bottom to up
execution
Build_phase
• Build_phase are executed at start of simulation
• Purpose is to construct the testbench components
• All build_phase methods are functions, therefore execute in zero-simulation time
• Build_phase is top-down approach
Connect_phase
• Used to make TLM connections between components
• It has to occur after build_phase
• It works from bottom to up in testbench component hierarchy
Virtual function void build_phase(uvm_phase
phase);
super.build_phase(phase);
endfunction
Virtual function void connect_phase(uvm_phase phase);
super.build_phase(phase);
driv.seq_item_port.connect(seqr.seq_item_export)
endfunction
end_of_elaboration
• Used to make any final adjustments to tb connectivity, tb configuration before simulation starts
• Used to display environment hierarchy
• This phase executes from bottom_up
2.Run_Phase
• The UVM Testbench stimulus is generated and executed during the run time phases which
follows the build phases.
• Run phase was present in OVM as well but additional other phases were added to UVM to give
finer run-time for tests, scoreboard and other components.
virtual function void end_of_elaboration_phase(uvm_phase
phase);
uvm_top.print_topology();
uvm_report_info(get_full_name(),”end_of_elaboration”,uvm_LO
W);
endfunction
task run_phase(uvm_phase phase);
::::::::::
endtask
start_of_simulation
• It is a function which occurs before the time consuming part of the testbench begins.
• It is intended to be used for displaying banners; Testbench topology; or configuration
information. It is called in bottom up order.
run_Phase:
• The run phase occurs after the start_of_simulation phase and is used for the stimulus generation
and checking activities of the Testbench.
• The run phase is implemented as a task, and all uvm_component run tasks are executed
in parallel.
• Transactors such as driver and monitor will nearly always use this phase.
virtual function void start_of_simulation phase(uvm_phase
phase);
uvm_report_info(get_full_name(),”start_of_simulation”,uvm_LO
W);
endfunction
task run_phase(uvm_phase phase);
::::::::::
endtask
pre_reset:
• pre_reset phase starts at the same time as the run phase.
• Its purpose is to take care of any activity that should occur before the reset.
• E.g. waiting for a power signal to go active.
reset:
• Specially for DUT or Interface specific reset behavior.
• This phase would be used to generate reset to put the DUT/Interface into a default state.
post_reset:
• This phase is intended for any activity required just after the reset phase.
task reset_phase(uvm_phase phase);
::::::::::
endtask
task pre_reset_phase(uvm_phase
phase);
::::::::::
endtask
task post_reset_phase(uvm_phase
phase);
::::::::::
endtask
pre_configure:
• This phase is intended for anything that is required to prepare for the DUT configuration
process after the DUT is out of reset.
configure:
• configure phase is used to put the DUT into a known state before the stimulus could be applied
to the DUT.
• For example – programming the control registers of the device for a particular test scenario.
post_configure:
• This phase is intended to wait for the effect of the configuration to propagate through the DUT.
task configure_phase(uvm_phase
phase);
::::::::::
endtask
task pre_configure_phase(uvm_phase
phase);
::::::::::
endtask
task post_configure_phase(uvm_phase
phase);
::::::::::
endtask
pre_main:
• pre_main is used to ensure that all the components needed to generate the stimulus are ready to
do so.
main:
• main phase is where the stimulus specified by the Test case is generated and applied to the
DUT. It completes in two conditions: One is the stimulus gets exhausted and another is when
timeout occurs. Sequences are started in this phase to generate the stimulus.
post_main:
• Used for any final act after the main phase.
task main_phase(uvm_phase phase);
::::::::::
endtask
task pre_main_phase(uvm_phase phase);
::::::::::
endtask
task post_main_phase(uvm_phase phase);
::::::::::
endtask
pre_shutdown:
• This phase is acts like a buffer to apply any stimulus before the shutdown phase starts.
shutdown:
• The shutdown phase is to ensure that the effects of stimulus generated during the main phase
has propagated through the DUT and that the resultant data has drained away.
• It might also be used to execute the time consuming sequences that read status registers.
post_shutdown:
• post_shutdown is intended for any final activity before exiting the run phase. After it UVM
Testbench starts the cleanup phase.
task shutdown_phase(uvm_phase
phase);
::::::::::
endtask
task pre_shutdown_phase(uvm_phase
phase);
::::::::::
endtask
task post_shutdown_phase(uvm_phase
phase);
::::::::::
endtask
Clean up Phase:
• The clean up phases are used to extract information from Scoreboards and Functional
Coverage Monitors to determine whether the test case has passed and/or reached its coverage
goals immediately it will be cleaned the stimulus, it will give space for another stimulus
• The clean up phases are implemented as functions and therefore take zero time to execute.
• They work from the bottom to the top of the component hierarchy.
extract:
• The extract phase is used to retrieve and process information from Scoreboards and Functional
Coverage Monitors.
• This may include the calculation of statistical information used by the report phase.
• This phase is usually used by Analysis side components.
check:
• This phase is also used by the Analysis Components.
• This phase is used to check if the DUT behaved correctly and to find any error that
may have occurred during the stimulus execution.
report:
• The report phase is used to display the results of the simulation to the standard output
or to write the results to file.
• This phase is usually used by Analysis Components.
final:
• The final phase is used to complete any other outstanding actions that the Testbench
has not already completed.
How UVM Phasing is triggered?
• To start a UVM Testbench, the run_test() method has to be called from the static part of the
Testbench.
• It is usually called from within an initial block in the top level module of the Testbench.
• Once the run_test() method is called, it constructs the root componentof the UVM environment
& then triggers/initiates the UVM Phasing process.
• A phase starts only when all components in the previous phase have dropped their objections.
• A phase continues to execute until all components have dropped their objections in the current
phase
// Top level Testbench module
module top_tb; .... .... // UVM start up:
initial begin
uvm_config_db #(virtual bus_if)::set(null, "*", "BUS_vif" ,
BUS);
run_test("bidirect_bus_test");
end endmodule: top_tb
• The run_test() method can be passed with a string argument, which in the above code
is “bidirect_bus_test”, to define the default type name which is used as the root node of
the Testbench Hierarchy.
• In addition, run_test() method also checks for a command line plusarg
called UVM_TESTNAME and uses that plusarg string to lookup a factory registered
uvm_component to override any default type name.
• Hence to execute the “bidirect_bus_test” using command line plusarg, we’ve to use the
following command line:
• % <simulator executable> +UVM_TESTNAME=bidirect_bus_test
// Top level Testbench module
module top_tb; .... .... // UVM start up:
initial begin
uvm_config_db #(virtual bus_if)::set(null, "*", "BUS_vif" ,
BUS);
run_test("bidirect_bus_test");
end endmodule: top_tb
Which phase takes more time and why ?
• Run phase takes more time because they are the major phases to consume simulation time.
• The time taken for each test can be different, because all tests have different aspects of
design
Phase Synchronization
• By default all components allow all other components to complete a phase before all
components move to next phase
VIP1
VIP2
VIP3
Reset configure main shutdown
Reset
Reset
configure
configure
main
main
shutdown
shutdown
time
Note:In Phase domain each component phases are independent of another component phase- do not use phase domain
How simulation ends in UVM methodology?
• UVM has a phased execution which consists of a set of build phases, run phases and check
phases.
• The run() phase is where the actual test simulation happens and during this phase every
component can raise an objection in beginning and hold it until it is done with its activity.
• Once all components drops the objection, the run() phase completes and then check() phase of all
components execute and then the test ends.
• This is how a normal simulation ends, but there are also controls on simulation timeouts to
terminate the run() phase if some component hangs due to a bug in design or testbench.
• When the run() phase starts, a parallel timeout timer is also started.
• If the timeout timer reaches one of the specified timeout limits before the run() phase completes,
the run() phase will timeout, an error message will be issued and then all phases post run() will
get executed and test ends after that.
UVM REPORTING
Why we go for uvm reporting
• Verilog $display doesn’t allow filtering and control of messages.
• Changing the verbosity on the command line doesn’t require that u recompile and re-elaborate
the design to observe different trace messages
• If we are using verilog system tasks we need to re-compile the whole RTL again to activate it
which consumes time, energy & resources.
• Uvm reporting services are built into all components and are derived from a parent
uvm_report_object class
• UVM Reporting or Messaging has a rich set of message-display commands & methods to alter
the numbers & types of messages that are displayed without re-compilation of the design.
reporting
• UVM Reporting or Messaging has a rich set of message-display commands & methods to
alter the numbers & types of messages that are displayed without re-compilation of the design.
• UVM Reporting also includes the ability to mask or change the severity of the message to adapt
the required environment condition.
• UVM Reporting has the concepts of Severity, Verbosity and Simulation Handing Behavior.
Each of them can be independently specified and controlled.
• Components are inherited from uvm_report_object, hence components already have methods
and functions to display messages
Concepts of reporting
• Severity
• Severity indicates importance
• Examples are Fatal, Error, Warning & Info
• Verbosity
• Verbosity indicates filter level
• Examples are None, Low, Medium, High, Full & Debug
• Simulation Handling Behavior
• Simulation handling behavior controls simulator behavior
• Examples are Exit, Count, Display, Log, Call Hook & No Action
• Simulation Handling Behavior in-fact is the Action taken by the Simulator which is
dependent on Severity being produced by the Verification Environment. We’ll see
more details shortly about it.
Reporting methods
• Four basic reporting functions that can be used with different verbosity levels
• id -- a unique id to form a group of messages.
• message -- The message text
• verbosity -- the verbosity of the message, indicating its relative importance. If this number is
less than or equal to the effective verbosity level, then the report is issued, subject to the
configured action and file descriptor settings.
• filename/line -- If required to print filename and line number from where the message is
issued, use macros, `__FILE__ and `__LINE__.
virtual function void uvm_report_info
(string id,string message,int verbosity=UVM_MEDIUM,string filename="",int line=0)
virtual function void uvm_report_warning
(string id,string message,int verbosity=UVM_MEDIUM,string filename="",int line=0)
virtual function void uvm_report_error(string id,string message,int verbosity=UVM_LOW, string filename="",int lin
e=0)
virtual function void uvm_report_fatal (string id,string message,int verbosity=UVM_NONE, string filename="",int li
ne=0)
Reporting method examples
• Four basic reporting functions that can be used with different verbosity levels
• UVM has six levels of verbosity with each one represented by an integer.
• UVM verbosity level is required only for uvm_info, cant be used for uvm_warning,
uvm_error, uvm_fatal
uvm_report_info (string id,string message,int verbosity=UVM_MEDIUM,string filename="",int line=
0)
uvm_report_warning (string id,string message,int verbosity=UVM_MEDIUM,string filename="",int l
ine=0)
uvm_report_error (string id,string message,int verbosity=UVM_LOW, string filename="",int line=0)
uvm_report_fatal (string id,string message,int verbosity=UVM_NONE, string filename="",int line=0)
uvm_report_info (get_type_name (), $sformatf ("None level message"), UVM_NONE);
uvm_report_info (get_type_name (), $sformatf ("Low level message"), UVM_LOW);
uvm_report_info (get_type_name (), $sformatf ("Medium level message"),
UVM_MEDIUM);
uvm_report_info (get_type_name (), $sformatf ("High level message"), UVM_HIGH);
uvm_report_info (get_type_name (), $sformatf ("Full level message"), UVM_FULL);
uvm_report_info (get_type_name (), $sformatf ("Debug level message"),
UVM_DEBUG);
uvm_report_warning (get_type_name (), $sformatf ("Warning level message"));
uvm_report_error (get_type_name (), $sformatf ("Error level message"));
uvm_report_fatal (get_type_name (), $sformatf ("Fatal level message"));
uvm_actions
• uvm_action are used to control the simulation behavior
• These methods associate the specified action or actions with reports of the given severity, id,
or severity-id pair.
• Following are the actions defined:
UVM_NO_ACTION -- Do nothing
UVM_DISPLAY -- Display report to standard output
UVM_LOG -- Write to a file
UVM_COUNT -- Count up to a max_quit_count value before exiting
UVM_EXIT -- Terminates simulation immediately
UVM_CALL_HOOK -- Callback the hook method .
Messages Verbosity:
• Fundamentally the Verbosity level describes how verbose a Testbench can be.
• The default Verbosity is UVM_MEDIUM.
• There are different Verbosity level being supported by UVM.
• In case of default Verbosity level i.e. UVM_MEDIUM, any messages with UVM_HIGH or
above are filtered out.
UVM_NONE 0
UVM_LOW 100
UVM_MEDIUM
(Default)
200
UVM_HIGH 300
UVM_FULL 400
UVM_DEBUG 500
Highest priority messages UVM_NONE
lowest priority messages UVM_DEBUG
How to change verbosity settings
• Verbosity settings can be modify by using command line switches and method calls
1. Changing verbosity using Cmd_line switch
• The advantage of cmd_line switches is that if the design has been compiled into a simulation
executable
• The design can be re-simulated using a different verbosity setting without recompiling the
design and testbench
• The most common option is cmd_line switch +UVM_VERBOSITY= <verbosity>
• Where <verbosity> can be anyone of the following UVM_NONE, UVM_LOW,
UVM_MEDIUM, UVM_HIGH, UVM_DEBUG
• Changing the verbosity for specific component using cmd_line option
+uvm_set_verbosity=<comp>,<id>,<verbosity>,<phase>
• Change the verbosity to UVM_LOW for all components in the agent
simv +UVM_TESTNAME=test1
+uvm_set_verbosity=uvm_test_top.e.agnt.*,_ALL_,UVM_LOW,run
Changing verbosity using method calls
• Using verbosity method calls that selectively change verbosity settings for specific components or
for entire component hierarchy
• function void set_report_verbosity_level (int verbosity_level)
function void set_report_severity_action (uvm_severity severity,uvm_action action)
function void set_report_id_action (string id,uvm_action action)
function void set_report_severity_id_action (uvm_severity severity,string id,uvm_action action)
Using get_type_name, get_full_name, get_name
• There are three built in methods which is used to call inside the reporting macros
• They are get_name(), get_type_name(), get_full_name()
• get_name(): returns the name of the object
`uvm_error(get_name(), “driver“) // :drv [D] driver
• get_full_name(): returns the full hierarchical name of this object
`uvm_error(get_full_name(), “driver“) // :top.test.env.agent1.drv[DRV]driver
• get_type_name(): returns the type name of the object
`uvm_error(get_type_name(), “driver“) // :drv [DRV]driver
Config_db
Configuration database
• Config db allows passing of objects and data to various components in the testbench.
• It is built on top of the UVM resource database, uvm_resource_db
• uvm_resource_db is a data sharing mechanism where hierarchy is not important.
• The database is essentially a lookup table which uses a string as a key and where you can add
and retrieve entries.
• resource database makes use of type-parameterized classes.
• This means that T in the class header must be replaced by the actual type of the resource you
want to work with.
• Each type of resource, therefore, gets its own specialized class.
class uvm_resource_db#(type T=uvm_object)
Continu,,
• The uvm_config_db class is the recommended way to access the resource database.
• A resource is any piece of information that is shared between more than one component or object.
• We use uvm_config_db::set to put something into the database and uvm_config_db::get to
retrieve information from the database.
• The uvm_config_db class is parameterized, so the database behaves as if it is partitioned into
many type-specific "mini databases."
• There are no limitations on the the type - it could be a class, a uvm_object, a built in type such as
a bit, byte, or a virtual interface.
• There are two typical uses for uvm_config_db.
• The first is to pass virtual interfaces from the DUT to the test, and the second is to pass
configuration classes down through the testbench.
Functions in resource_db
• There are several common functions of the uvm_resource_db class that allow you to add or
retrieve data.
Methods Description
get_by_type This function gets the resource by the type specified by the parameter so the only argument is the
scope.
get_by_name This function gets a resource by using both the scope and name given when it was added to the
database.
set This function creates a new resource in the database with a value, scope, and name that will be
used for retrieval.
read_by_name This function locates a resource by scope and name and returns the value through an output
argument.
read_by_type This function locates the resource using only the scope as a lookup and returns the value through
an output argument.
write_by_name This function locates the resource by scope and name and writes the value to it. If the resource
does not exist then it will be created like the set function.
write_by_type This function locates the resource by the scope and writes the value to it. If the resource does not
exist it is created.
WHEN IS THE CONFIGURATION DATABASE USED?
• The uvm_config_db is used when hierarchy is important.
• With the uvm_config_db, the user not only can add an object to the database, but can also specify, with
great detail, the level of access to retrieval by specifying the hierarchy
• The classic example of uvm_config_db usage is with sharing a virtual interface.
• A SystemVerilog interface is instantiated at the top level of the testbench and connects to the ports of
the device under test (DUT).
• For the UVM testbench to be able to drive to driver or monitor this interface, it needs to have access
to it.
• The various interface instantiations can be added to the database with the access level controlled, since
it can then be retrieved by the appropriate component only if it is in the specified hierarchy.
• Virtual interfaces are not the only use for the configuration database. Any object can be stored and
retrieved.
• Other common uses of the configuration database include sharing configuration objects or setting
whether an agent is active or passive.
HOW IS DATA STORED AND RETRIEVED?
• configdatabase, there are only two functions that are most commonly used with the configuration
database:
• • set – adds an object to the uvm_config_db
• • get – retrieves an object from the uvm_config_db
• Note that all the methods of the uvm_config_db class are static so they must be called with the scope
resolution operator, as is the case with the uvm_resource_db.
• Once again, type parameterization is used so the actual type for the resource, T, must be given.
• Also noteworthy, the default parameter type of the uvm_resource_db is uvm_object, whereas the
default type for the uvm_config_db is int
class uvm_config_db#(type T=int) extends uvm_resource_db#(T)
Contin……
• set() function has four arguments
• The get() function which is used to retrieve items from the database.
• It is important to note that objects are not removed from the database when you call get().
• The actual variable is passed in as an inout formal function argument and so is performed as a
copy-in-copy-out operation.
Static function void set(uvm_component cntxt, string inst_name, string field_name, Tvalue)
cntxt: The context is the hierarchical starting point of where the database entry is accessible.
string inst_name:The instance name is the hierarchical path that limits accessibility of the
database entry.
String field_name:The field name is the label used as a lookup for the database entry.
Tvalue: The value to be stored in the database of the parameterized type. By default the type is int.
Static function void get(uvm_component cntxt, string inst_name, string field_name,
inoutTvalue)
cntxt:The context (cntxt) is the starting point for the search.
string inst_name: The instance name in this case can be an empty string since it is relative to the
context
String field_name:The field name is the label given when the object was added to the database
Contin……
• An interface has been instantiated in the top level and now needs to be added to the uvm_config_db
using the set()function.
• The most basic way to do this is to use the set() function and allow the virtual interface to be widely
accessible from anywhere within the testbench.
• The first argument is the context (cntxt) which is the starting point of the lookup search.
• The example uses uvm_root::get() to acquire the top-level so the search will start at the top of the
hierarchy in this case.
• Normally "this" would be used as the context if the call to set() is within a class, but to set the virtual
interface correctly in the database, the code has to be placed inside a module, so there would be no
class context.
• The second argument is the instance name. In this example, the interface is being made globally
available amongst all components so the wildcard, "*", is used.
• The third argument is the field name which is a label used for lookup.
• Finally, the value argument is the actual instance of the interface.
uvm_config_db#(virtual tb_intf)::set(uvm_root::get(),"*",
"dut_intf", vif
Contin……
• In most cases, you do not want to make a database entry globally available.
• Since a global scope is essentially a single namespace, it makes reuse more difficult if everything is
stored in this scope.
• To restrict its access, use the hierarchical path in conjunction with the wildcard character as shown
below
• Adding other objects into the uvm_config_db is just as straightforward as adding a virtual interface
• The important thing to remember is that each entry needs a unique field name or label (if the global
scope is being used), or the path needs to be limited in such a way that non-unique labels do not
conflict as the scopes are now limited to specific areas of the naming hierarchy
uvm_config_db#(TYPE)::set(this,"*.path","label",
value)
Contin……
• uvm_config_db: a virtual interface, an integer value, and a configuration object. Also, there is
a generic call to the get() function.
• To retrieve the integer value the label would be "retry_count" and the value stored in this
entry would be assigned to the rty_cnt property in the object that is calling the get() function.
Config database methods
• set()
• get()
• exists()
• wait_modified()
• Convenience tasks
set()
• Static function of the class uvm_Config_db to set a variable in the configuration database
static function void set ( uvm_component cntxt, string inst_name, string field_name, T
value);
virtual function void build_phase (uvm_phase phase);
... uvm_config_db #(int) :: set (null, "uvm_test_top.m_env.m_apb_agent", "cov_enable", 1); ... endfunction
// Set virtual interface handle under name "apb_vif" available to all components below uvm_test_top,
indicated by the *
uvm_config_db #(virtual apb_if) :: set (null, "uvm_test_top.*", "apb_vif", apb_if);
// Set an int variable to turn on coverage collection for all components under m_apb_agent
uvm_config_db #(int) :: set (null, "uvm_test_top.m_env.m_apb_agent.*", "cov_enable", 1);
// Consider you are in agent's build_phase then you may achieve the same effect by
uvm_config_db #(int) :: set (this, "*", "cov_enable", 1);
• Static function of the class uvm_Config_db to set a variable in the configuration database
static function bit get ( uvm_component cntxt, string inst_name, string field_name, T
value);
// Get virtual interface handle under name "apb_vif" into local virtual interface handle at m_env level
uvm_config_db #(virtual apb_if) :: get (this, "*", "apb_vif", apb_if);
// Get int variable fails because no int variable found in given scope uvm_config_db #(int) :: get (null,
"uvm_test_top", "cov_enable", cov_var);
get()
static function bit exists ( uvm_component cntxt, string inst_name, string field_name, bit
spell_chk);
exists()
// Check if interface handle exists at the given scope
if (! uvm_config_db #(virtual apb_if) :: exists (this, "*", "apb_vif"))
`uvm_error ("VIF", "Could not find an interface handle",
UVM_MEDIUM)
Convenience tasks
static task wait_modified ( uvm_component cntxt, string inst_name, string field_name);
class my_agent extends uvm_agent;
virtual task run_phase (uvm_phase phase); ... // Waits until loopCount variable gets a new
value uvm_config_db #(int) :: wait_modified (this, "", "loopCount");
endtask endclass
class my_env extends uvm_env; my_agent m_apb_agent;
virtual task main_phase (uvm_phase phase);
... // Update loopCount variable in database
for (int i = 0; i < N; i++) begin ... uvm_config_db #(int) :: set (this, "m_apb_agent", "loopCount", i);
end
endtask endclass
wait_modified()
typedef uvm_config_db #(uvm_bitstream_t) uvm_config_int; typedef
uvm_config_db #( string) uvm_config_string; typedef uvm_config_db
#(uvm_object) uvm_config_object; typedef uvm_config_db
#(uvm_object_wrappet) uvm_config_wrapper;
FACTORY CONCEPT
Introduction
• As per the recommended UVM methodology, we should never construct new components
and/or transactions using new() class constructor.
• Instead, it is recommended that – we should make calls to a look-up table to create the requested
components and transactions.
• This special look-up table is called “Factory” in UVM. Entries to this look-up table are made
by registering the components and/or transactions while defining them.
• To create a component/transaction using Factory, create() method is used.
• The purpose of factory in UVM is to change the behaviour of the testbench without any
change in code or without any compilation.
• Basically it's for overriding purpose where you can override any object/component or class
into the another without any further modification.
Contin…
• UVM Factory facilitates an object of one type to be substituted with an object of derived type
without having to change the structure of the Testbench or modify the Testbench code.
• This behavior is called “overriding” and there are following types of overriding is possible
with UVM Factory
1. Type Overriding(set_type_override or Global override)
2. Instance Overriding(set_inst_override)
• Overriding helps to replace one transaction/sequence with another to generate new scenarios or
conditions without making any change in the Testbench structure/code.
• Similarly, a new version of the components can be brought into the Testbench without any
change in the structure of the Testbench & beauty of all this is that – it happens all the fly at the
run time.
• Registering uvm_object with factory
`uvm_object_utils(sequence)
• Using create() to instantiate objects
• <type>::type_id::create(“<name>”);
seq=sequence::type_id::create(“seq”);
this keyword not used for objects, objects
doesn’t have any hierarchy
• Registering uvm_component with factory
`uvm_component_utils(monitor)
• Using create() to instantiate components
• <type>::type_id::create(“<name>”, <parent>);
mon=monitor::type_id::create(“mon”, this);
• this keyword is required, because testbench
component have a hierarchy
• this keyword represents parent
Factory registrations for object and component
Why do we need factory?
• Consider a case in which you have a agent and driver, monitor are instantiated inside the agent.
• Now you want to change the base driver with the extended driver, you can the extend the base
driver and create a new driver class.
• Now to use this extended driver class you need to change the code in the agent class also
• e.g new instantiation of the extended driver class, if not then again you need to extend the agent
class and use extended driver class, which is too much of overhead
• To avoid this we use factory. In factory by registering the driver class we can now override the
base driver with extended driver without changing the code in the agent class.
Components/objects creating using new()
calling the new() is not recommended and limits reuse
Class driver extends uvm_component;
`uvm_component_utils(driver)
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
::::::
virtual task driver_transfer();
::::::
endclass
Class agent extends uvm_component;
`uvm_component_utils(agent)
driver my_drv;
function new(string name, uvm_component parent);
super.new(name,parent);
my_drv=new(“my_drv”, this);
endfunction
::::::
virtual task driver_transfer();
::::::
endclass
Suppose user wants to add extra data or trying to override the
driver task using new()
Class my_project extends driver;
`uvm_component_utils(my_project)
function new(string name, uvm_component
parent);
super.new(name,parent);
endfunction
::::::
virtual task driver_transfer();
Super.drive_transfer();
::::::
endclass
• Agent instantiates previous driver not the my_project driver
• To add this m_project driver in agent need to extend the
agent class & potentially other clases need to instantiate dis
driver
• Sine driver, agent clases need to be extended dis makes
code modification all over the testbench
• Uvm factory introduces overriding option by using factory concept overriding the exact
driver from outside the agent instead of using new(), user needs to use create()
• The main difference between new() and create() is, by using create we can overriding the
code without any modification ,but by using new() its not possible
Components/objects creating using create()
calling the create()
Class driver extends uvm_component;
`uvm_component_utils(driver)
function new(string name, uvm_component
parent);
super.new(name,parent);
endfunction
:::::virtual task driver_transfer():
endclass
Class agent extends uvm_component;
`uvm_component_utils(agent)
driver my_drv;
function new(string name, uvm_component
parent);
super.new(name,parent);
my_drv=new(“my_drv”, this);
endfunction
::::::
virtual task driver_transfer();
::::::
endclass
• We will register the components & objects using type_id and while creating the instances of
components or objects we will create them by the factory method “create” which is called
using type_id
• type_id of driver is overridden with child_driver type_id, inside agent create method is called with child_driver type_id hence the
instance of child_driver is created.
• With the help of the factory, we can override the type of underlying components or objects from the top-level component without having
to edit the code.
class test extends uvm_test;
....
function void build_phase(uvm_phase phase);
....
// calling factory overriding method to override the type_id of driver with child_driver
type_id
set_type_override_by_type(driver::get_type(),child_driver::get_type());
endfunction
Class child_driver extends driver;
`uvm_component_utils(child_driver)
function new(string name, uvm_component
parent);
super.new(name,parent);
endfunction
::::::
virtual task driver_transfer();
Super.drive_transfer();
::::::
Endclass
The code inside the agent, the
driver instance will be created as
the create method is called with
type_id of driver
later in one test case suppose we
need to override this particular
driver with child_driver, we can
override it from top-level
component (test) using the factory
as shown below.
Example for factory reuse
• Consider a case when you are moving from one project to another.
• For example, USB 2.0 to USB 3.0. You understand that you leverage everything from USB 2.0
testbench and use it as USB 3.0 testbench with exception of driver.
• It will so much of a pain to replace each instance of driver class.
• Now, this is where factory comes to your rescue.
• When you override the old driver class with the new driver class using factory method
set_type_override, all instances of old driver class will be replaced by the new instance of USB3.0
driver class.
Factory methods
set_type_override_by_type
set_inst_override_by_type
Instance override
Type override
set_type_override_by_name
set_inst_override_by_name
set_type_override_by_type(original_type::get_type(), substitute_type::get_type(),
replace=1)
factory.set_type_override_by_name(“original_name",
“substitute_name") ;
<original_type>::type_id::set_inst_override(<substitute_type>::get_type(),
<path_string>);
factory.set_inst_override_by_name("a_packet","bad_pac
ket", "*");
Type Overriding:
• In type-override, overriding the current class type(original class) with other class type(derived class
or substitute_type)
• where “replace” is a bit which is when set equals to 1, enables the overriding of an existing
override else existing override is honoured.
<original_type>::type_id::set_type_override(<substitute_type>::get_type(),
replace);
class my_test extends uvm_test;
`uvm_component_utils(my_test) env e;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_driver::type_id::set_type_override(my_updated_driver::get_typ
e(),1); (or)
//set_type_overidde_by_type(my_driver::get_type(),my_updated_d
river());
e = env::type_id::create("e", this);
endfunction: build_phase
task run_phase (uvm_phase phase); ... ... ...
endtask: run_phase
endclass: my_test
//Overriding
my_driver class with
my_updated_driver
• Note here is the order of 2 commands
i.e. set_type_override() to be placed
before the create() command inside the
build_phase() of the my_test class.
• Only with this order of commands the
substitution will get into effect.
• In case the order is reversed, original
driver in the code i.e. my_driver will be
constructed instead of the intended driver
i.e. my_updated_driver.
Instance override
• In Instance Overriding, as name indicates it substitutes ONLY a particular instance of the
component OR a set of instances with the intended component.
• The instance to be substituted is specified using the UVM component hierarchy.
path should be agent1 * check only below components
path should be agent1* check agent1 and below components
Agent_top
agent1
agent2
drv2 mon2 seqr2
drv1 mon1 seqr1
<original_type>::type_id::set_inst_override(<substitute_type>::get_type(), <path_string>);
Instance Override
class my_test extends uvm_test;
`uvm_component_utils(my_test) env e;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_driver::type_id::set_inst_override(my_updated_driver::get_type(),
"top.e.agent.drvr");
set_inst_overidde_by_type(my_driver::get_type(),my_updated_driver(),”*”)
or
set_inst_overidde_by_type(my_driver::get_type(),my_updated_driver(),”agen
t.*”);
or
set_inst_overidde_by_type(my_driver::get_type(),my_updated_driver(),”agen
t*”);
e = env::type_id::create("e", this);
endfunction: build_phase
task run_phase (uvm_phase phase); ... ... ...
endtask: run_phase
endclass: my_test
• Objects or sequence related
objects are generally only used
with type override
• since the instance override
approach relates to a position
in the UVM Testbench
component hierarchy which
objects do not take part in.
• For complex UVM Testbench Environment, it is often useful to print out the structure of the
testbench in tabular form, that were registered with the Factory.
• A great technique to view the structural composition of the Testbench classes and the Factory
setup is to call the this.print() and factory.print() methods in the end_of_elaboration_phase()
(as shown in Code below) from the top-level testbench.
• By the time the end_of_elaboration_phase() executes, the entire environment has already been
built and connected
• So these print() methods show what had been built in the Testbench and the types that were
registered with the factory.
Debugging the UVM Testbench Structure & Factory Content
function void end_of_elaboration_phase(uvm_phase
phase);
super.end_of_elaboration_phase(phase);
this.print();
factory.print();
endfunction: end_of_elaboration_phase
UTILITY & FIELD
Macros
• They are 2 ways to implement important transaction methods
1. First method is using field_macros
2. Second method is using manual coding techniques by overriding build in do_methods
• Using field macros is relatively simple but they can be inefficient during simulation & difficult
to debug if something does go wrong
• Cadence and mentor uvm experts avoid using field macros due to their coding and simulation
inefficiencies
Transaction methods in sequence_item
Utility & field macros example
class ABC extends uvm_object;
rand bit [15:0] m_addr;
rand bit [15:0] m_data;
`uvm_object_utils_begin(ABC)
`uvm_field_int(m_addr, UVM_DEFAULT)
`uvm_field_int(m_data, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "ABC");
super.new(name); endfunction
endclass
//field macros are invoked inside the
utility macros
class ABC extends uvm_object;
`uvm_object_utils(ABC)
rand bit [15:0] m_addr;
rand bit [15:0] m_data;
`uvm_field_utils_begin(ABC) // error dis line
`uvm_field_int(m_addr, UVM_DEFAULT)
`uvm_field_int(m_data, UVM_DEFAULT)
`uvm_field_utils_end
function new(string name = "ABC");
super.new(name); endfunction
endclass
Output:Error because uvm_object_utils macro
also calls uvm_field_macros no need to define as
uvm_field_utils_begin, its illegal
Utility macros
• Utility macros are used to declare either object or component going to store in factory
• Field_macros are used inside the utility macros
• If we specify begin-end, it wont be block , if it is not blocked then every time we need to change
fields
• Once it blocks by using begin-end, then particular fields will freeze with particular i.e., int, enum
etc
uvm_object_utils_begi
n();
:::::::;
uvm_object_utils_end
begin-end , whatever inside
fields we r going to write
that will be block
uvm_component_utils_be
gin();
:::::::;
uvm_object_utils_end
Utility macros
For simple objects with no field macros, use
`uvm_object_utils(TYPE)
For simple objects with field macros, use
`uvm_object_utils_begin(TYPE)
`uvm_field_* macro invocations here
`uvm_object_utils_end
For parameterized objects with no field macros,
use
`uvm_object_param_utils(TYPE)
For parameterized objects, with field macros,
use
`uvm_object_param_utils_begin(TYPE)
`uvm_field_* macro invocations here
`uvm_object_utils_end
For simple objects with no field macros, use
`uvm_component_utils(TYPE)
For simple objects with field macros, use
`uvm_component_utils_begin(TYPE)
`uvm_field_* macro invocations here
`uvm_component_utils_end
For parameterized objects with no field macros,
use
`uvm_component_param_utils(TYPE)
For parameterized objects, with field macros,
use
`uvm_component_param_utils_begin(TYPE)
`uvm_field_* macro invocations here
`uvm_component_utils_end
UVM Field Macros
• The `uvm_field_* macros are invoked inside of the `uvm_*_utils_begin and `uvm_*_utils_end
macro blocks to form “automatic” implementations of the core data methods:
copy, compare, pack, unpack, record, print, and sprint.
• By using the macros, you do not have to implement any of the do_* methods inherited
from uvm_object.
• The field macros expand into general inline code that is not as run-time efficient nor as flexible as
direct implementions of the do_* methods.
• Each `uvm_field_* macro is named according to the particular data type it handles:
integrals, strings, objects, queues, etc., and each has at least two arguments: FIELD and FLAG.
FIELD-int
FLAG-UVM_DEFAULT `uvm_object_utils_begin(ABC)
`uvm_field_int(m_addr, UVM_DEFAULT)
`uvm_field_int(m_data, UVM_DEFAULT)
`uvm_object_utils_end
Field_macros FIELD description
FIELD TYPE Description
`UVM_FIELD_* MACROS Macros that implement data operations for scalar properties.
`uvm_field_int Implements the data operations for any packed integral property.
`uvm_field_object Implements the data operations for an uvm_object-based property.
`uvm_field_string Implements the data operations for a string property.
`uvm_field_enum Implements the data operations for an enumerated property.
`uvm_field_real Implements the data operations for any real property.
`uvm_field_event Implements the data operations for an event property.
• Macros that implement data operations for scalar properties.
Field_macros FIELD description
FIELD TYPE Description
`UVM_FIELD_SARRAY_*
MACROS
Macros that implement data operations for one-dimensional static array
properties.
`uvm_field_sarray_int Implements the data operations for a one-dimensional static array of integrals.
`uvm_field_sarray_object Implements the data operations for a one-dimensional static array of uvm_object-based
objects.
`uvm_field_sarray_string Implements the data operations for a one-dimensional static array of strings.
`uvm_field_sarray_enum Implements the data operations for a one-dimensional static array of enums.
`UVM_FIELD_ARRAY_*
MACROS
Macros that implement data operations for one-dimensional dynamic array
properties.
`uvm_field_array_int Implements the data operations for a one-dimensional dynamic array of integrals.
`uvm_field_array_object Implements the data operations for a one-dimensional dynamic array
of uvm_object-based objects.
`uvm_field_array_string Implements the data operations for a one-dimensional dynamic array of strings.
`uvm_field_array_enum Implements the data operations for a one-dimensional dynamic array of enums.
Field_macros FIELD description
FIELD TYPE Description
`UVM_FIELD_QUEUE_* MACROS Macros that implement data operations for dynamic queues.
`uvm_field_queue_int Implements the data operations for a queue of integrals.
`uvm_field_queue_object Implements the data operations for a queue of uvm_object-based
objects.
`uvm_field_queue_string Implements the data operations for a queue of strings.
`uvm_field_queue_enum Implements the data operations for a one-dimensional queue of enums.
`UVM_FIELD_AA_*_STRING
MACROS
Macros that implement data operations for associative arrays
indexed by string.
`uvm_field_aa_int_string Implements the data operations for an associative array of integrals
indexed by string.
`uvm_field_aa_object_string Implements the data operations for an associative array
of uvm_object-based objects indexed by string.
`uvm_field_aa_string_string Implements the data operations for an associative array of strings
indexed by string
Field_macros FIELD description
FIELD TYPE Description
`UVM_FIELD_AA_*_INT MACROSMacros that implement data operations for associative arrays
indexed by an integral type.
`uvm_field_aa_object_int Implements the data operations for an associative array
of uvm_object-based objects indexed by the int data type.
`uvm_field_aa_int_int Implements the data operations for an associative array of integral types
indexed by the int data type.
`uvm_field_aa_int_int_unsign
ed
Implements the data operations for an associative array of integral types
indexed by the int unsigneddata type.
`uvm_field_aa_int_integer Implements the data operations for an associative array of integral types
indexed by the integer data type.
`uvm_field_aa_int_integer_un
signed
Implements the data operations for an associative array of integral types
indexed by the integer unsigned data type.
Field_macros FIELD description
RECORDING MACROS The recording macros assist users who implement
the uvm_object::do_record method.
`uvm_record_attribute Vendor-independent macro for recording attributes (fields) to a
vendor-specific transaction database.
`uvm_record_field Macro for recording name-value pairs into a transaction recording
database.
PACKING MACROS The packing macros assist users who implement
the uvm_object::do_packmethod.
PACKING - WITH SIZE INFO
`uvm_pack_intN Pack an integral variable.
`uvm_pack_enumN Pack an integral variable.
`uvm_pack_sarrayN Pack a static array of integrals.
`uvm_pack_arrayN Pack a dynamic array of integrals.
`uvm_pack_queueN Pack a queue of integrals.
Field_macros FIELD description
PACKING - NO SIZE INFO
`uvm_pack_int Pack an integral variable without having to also specify the bit size.
`uvm_pack_enum Pack an enumeration value.
`uvm_pack_string Pack a string variable.
`uvm_pack_real Pack a variable of type real.
`uvm_pack_sarray Pack a static array without having to also specify the bit size of its
elements.
`uvm_pack_array Pack a dynamic array without having to also specify the bit size of its
elements.
`uvm_pack_queue Pack a queue without having to also specify the bit size of its elements.
Field_macros FIELD description
UNPACKING - WITH SIZE INFO
`uvm_unpack_intN Unpack into an integral variable.
`uvm_unpack_enumN Unpack enum of type TYPE into VAR.
`uvm_unpack_sarrayN Unpack a static (fixed) array of integrals.
`uvm_unpack_arrayN Unpack into a dynamic array of integrals.
`uvm_unpack_queueN Unpack into a queue of integrals.
UNPACKING - NO SIZE INFO
`uvm_unpack_int Unpack an integral variable without having to also specify the bit size.
`uvm_unpack_enum Unpack an enumeration value, which requires its type be specified.
`uvm_unpack_string Pack a string variable.
`uvm_unpack_real Unpack a variable of type real.
`uvm_unpack_sarray Unpack a static array without having to also specify the bit size of its
elements.
`uvm_unpack_array Unpack a dynamic array without having to also specify the bit size of
its elements.
`uvm_unpack_queue Unpack a queue without having to also specify the bit size of its
elements.
Field_macros FIELD description
FIELD TYPE Description
`uvm_field_aa_int_byte Implements the data operations for an associative array of integral types
indexed by the byte data type.
`uvm_field_aa_int_byte_unsig
ned
Implements the data operations for an associative array of integral types
indexed by the byte unsigned data type.
`uvm_field_aa_int_shortint Implements the data operations for an associative array of integral types
indexed by the shortint data type.
`uvm_field_aa_int_shortint_un
signed
Implements the data operations for an associative array of integral types
indexed by the shortint unsigned data type.
`uvm_field_aa_int_longint Implements the data operations for an associative array of integral types
indexed by the longint data type.
`uvm_field_aa_int_longint_un
signed
Implements the data operations for an associative array of integral types
indexed by the longint unsigned data type.
`uvm_field_aa_int_key Implements the data operations for an associative array of integral types
indexed by any integral key data type.
`uvm_field_aa_int_enumkey Implements the data operations for an associative array of integral types
indexed by any enumeration key data type.
Field_macros FLAG description
FLAG Description
UVM_ALL_ON Set all operations on (default)
UVM_DEFAULT Use the default flag settings
UVM_NOCOPY Do not copy this field
UVM_NOCOMPARE Do not compare this field
UVM_NOPRINT Do not print this field
UVM_NODEFPRINT Do not print the field if it is the same as its
UVM_NOPACK Do not pack or unpack this field
UVM_PHYSICAL Treat as a physical field. Use physical setting in policy class for this field
UVM_ABSTRACT
Treat as an abstract field. Use the abstract setting in the policy class for
this field
UVM_READONLY Do not allow the setting of this field from the set_*_local methods
A radix for printing and recording can be specified by OR’ing one of the following constants in
the FLAG argument
FLAG Description
UVM_BIN Print/record the field in binary (base-2)
UVM_DEC Print/record the field in decimal (base-10)
UVM_UNSIGNE
D
Print/record the field in unsigned decimal
(base-10)
UVM_OCT Print/record the field in octal (base-8).
UVM_HEX Print/record the field in hexadecimal (base-16)
UVM_STRING Print/record the field in string format
UVM_TIME Print/record the field in time format
• Using field_automation_macros are not recommended these days, because it introduce a lot of
additional code and reduces simulation performance
• It is recommended to use do_macros, user can implement the functions called do_print,
do_record, do_compare, do_pack, do_unpack
• They are 6 do_macros in sequence_item
1. do_print
2. do_record
3. do_copy
4. do_compare
5. do_pack
6. do_unpack
7. clone
8. create
do_methods in sequence_item
do_methods in sequence_item
print
sprint
convert2string
record
copy
compare
pack
pack_bytes
pack_ints
unpack
unpack_bytes
unpack_ints
clone
create
do_print
do_record
do_copy
do_compare
do_pack
do_unpack
clone
create
do_methods in sequence_item
print virtual function void do_print(uvm_printer printer);
super.do_print(printer);
printer.print_string("m_bool", m_bool.name());
printer.print_field_int("m_mode", m_mode, $bits(m_mode), UVM_HEX);
printer.print_string("m_name", m_name);
endfunction
sprint `uvm_info(get_type_name(), $sformatf("Contents: %s", obj.sprint()), UVM_LOW)
convert2string virtual function string convert2string();
string contents = ""; $sformat(contents, "%s m_name=%s", contents, m_name);
$sformat(contents, "%s m_bool=%s", contents, m_bool.name());
$sformat(contents, "%s m_mode=0x%0h", contents, m_mode);
foreach(m_data[i])
Begin
$sformat(contents, "%s m_data[%0d]=0x%0h", contents, i, m_data[i]);
end
return contents;
do_methods in sequence_item
Copy // Implementation of "do_copy". A generic uvm_object called "rhs“
// is received and type casted into Packet called "_pkt". Then
// m_addr is copied from _pkt to the variable in current
class virtual function void do_copy(uvm_object rhs);
Packet _pkt; super.do_copy(rhs);
$cast(_pkt, rhs); m_addr = _pkt.m_addr;
`uvm_info(get_name(), "In Packet::do_copy()", UVM_LOW)
endfunction
Compare function void _compare(Object obj1, obj2);
if (obj2.compare(obj1))
`uvm_info("TEST", "obj1 and obj2 are same", UVM_LOW)
else
`uvm_info("TEST", "obj1 and obj2 are different", UVM_LOW)
endfunction
pack virtual function void do_pack(uvm_packer packer);
super.do_pack(packer);
packer.pack_field_int(m_addr, $bits(m_addr));
packer.pack_field_int(m_wdata, $bits(m_wdata));
packer.pack_field_int(m_rdata, $bits(m_rdata));
packer.pack_field_int(m_wr, $bits(m_wr));
Endfunction
do_methods in sequence_item
unpack // Now unpack the packed int array into the second object, and display
m_val3 = m_pkt2.unpack_ints(m_ints);
`uvm_info(get_type_name(), $sformatf("unpacked m_val3=0x%0h", m_val3),
UVM_LOW)
m_pkt2.print();
clone function void build_phase(uvm_phase phase);
// Create obj1, but only declare handle for obj2
Object obj2;
Object obj1 = Object::type_id::create("obj1");
obj1.randomize();
`uvm_info("TEST", $sformatf("Obj1.print: %s", obj1.convert2string()), UVM_LOW)
// Use $cast to clone obj1 into obj2
$cast(obj2, obj1.clone());
`uvm_info("TEST", "After clone", UVM_LOW)
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
endfunction
uvm_do macros in
sequence
Writing sequence using do_macros
• We know that sequence calls the start_item and finish_item inside the task body
• Instead of writing all these statements in code by simply calling uvm_sequence macros
• At compile time these macros will be substituted , with calls to start_item() and
finish_item()
• uvm_do_macros are used to reduce the no. of lines in code by creating item,
randomizing it and automatically calling the required tasks to start given sequence or
sequence_item
uvm sequence macros
Macro Description
`uvm_do(Item/Seq)
This macro takes seq_item or sequence as argument.
On calling `uvm_do() the above-defined 6 steps will be
executed.
`uvm_create(Item/Seq) This macro creates the item or sequence.
`uvm_send(Item/Seq)
create() and randomize() are skipped, rest all other
steps are executed.
`uvm_rand_send(Item/Seq)
Only create() is skipped, rest all other steps are
executed.
`uvm_do_with(Item/Seq,Constraints)
This macro performs above 6 steps along with
constraints defined in second argument.
`uvm_rand_send_with(Item/Seq,Constraints)
create() is skipped, rest all other steps are executed
along with constraints defined in second argument.
`uvm_do_pri(Item/Seq,Priority ) Performs `uvm_do() with priority mentioned.
1.create_item() / create req 2.wait_for_grant() 3.randomize the req 4.send the req
5.wait for item done 6.get response.
uvm sequence macros
Macro Description
`uvm_do_pri_with(Item/Seq,Constraints,Priority)
Performs `uvm_do() along with constraints defined and priority
mentioned.
`uvm_send_pri(Item/Seq,Priority)
create() and randomize() are skipped, rest all other steps are
executed with priority mentioned.
`uvm_rand_send_pri(Item/Seq,Priority)
Only create() is skipped, rest all other steps are executed with
priority mentioned.
`uvm_rand_send_pri_with(Item/Seq,Priority,
Constraints)
create() is skipped, rest all other steps are executed along with
constraints defined with priority mentioned.
`uvm_declare_p_sequencer(SEQUENCER)
This macro is used to declare a variable p_sequencer whose type
is specified by SEQUENCER. by using p_sequencer
handle, properties of sequencer can be accessed.
1.create_item() / create req 2.wait_for_grant() 3.randomize the req 4.send the req
5.wait for item done 6.get response.
Writing sequence using do_macros
//`uvm_do
class mem_sequence extends uvm_seque
nce#(mem_seq_item);
`uvm_object_utils(mem_sequence)
//Constructor
function new(string name = "mem_seque
nce");
super.new(name);
endfunction
virtual task body();
`uvm_do(req)
endtask
endclass
//`uvm_create and `uvm_send
class mem_sequence extends uvm_sequence#(mem
_seq_item);
`uvm_object_utils(mem_sequence)
//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
virtual task body();
`uvm_create(req)
assert(req.randomize());
`uvm_send(req);
endtask
endclass
Writing sequence using do_macros
//`uvm_rand_send
class mem_sequence extends uvm_sequen
ce#(mem_seq_item);
`uvm_object_utils(mem_sequence)
//Constructor
function new(string name = "mem_sequen
ce");
super.new(name);
endfunction
virtual task body();
`uvm_create(req)
`uvm_rand_send(req)
endtask
endclass
//`uvm_do_with
class mem_sequence extends uvm_sequence#(mem
_seq_item);
`uvm_object_utils(mem_sequence)
//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
virtual task body();
`uvm_do_with(req,{req.wr_en == 1;})
endtask
endclass
Writing sequence using do_macros
//`uvm_rand_send_with
class mem_sequence extends uvm_sequen
ce#(mem_seq_item);
`uvm_object_utils(mem_sequence)
//Constructor
function new(string name = "mem_sequen
ce");
super.new(name);
endfunction
virtual task body();
`uvm_create(req)
`uvm_rand_send_with(req,{req.rd_en == 1;}
)
endtask
//calling sequence’s inside sequence
class wr_rd_seq extends uvm_sequence#(mem_seq_item);
write_sequence wr_seq;
read_sequence rd_seq;
`uvm_object_utils(wr_rd_seq)
//Constructor
function new(string name = "wr_rd_seq");
super.new(name);
endfunction
virtual task body();
`uvm_do(wr_seq)
`uvm_do(rd_seq)
endtask
endclass
m_sequencer
&
p_sequencer
Need for m_sequencer, p_sequencer
• In SystemVerilog based OVM/UVM methodologies, the sequence is an object with limited life time
unlike a component which has a lifetime through out simulation.
• The sequence is created , started and once done, de-referenced from memory and this can be any time
in the duration of a test.
• So if we want to access anything from the testbench hierarchy (which are components) - the sequence
would need a handle to the sequencer on which the sequence is running.
• Note that sequencer is a component
• m_sequencer is a handle of type uvm_sequencer_base which is available by default in a sequence
• To access the real sequencer on which sequence is running , you would need to typecast the
m_sequencer to the physical sequencer which is generally called p_sequencer (though you could
name it any)
• We do not use them only in virtual sequences. These can be used in any sequence, if we need any
functionality of sequencer in sequence.
Example for m_sequencer, p_sequencer
• Example where a sequence(object) wants to access a clock monitor which is available with its sequencer
(component)
• //Typecast the m_sequencer base type to p_sequencer because get access to clock monitor
//A test_sequencer class derived from base UVM sequencer
//Lets say, it has a clock monitor component to access clock.
class test_sequencer_c extends uvm_sequencer;
clock_monitor_c clk_monitor;
endclass
//Here is a test sequence that runs on the test_sequencer
//Lets say, sequence need access to sequencer to get access to clock monitor
class test_sequence_c extends uvm_sequence;
test_sequencer_c p_sequencer; //p_sequencer need to declare, m_sequencer by default it
available
clock_monitor_c my_clock_monitor;
task pre_body()
//Typecast the m_sequencer base type to p_sequencer
if(!$cast(p_sequencer, m_sequencer)) begin
`uvm_fatal("Sequencer Type Mismatch:", " Worng Sequencer");
end
//get access to clock monitor
my_clock_monitor = p_sequencer.clk_monitor;
endtask
endclass
m_sequencer, p_sequencer
• m_sequencer is a generic sequencer pointer of type uvm_sequencer_base. It will always exist for
a uvm_sequence and is initialized when the sequence is started.
• The p_sequencer is a type specific sequencer pointer, created by registering the sequence to a sequencer
using the `uvm_declare_p_sequencer macros.
• Being type specific, you will be able to access anything added to the sequencer (i.e. pointers to other
sequencers, etc.).
• p_sequencer will not exist if the `uvm_declare_p_sequencer macros isn’t used.
• m_sequencer is a handle of type uvm_sequencer_base while p_sequencer is a handle of
type user_defined_sequencer.
contin,,,,
• The user_defined_sequencer is a grandchild of uvm_sequencer_base class.
• When we start a sequence, we provide an object handle of our user_defined_sequencer.
• Internally, in start method, this child class object is assigned into parent handle called m_sequencer.
• So, a static casting occurs such that a parent class handle points to child class object
(m_sequencer = user_defined_sequencer_object).
• Now, when referring to sequence, if a p_sequencer is defined, the macro `uvm_declare_p_sequencer expands
to a function that declares a user_defined_sequencer handle known as p_sequencer.
• This function then casts the m_sequencer (parent class handle) back to p_sequencer (child class handle)
using dynamic casting ($cast).
m_sequencer vs p_sequencer
m_sequencer
• For a sequence, m_sequencer is a
handle to the sequencer on which the
sequence runs on.
• m_sequencer exists by default in UVM
sequences
• It's set automatically when you call
start()
p_sequencer
• For a sequence, p_sequencer is a
specific-type of sequencer which you
would like the sequence to run on
• P_sequencer doesn’t exist by default in
sequences declare using
`uvm_declare_psequencer(“sequencer_t
ype”)
• Implement a function to set a value
Virtual sequence
&
Virtual sequencer
Why Sequence is not directly connected to Driver in UVM
• In SV, the Transgenerator is randomizing all the transactions sending all these transactions
at a time to the driver through mailbox.
• Here we don’t know that driver is getting all transactions and sending to DUT, the driver
doesn’t give any response
• In UVM , the sequence is generating and randomizing each transaction, before sending
transaction to driver it sends request to driver through sequencer
• For each transfer it gets the response from the driver
• Driver gives response like get_next_item and item_done responses to sequence through this
sequencer
What is Virtual Sequence
• Virtual sequence is a container which consists of different Sequences inside in it
• Virtual sequence is used to start multiple sequences on different sequencers
• When multiple sequences are there, then virtual sequence should be used
• Inside the Virtual sequence, different sequences ,sequencers and Virtual sequencer handles
are declared
What is Virtual Sequencer
• It just contains the handles of different sequencers
• When multiple sequencers are there in Environment then Virtual sequencer should
be required
• Virtual Sequencer is a Sequencer that is not connected to the UVM Driver itself,
but contains the handles of the target Sequencer in the Testbench hierarchy.
When we require Virtual Sequence/Virtual Sequencer
Same stimuli Different stimulus
When we need Virtual Sequence/Virtual Sequencer
Scenario-1
• Let us consider a simple case where we are integrating two such blocks: two sequencers driving
these two blocks.
• From the top-level test, we will need a way to control these two sequencers.
• This can be achieved by using a virtual sequencer and virtual sequences.
Scenario-2
• Another scenario let’s consider the DUT is having 2 different interface ports, in this situation,
there would be 2 Agents serving each interface port inside the UVM Testbench.
• Virtual Sequence will co-ordinate & synchronize the Transactions for the 2 Agents to generate
the simulation uses cases using the corresponding Sub-Sequences.
• Virtual Sequence decides which Agent’s Sequence will start first and the order of
Sub-Sequences execution.
• We can say, Virtual Sequence acts like a Controller of the simulation data being generated for
the DUT.
How virtual sequence works
VIRTUAL SEQUENCE SUB SEQUENCES PRIMITIVE SEQUENCES
Do not create any Transaction items Create Transaction items
WR_sequence
RD_sequence
WR_sequence
RD_sequence
WR_sequence
RD_sequence
VIRTUAL
SEQUENCE
APB_SEQ
AHB_SEQ
AXI_SEQ
Virtual Sequence implementation
• By using 2 approaches we can implement Virtual Sequence
• 1st
Approach: Virtual sequence will contain handles of Sequencers on which
subsequences are to be executed(Stand alone Virtual Sequence, without virtual
sequencer)
• 2nd
Approach: Virtual sequence will run on Virtual Sequencer, Virtual sequencer will
contain sequencers handle(with virtual sequencer)
Virtual Sequence implementation approach-1
(without virtual sequencer & starting virtual
sequence on null )
Virtual Sequence implementation
• Virtual Sequence declaration which includes target Sequencers handles
• The way a Virtual Sequence starts the Sub-Seqs on target Sequencers
• The way a Virtual Sequence is started from a Test class
As shown in the diagram, Virtual Sequence contains
two Sequencer handles i.e. SQR_AHB & SQR_AXI.
• There are 2 Agents i.e. AHB Agent & AXI Agent
which physically contains 2 Sequencers.
• These 2 Sequencers are assigned to the Sequencer
handles inside Virtual Sequence in a Test.
• As per the shown diagram, Virtual Sequence also
creates two Sequences which are to be run on the
Sequencers of the respective Agents.
Virtual Sequence implementation
///// Base Virtual Sequence Class
class base_vseq extends uvm_sequence
#(uvm_sequence_item);
`uvm_object_utils(base_vseq)
/// Target Agent Sequencers
uvm_sequencer #(ahb_txn) SQR_AHB;
uvm_sequencer #(axi_txn) SQR_AXI;
/// Constructor
function new (string name = "base_vseq");
super.new(name);
endfunction: new
endclass: vseq_base
///// Virtual Sequence Class
class my_vseq extends base_vseq;
`uvm_object_utils(my_vseq)
/// Constructor function new (string name = "my_vseq");
super.new(name);
endfunction: new
/// Sequence Body Task
task body();
ahb_seqeunce ahb_seq;
axi_sequence axi_seq;
ahb_seq = ahb_sequence::type_id::create("ahb_seq");
axi_seq = axi_sequence::type_id::create("axi_seq");
fork
abh_seq.start(SQR_AHB);
axi_seq.start(SQR_AXI);
join
• Create a “Base Virtual Sequence” which may contain
the handles of all the required target Sequencers.
• Later this Base Virtual Sequence can be extended to
create the Virtual Sequence with the code to start the
Sub-Sequences on the target Sequencers.
• we got two classes i.e. & my_vseq class. base_vseq
is the base virtual sequence class and my_vseq is the
intended Virtual Sequence.
• Base virtual sequence contains the handle of the two
target Sequencers i.e. SQR_AHB & SQR_AXI.
• Virtual Sequence class is extended from the Base
Virtual Sequenceclass.
• It creates the two Sub-Sequences
i.e ahb_seq & axi_seq using
the Factory mechanism.
• Later inside the body() task the Sub-Sequences are
started on the target Agent’s Sequencer by the
Virtual Sequence.
Inside the Test and how the Virtual Sequence is started from the Test?
///// Base Test Class
class base_test extends uvm_test;
`uvm_component_utils(base_test);
/// Environment Class Instantiation top_env Env;
/// Constructor function new (string name = "base_test",
uvm_component parent = null);
super.new(name, parent); endfunction: new
/// Build Phase function void build_phase (uvm_phase phase);
Env = top_env::type_id::create("Env");
endfunction: build_phase
/// Method to Connect Sequencer Handles in VSEQ
function void init_vseq (base_vseq vseq);
vseq.SQR_AHB = test.env.ahb_agent.SQR_AHB;
vseq.SQR_AXI = test.env.axi_agent.SQR_AXI;
endfunction: init_vseq
endclass: base_test
///// Main Test
class test extends base_test;
`uvm_component_utils(test)
/// Constructor function new (string name = "test",
uvm_component parent = null); super.new(name, parent);
endfunction: new
/// Run Phase
task run_phase (uvm_phase phase); /// Create the Virtual
Sequence my_vseq vseq = my_vseq::type_id::create("vseq");
phase.raise_objection(this); /// Virtual Sequence Initialization
init_vseq(vseq); /// Start the Virtual Sequence
vseq.start(null);
phase.drop_objection(this);
endclass: test
• In the test base class i.e. base_test, shown UVM
code, a method i.e. init_vseq() is created
• which is used to assign the sequencer handles to
the handles in classes derived from the virtual
sequence base class.
• Inside the main test which is derived from the base
test, Virtual Sequence is created using Factory.
• Later, the initialization method i.e. init_vseq() is
being called to connect the Sequencers handle.
• Finally Virtual Sequence is started using “Null”
since this Virtual Sequence is NOT started on any
particular Sequencer.
Virtual Sequence implementation approach-2
(with virtual sequencer & starting virtual sequence
on virtual sequencer)
Virtual Sequence implementation
• Virtual Sequencer is a Sequencer that is not connected to the UVM Driver itself but contains the
handles of the target Sequencer in the Testbench hierarchy.
• In the diagram below, there is an example UVM Testbench environment to show the Virtual
Sequencer’s application and 2nd approach of Virtual Sequence Implementation:
Virtual Sequencer is the part of the Environment
i.e. “Env”.
Virtual Sequencer contains the handles of the
target Sequencers i.e. & which are physically
located inside the Agents i.e. AHB Agent & AXI
Agent respectively.
These target Sequencers handles assignment will
be done during the connect phase of the
Environment class..
Virtual Sequence is located outside the Environment class & it is created in the run_phase()
method of the Test. The Virtual Sequence is designed to run on the Virtual Sequencer & Virtual
Sequence also gets the handles of the target Sequencers from the Virtual Sequencer.
Virtual Sequencer class
// Virtual Sequencer
Class class virtual_seqr extend uvm_sequencer;
`uvm_component_utils(virtual_seqr)
// Target Sequencer Handles
ahb_seqr SQR_AHB;
axi_seqr SQR_AXI;
// Constructor
function new (string name = "virtual_seqr",
uvm_component parent);
super.new(name, parent);
endfunction: new
endclass: virtual_seqr
• Virtual Sequencer
i.e. “virtual_seqr” class is declared
by extended the UVM base
class uvm_sequencer.
• Target Sequencer handles are also
declared inside it.
Virtual Sequence class
// Base Virtual Sequence
class base_vseq extends uvm_sequence
#(uvm_sequence_item); `uvm_object_utils(base_vseq)
// Virtual Sequencer Handle
virtual_seqr v_sqr;
// Target Sequencers Handle
ahb_seqr SQR_AHB;
axi_seqr SQR_AXI;
// Constructor
function new (string name = "base_vseq"); super.new(name);
endfunction: new
// Body Task (Assign target sequencers handle)
task body();
if (!$cast(v_sqr, m_sequencer))
begin
`uvm_error(get_full_name(), "Virtual Seqr pointer cast
failed")
end
SQR_AHB = v_sqr.SQR_AHB;
SQR_AXI = v_sqr.SQR_AXI;
endtask: body
endclass: base_vseq
// Virtual Sequence
class my_vseq extends base_vseq;
`uvm_object_utils(my_vseq)
// Constructor
function new (string name = "my_vseq");
super.new(name);
endfunction: new
// Body Task(starting the sub-sequences)
task body();
// Assigning the Sub-Sequencer Handles
super.body;
// Sub-Sequence Creation & Execution
ahb_sequence ahb_seq; axi_sequence axi_seq;
ahb_seq = ahb_sequence::type_id::create("ahb_seq");
axi_seq = axi_sequence::type_id::create("axi_seq");
repeat(30)
begin
ahb_seq.start(SQR_AHB); axi_seq.start(SQR_AXI);
end
endtask: body
endclass: my_vseq
First, a Base Virtual Sequence will be declared & later Virtual Sequence will be derived from the base virtual
sequence.
Environment class
// Environment
Class class Environment extends uvm_env;
`uvm_component_utils(Environment)
// Virtual Sequencer Handle
virtual_seqr v_sqr;
// Agents Handles
ahb_agent AHB_AGNT;
axi_agent AXI_AGNT;
// Constructor
function new (string name = "Environment", uvm_component
parent);
super.new(name, parent);
endfunction: new
// Build Phase
function void build_phase (uvm_phase phase);
v_sqr = virtual_seqr::type_id::create("v_sqr");
AHB_AGNT=ahb_agent::type_id::create("AHB_AGNT");
AXI_AGNT = axi_agent::type_id::create("AXI_AGNT");
endfunction: build_phase
// Connect Phase
function void connect_phase (uvm_phase phase);
v_sqr.SQR_AHB = AHB_AGNT.m_sequencer;
v_sqr.SQR_AXI = AXI_AGNT.m_sequencer;
endfunction: connect_phase
• In the Environment class
i.e. “Environment”, Virtual Sequencer
is instantiated & built along with two
Agents
i.e. “AHB_AGNT” & “AXI_AGNT”.
• Target Sequencer handles are also
assigned in the connect_phase().
• Usage of a flexible & handy feature of
UVM i.e. “m_sequencer” is being
shown which by default points to the
UVM Sequencer derived from
the uvm_sequencer.
Environment class
// Main Test
class Test extends uvm_test;
`uvm_component_utils(Test)
// Instantiations
my_vseq vseq;
Environment Env;
// Constructor
function new (string name = "Test", uvm_component parent =
null);
super.new(name, parent);
endfunction: new
// Build Phase
function void build_phase (uvm_phase phase);
Env = Environ::type_id::create("Env");
endfunction: build_phase
// Run Phase
task run_phase (uvm_phase phase);
// Create the Virtual Sequence & Environment
vseq = my_vseq::type_id::create("vseq");
phase.raise_objection(this);
// Start the Virtual Sequence
vseq.start(Env.v_sqr);
phase.drop_objection(this);
endtask: run_phase
• Virtual Sequence is started on the
Virtual Sequencer from the Test
• In the Test class i.e. “Test”, both
Environment & Virtual Sequence
i.e. “Environment” & “my_vseq”
are instantiated and created.
• Finally, Virtual Sequence is started
on the Virtual Sequencer which
exists inside the Environment.
Sequence arbitration
UVM Sequence Arbitration Mechanism
• Multiple sequences can interact concurrently with a driver connected to a single interface.
• The sequencer supports an arbitration mechanism to ensure that at any point of time only one
sequence has access to the driver.
• The choice of which sequence can send a sequence_item is dependent on a user-selectable
sequencer arbitration algorithm.
• There are six built-in sequencer arbitration mechanisms that are implemented in UVM.
• There is also an additional hook to implement a user-defined algorithm.
• The sequencer has a method called set_arbitration() that can be called to select which
algorithm the sequencer should use for arbitration.
Contin,,,
• The six algorithms that can be selected are as follows:
1.SEQ_ARB_FIFO (Default if none specified).
• If this arbitration mode is specified, then the sequencer picks sequence items in a FIFO order
from all sequences running on the sequencer.
• Example: if seq1, seq2, and seq3 are running on a sequencer, it will pick an item from seq1 first,
followed by seq2, and then seq3 if available, and continue.
2.SEQ_ARB_WEIGHTED
• If this arbitration mode is selected, sequence items from the highest priority sequence are always
picked first until none available, then the sequence items from the next priority sequence, and so
on.
• If two sequences have equal priority, then the items from them are picked in random order.
Continu,,,
3.SEQ_ARB_RANDOM
• If this arbitration mode is selected, sequence items from different sequences are picked in random
order by ignoring all priorities.
4.SEQ_ARB_STRICT_FIFO
• This is similar to SEQ_ARB_WEIGHTED except that if two sequences have the same priority,
then the items from those sequences are picked in a FIFO order rather than in random order.
5.SEQ_ARB_STRICT_RANDOM
• This is similar to SEQ_ARB_RANDOM except that the priorities are NOT ignored.
• The items are picked randomly from sequences with the highest priority first followed by next and
in that order.
6.SEQ_ARB_USER
• This algorithm allows a user to define a custom algorithm for arbitration between sequences.
• This is done by extending the uvm_sequencer class and overriding the
user_priority_arbitration() method.
How to prioritize a sequence?
• The priority is specified by passing an argument to the start() method of the sequence.
• The priority is decided based on relative values specified for different sequences.
For Example:
• If two sequences are started as follows, the third argument specifies the priority of the
sequence.
• seq_1.start(m_sequencer, this, 700); //Highest priority
• seq_2.start(m_sequencer, this, 500); //Next Highest priority
• seq_3.start(m_sequencer, this, 300); //Lowest priority among three sequences
Sequencer and driver
handshaking
Sequencer and driver communiction
• In UVM, there is a mechanism to be followed when we want to send the transactions from the
sequencer to the Driver in order to provide stimulus to the DUT.
• The transfer of request and response sequence items between sequences and their target driver is
facilitated by a TLM communication mechanism implemented in the sequencer.
• A particular Sequence is directed to run on a Sequencer which in turns further breaks down into a
series of transaction items
• These transaction items are needs to be transferred to the Driver where these transaction items
are converted into cycle based signal/pin level transitions.
Sequencer side operation
• To send a sequence_item to a driver there are four steps that need to occur:
Step 1 – Creation: Creating the “transaction item” with the declared handle using factory
mechanism.
Step 2 - Ready - start_item():The start_item() call is made, passing the sequence_item handle as an
argument. This call blocks until the sequencer grants the sequence and the sequence_item access to
the driver.
Step 3 – Set:The sequence_item is prepared for use, usually through randomization, but it may also
be initialised by setting properties directly.
Step 4 - Go - finish_item(): The finish_item() call is made, which blocks until the driver has
completed its side of the transfer protocol for the item. No simulation time should be consumed
between start_item() and finish_item().
Step 5 - Response - get_response():This step is optional, and is only used if the driver sends a
response to indicate to indicate that it has completed transaction associated with the
sequence_item. The get_response() call blocks until a response item is available from the
sequencers response FIFO.
Sequencer and driver communiction
• These are the operational steps from a Sequence which we want to execute using a Sequencer
that is connected to a Driver inside an “Agent”.
• Whole of this process is shown in the Figure 1 & Figure 2 below:
Fig: Driver & Sequencer Interaction for
Transaction Exchange
Fig: Transaction Execution Flow Between a Sequencer,
Driver & Virtual Interface
Driver side operation
• Steps are made by the Driver in order to complete the communication with Sequencer(Sequence)
Declaring the “transaction item” with a handle.
get_next_item(): Calling the “get_next_item(<transaction_item_handle>)“.
Default transaction_handle is “req”. “get_next_item()” blocks the processing until the “req” transaction
object is available in the sequencer request FIFO & later “get_next_item” returns with the pointer of the
“req” object.
try_next_item(): This is a non-blocking variant of the get_next_item() method.
It will return a null pointer if there is no REQ sequence_item available in the sequencers request FIFO.
However, if there is a REQ sequence_item available it will complete the first half of the
driver-sequencer handshake and must be followed by an item_done() call to complete the handshake.
Next, Driver completes its side protocol transfer while working with the virtual interface.
item_done(): Calling the “item_done()” OR “item_done(rsp)“.
It indicates to the sequencer the completion of the process. “item_done” is a non-blocking call & can be
processed with an argument or without an argument.
If a response is expected by the Sequencer/Sequence then item_done(rsp) is called.
It results in Sequencer response FIFO is updated with the “rsp” object handle.
TLM PORTS
Introduction
• Transaction-Level Modeling (TLM) is used for communication among modules.
• TLM is the concept in which transaction based methods are implemented, these methods can be
used for communication between the modules
• The UVM provides TLM library with transaction-level interfaces, ports, exports, imp ports, and
analysis ports.
• All these TLM elements are required to send a transaction, receive transaction, and transport
from one component to another. where each one plays its unique role.
• TLM Interfaces consists of methods for sending and receiving the transaction
• All different types of TLM Ports are used like PIPES to connect between the components
Continu,,,
• The UVM TLM library provides,
• TLM1 – The TLM1 ports provide blocking and non-blocking pass-by-value transaction-level
interfaces.
• TLM2 – The TLM2 sockets provide blocking and non-blocking transaction-level interfaces with
well-defined completion semantics.
• Sequencer Port – A push or pull port, with well-defined completion semantics.
• Analysis – The analysis interface is used to perform non-blocking broadcasts of transactions to
connected components
Advantages
• Data is transferred at high level of abstraction. Transactions which are developed by extending
the uvm_sequence_item can be transferred between components using method calls.
• These methods are not hierarchical fixed, so that components can be reused.
• The advantages of TLM interfaces are
1) Higher level abstraction
2) Reusable. Plug and play connections.
3) Maintainability
4) Less code.
5) Easy to implement.
6) Faster simulation.
7) Connect to SystemC.
8) Can be used for reference model development.
Continu,,
• connection are between port and exports not in component
• these are parameterized by xtn class.
• multiple language supported(system c) where mailbox is only SV construct.
• mailbox is only unidirectional.
TLM1
• UVM TLM provides unidirectional and bidirectional,
1. TLM interfaces
2. ports
3. exports
4. imp ports
5. analysis portss
6. FIFOs
• Each TLM interface is either blocking, non-blocking, or a combination of these two.
1. Blocking – Blocking TLM methods call will not return until the transaction has been
successfully sent or retrieved
2. Non-blocking – Non-Blocking TLM methods call attempts to convey a transaction without
consuming simulation time
3. Combination – A combination interface contains both the blocking and nonblocking variants.
Continu,,
• TLM is used for communication among the components.
• The most basic TLM operation allows one component to sends a transaction packet to
another component.
• Let’s consider the two components producer and consumer, where the producer
generates and sends the transaction packet to the consumer through TLM ports.
• Symbolic representation of TLM Ports is shown below,
TLM methods & declarations
TLM
Tlm
interfac
es
ports exports analysis Tlm fifo
blocking
Non-blockin
g
Transport
put()
get()
peek()
try_put(
)
try_get()
try_peek()
can_put(
)
can_get(
)
can_peek()
transport
()
nb_transport
()
new() new()
imports
new() Write()
new()
flush()
size()
used()
is_empty(
)
is_full()
Unidirectional port
uvm_*_port#(T)
Bidirectional port
uvm_*_port#(REQ,
RSP)
Unidirectional export
uvm_*_export#(T)
Bidirectional export
uvm_*_export#(REQ,
RSP)
Unidirectional import
uvm_*_import#(T)
Bidirectional import
uvm_*_import#(REQ,
RSP)
uvm_analysis_export#
(T)
uvm_analysis_port#(T)
uvm_analysis_import#
(T)
Unidirectional import
uvm_tlm_fifo#(T)
Bidirectional import
uvm_tlm_analysis_fifo#(
T)
• put() is one of the TLM method which can be used to communicate between a Producer&
a Consumer.
• port is the interface which calls put() method and export is the interface which provides the
implementation of put() method.
• That way, TLM communication is independent of the Producer and Consumer abstraction level.
TLM interface put methods
/////////// Producer //////////////////////////
class producer extends uvm_component;
`uvm_component_utils(producer)
uvm_blocking_put_port #(txn) my_port;
function new (string name, uvm_component parent);
super.new(name, parent);
my_port = new (“my_port”, this);
endfunction: new
task run_phase(uvm_phase phase);
for (int packet = 1; packet<11; packet++)
begin
txn t;
t = txn::type_id::create(“t”, this);
`uvm_info(“PID”, $sformatf(“Packet no. %d is sent”,
packet), UVM_LOW)
my_port.put(t);
#10;
end
endtask: run_phase
endclass: producer
/////////// Consumer //////////////////////////
class consumer extends uvm_component;
`uvm_component_utils(consumer)
uvm_blocking_put_imp #(txn, consumer)
my_export;
function new (string name, uvm_component
parent);
super.new(name, parent);
my_export = new(“my_export”, this);
endfunction: new
task put (txn t);
case(t.kind)
t.READ: $display(“Read transaction”);
t.WRITE: $display(“Write transaction”);
endcase
endtask: put
endclass: consumer
p1.my_port.connect(c1.my_expor
t);
• In terms of purpose, get() method performs the same the put() does – only difference is that get()
pulls the transaction from the Producer yet put()push the transaction to theConsumer.
TLM interface get methods
///////////// Producer ///////////////////
class producer extends uvm_component;
`uvm_component_utils(producer)
///// Export Declaration
uvm_blocking_get_imp #(my_txn, producer)
my_export;
//// Constructor
function new (string name, uvm_component
parent);
super.new(name, parent);
my_export = new(“my_export”, this);
endfunction: new
//// get() task implementation
task get(output my_txn t);
my_txn tmp;
tmp = my_txn::type_id::create(“tmp”, this);
t = tmp;
`uvm_info (“PID”, $sformatf(“Transaction type %s
is sent”, t.kind), UVM_LOW)
endtask: get
endclass: producer
/////////// Consumer ///////////////////
class consumer extends uvm_component;
`uvm_component_utils(consumer)
//// Port Declaration
uvm_blocking_get_port #(my_txn) my_port;
function new (string name, uvm_component
parent);
super.new(name, parent);
my_port = new(“my_port”, this);
endfunction: new
task run_phase(uvm_phase phase);
for (int i=1; i<11; i++)
begin
my_txn txn;
`uvm_info("CID", $sformatf("Transaction no. %0d is
asked for", i), UVM_LOW)
my_port.get(txn);
#10;
`uvm_info(“CID”, $sformatf(“Transaction type %s
is received”, txn.kind), UVM_LOW)
end
endtask: run_phase
endclass: consumer
c1.my_port.connect(p1.my_expor
t);
TLM interface methods
• peek
• peek method obtain a transaction without consuming it
• Calling <port>.peek(trans) retrieve transaction from other component
• peek() method call is a blocking call.
• The returned transaction is not consumed. A subsequent peek or get will return the same transaction
Non-blocking methods
• try_put
• the try_put method is used to send a transaction to another component without blocking the
execution
• Calling <port>.try_put(trans) sends a transaction to another component, if possible
• try_put method returns 1 If the component is ready to accept the transaction, otherwise it returns 0
TLM interface methods
can_put
• can_put() method call returns 1 if the component is ready to accept the transaction, otherwise, it returns 0
• no argument must be passed to can_put, this method is used to see whether the other component is ready
to accept the trans
try_get
• the try_get method is used to retrieve transaction from another component without blocking the execution
• Calling <port>.try_get(trans) retrieve transaction from another component, if possible
• try_get method returns 1 if the transaction is available, otherwise, it returns 0
can_get
• can_get() method call returns 1 if the component can get transaction immediately, otherwise, it returns 0
• no argument must be passed to can_get, this method is used to see whether any transaction is available to
get
TLM interface methods
transport
• Calling <port>.transport(req, resp) method executes the given request and returns the
response in the given output argument
• The transport method call is blocking, it may block until the operation is complete
nb_transport
• Calling <port>.nb_transport(req,resp) the method executes the given request and returns the
response in the given output argument, if possible
• If for any reason the operation could not be executed immediately, then a 0 must be
returned, otherwise 1
TLM Export
• The TLM Export is a port that forwards a transaction from a child component to its parent
• The TLM Export has unidirectional and bidirectional ports
• An export can be connected to any compatible child export or imp port.
• It must ultimately be connected to at least one implementation of its associated interface
• Export Methods
• new
• This is a constructor method used for the creation of TLM Export.
TLM Export clases
uvm_*_export#(T) //unidirectional export class
uvm_*_export #(REQ,RSP) //bidirectional export class
Type parameters,
T – The type of transaction to be communicated by the export
REQ – The type of request transaction to be communicated by the
export
RSP – The type of response transaction to be communicated by the
export
function new (string name,uvm_component parent,int min_size=1,int max_size=1);
• The TLM Imp Port is used to receive the transactions at destination
• TLM Imp Ports has unidirectional and bidirectional ports
• import Methods -new
• This is a constructor method used for the creation of TLM import.
• TRANSPORT IMP CONSTRUCTOR
• MASTER AND SLAVE IMP CONSTRUCTOR
TLM port clases
uvm_*_imp #(T,IMP) //unidirectional port class
uvm_*_imp #(REQ, RSP, IMP, REQ_IMP, RSP_IMP) //bidirectional port class
Type parameters,
T – The type of transaction to be communicated by the imp
IMP – The type of the component implementing the interface. That is the class to which this imp will delegate.
REQ_IMP – The component type that implements the request side of the interface. Defaults to IMP. For master and slave
imps only.
RSP_IMP – The component type that implements the response side of the interface. Defaults to IMP. For master and
slave imps only.
function new(string name, IMP imp)
function new(string name, IMP imp, REQ_IMP req_imp=imp, RSP_IMP rsp_imp=imp)
TLM Import
• The TLM FIFO provides storage for the transactions between two independently running processes.
• We have seen put and get methods operates with only one outstanding transaction at a time i.e it is
allowed to send the transaction Only after consumption of the previously sent transaction, in this
case, the sender and receiver must be in sync else lead to blocking in one of the components.
• What if the case where the sender needs not wait for the receiver acknowledgment
• It just wants to store it in memory and the receiver can consume whenever required.
• In this sender and the receiver needs not to be in sync. Yes With TLM FIFO it is possible.
TLM FIFO
• In TLM FIFO, the sender pushes the transactions to FIFO and whenever it required reiver pops it
out or fetches from the FIFO
• Transactions are put into the FIFO via the put_export method
• Transactions are fetched from the FIFO via the get_peek_export method
• As its FIFO (First In First Out), transactions are fetched from the FIFO in the order they are put
TLM FIFO Classes
Continu,,,
uvm_tlm_analysis_fifo #(T)
uvm_tlm_fifo #(T)
TLM FIFO Methods
• size:The size indicates the maximum size of the FIFO,a value of zero indicates no upper bound
Calling size() returns the size of the FIFO
A return value of 0 indicates the FIFO capacity has no limit
• used: Returns the number of entries put into the FIFO
• is_empty: Returns 1 when there are no entries in the FIFO, 0 otherwise
• is_full: Returns 1 when the number of entries in the FIFO is equal to its size, 0 otherwise
• flush: Calling flush method will Remove all entries from the FIFO
after the flush method call used method returns 0 and the is_empty method returns 1
TLM Analysis_fifo
• An analysis_fifo is a uvm_tlm_fifo#(T) with an unbounded size and a write Method
• It can be used any place a uvm_analysis_imp is used
• Typical usage is as a buffer between a uvm_analysis_port in an initiator component and TLM1
target component
TLM Analysis FIFO Classes
• uvm_tlm_analysis_fifo#(T) An analysis_fifo is a uvm_tlm_fifo#(T) with an unbounded size and a
write method
Continu,,,
The analysis_export provides the write method to all connected analysis ports and parent exports
analysis_export #(T)
Method:function void write (T t)
• Write() method:
• Calling <port>.write(trans) method will broadcasts a transaction to any number of listeners.
• Write method call is a nonblocking call
• As along with the interface methods, TLM ports are required for the communication between
the components.
RAL Model
Introduction
• UVM Register Layer is also referred to as UVM Register Abstraction Layer (UVM RAL) which
is defined as the conversion of register sequences into bus sequences
• Need to write and read from those registers in order to verify the proper working of the design.
• It is mainly used to identify the bugs in the testbench instead of doing traditional verification.
• The UVM Register Layer provides a standard base class libraries that enable users to implement
the object-oriented model to access the DUT registers and memories.
• RAL model can be used with multiple interfaces, memory implementation inside the RAL model
and their access methods, information on predictor model to predict the register value based on
their operation, etc..
• It has also a list of ready-made UVM register sequences.
• By merely configuring these sequences users can access and verify the functionality of all the
design registers and memories.
How do verification engineers make use of RAL?
• Basically all test benches has to mimic the design behavior in order to verify the demanded
functionality of the design.
• Writing and Reading of registers is taken care by the TLM FIFO feature of the UVM.
• But the problem arises when hardware has to modify the register value.
• For example an IN register has to reflect the value of the bit on the pin which has to be read from
the hardware.
• The replicated design in the test bench has to modify some “virtual register” based on the hardware
changes, that has to be compared with the actual register bank for it’s functional correctness.
• That “Virtual register” bank inside the test bench is abstracted as the Register Abstraction
Layer(RAL).
• Generally UVM scoreboards make use of RAL to compare the register values inside RTL with the
required value inside the test bench.
For register access, can’t we proceed without RAL?
• Yes, we can.
• But RAL provides a set of base classes and methods with a set of rules which easies the
effort required for register access.
RAL with testbench
UVM Register Model Overview
• The register model is composed of a hierarchy of blocks that map to the design hierarchy
• Which means the RAL model consists of design register fields, registers, and memory.
• Blocks can contain,
• registers
• register files
• memories
• other blocks
• UVM RAL library provides the base class of each and each class has the default built-in methods
in it.
• Register classes cant be used as it is, they must be specialized via extensions to provide an abstract
view that corresponds to the design registers and memories
Continu,,,
uvm_reg shall consist of one or more uvm_reg_field
uvm_reg_file shall consist of one or more uvm_reg
uvm_reg_block shall consist of one or more uvm_reg_file or uvm_mem
Mapping of register model components to the environmental
components
Due to a large number of registers in design, this specialization shall be done by a register
model generator
register model generator
• Register model generators are outside the scope of the UVM library.
• A register model can be written or it can be created using a register generator application.
• Writing or Generating the register model is based on a design register specification.
• Writing a register model is easy, but complex designs will be having hundreds or thousands of
registers. in that case, writing the register model is tedious.
• The easiest way to construct a register model is by using a register generation application or
automation tool.
• Automation tools will generate the register model by taking register specification as input, this
includes reg name, width, register fields, access permissions, etc.
• There are paid and free register generators available.
• some of them are RGM – Register and Memory Package by Cadence, ralgen by Synopsys,
Semifore’s RDL, Duolog’s graphical Bitwise, Agnisys’ IDesignSpec, Mentor Graphics’ Certes
Testbench Studio, and Magillem MRV (Magillem Register View).
Mapping UVM RAL elements with design elements
• Design block containing four registers, which have two, three, one and one fields respectively, an
internal memory, and external memory.
The architecture of the CPU shows that it
consists of multiple modules
• uvm_reg_block: shall be used to
represent each design module each
module consists of many registers
• uvm_reg_file: shall group the module
registers each module consists of
registers
• uvm_reg shall be used to implement the
design register each register consists of
one or multiple fields
• uvm_reg_field type shall be used
for field implementation
RAL Building blocks
• There are four blocks, which can be used to build basic RAL. They are
• Register block
• Register file
• Register
• Register Filed
1. Register block
• The reg block is written by extending
the uvm_reg_block.
• A block corresponds to a design component/hierarchy
with its own interface(s), registers, register files,
memories, and sub-blocks.
• In simple words, a block can be referred to as a design
module with its own interface(s), registers, register files,
memories, and sub-blocks.
• And it will be having unique address decoding to route
the access to it.
• A register model is an instance of a register block,
which may contain any number of registers, register files,
memories, and other blocks.
2. Register file
• The reg file is written by extending
the uvm_reg_file.
• The reg file shall be used to group the number
of registers or register files.
• The block diagram shows the register file
consists of reg_file_0, reg_file_1 and
reg_file_2.
• Each register file consists of a set of registers.
( registers are shown only in reg_file_2
• Assume that there are registers in reg_file_0
and reg_file_1 )
3. 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.
4. Register field
• The register field is declared with the type uvm_reg_filed.
• Fields represent a contiguous set of bits.
• All data values are modeled as fields.
• A field is contained within a single register but may have different access policies. access policies
are explained in the next sections.
Complete RAL block diagram
Register Access Methods
• UVM RAL library classes have built-in 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
• Back door access uses simulator database access routines and this happens in zero
simulation time
Register Access Methods
Register Access 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.
Front_door access back_door access
Register Access Methods
Peek and poke
• peek() reads the DUT register value using a backdoor
• poke() writes a value to DUT register using backdoor
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.
Register Access Methods
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.
mirror
• mirror() reads the updated DUT register values.
• The mirroring can be performed in the front door or
back door( peek() ).
Register Access Methods
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.
reset
• reset() sets the register desired and mirrored value to the pre-defined reset value
Constructing Register Model
1. Register Field
• Register fields are declared with uvm_reg_field class type.
• 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
(refer to register example)
uvm_reg_field reg_name;
2. Register
• The register is constructed by writing a class extended from the uvm_reg class.
• There must be one class per unique register type
• The name of the register type class must be unique within the scope of its declaration
• The uvm_reg_field::configure() method shall be called from the register type build method
class my_reg extends uvm_reg;
rand uvm_reg_field Field_0;
rand uvm_reg_field Field_1;
endclass
class my_reg extends uvm_reg;
virtual function build();
this.Field_0 = my_reg::type_id::create(.name(“Field_0”), .parent(null),.contxt(get_full_name()))
;
this.Field_0.configure(this, ...);
endfunction
endclass
3. Register File
• A register file type is constructed by writing a class extended from the uvm_reg_file class
• The name of the register file type class must be unique within the scope of its declaration
• Register files can contain other register files.
• The build() method shall call the configure() method for all register and register file class properties
• specifying get_block() for the parent block and this for the parent register file
class my_reg extends uvm_reg;
virtual function build();
this.Field_0 = my_reg::type_id::create(.name(“Field_0”), .parent(null),.contxt(get_full_name()))
;
this.Field_0.configure(this, ...);
endfunction
endclass
class my_reg_file extends uvm_reg_file;
`uvm_object_utils(my_reg_file)
endclass
3. Register File
class reg_file extends uvm_reg_file;
virtual function build();
uvm_reg_block blk = get_block();
this.rf = reg_file_0::type_id::create(.name($psprintf(“%s.rf1”, get_name())).p
arent(null),.contxt(blk.get_full_name()));
this.rf.configure(get_block(), this, ...);
this.rf.build();
this.rf.add_hdl_path();
endfunction
endclass
Register file has 2 methods
1. map() method
2.set_offset() method
a. map method()
• A virtual map() function, with uvm_reg_map and address offset arguments map() method shall call
uvm_reg_map::add_reg()
• For all register class properties, adding the value of the address offset argument to the offset of the
register in the register file map() method shall call the map() method of all register file class
properties
• Adding the value of the address offset argument to the offset of the register file base offset
• The map() method may call the add_hdl_path() method for all register or register file class
properties
virtual function map(uvm_reg_map mp, uvm_reg_addr_t
offset);
mp.add_reg(this.reg_0, base_addr + 'h0);
mp.add_reg(this.reg_1, base_addr + 'h4;
this.rf.map(mp, base_addr + 'h100);
endfunction
b. set_offset() method
• A virtual set_offset() function, with a uvm_reg_map and address offset arguments, may also
be implemented
• The set_offset() method shall call the set_offset() method for all register and register file
class properties
virtual function set_offset(uvm_reg_map
mp, uvm_reg_addr_t offset);
this.reg_0.set_offset(mp, base_addr + 'h0);
this.reg_1.set_offset(mp, base_addr + 'h4);
this.rf.set_offset(mp, base_addr + 'h100);
endfunction
Memory Types
• A memory type is constructed using a class extended from the uvm_mem class
• The name of the memory type class must be unique within the scope of its
declaration
class my_mem extends uvm_mem;
`uvm_object_utils(my_mem)
endclass
Register block
• A block is constructed by writing a class extended from the uvm_reg_block class
• The name of the block type class must be unique within the scope of its declaration
• The block type must contain a class property for each,
• named address map
• register
• register file
• memory
• sub-block
• These shall have the rand attribute
• The build() method shall instantiate all named address maps by calling the
uvm_reg_block::create_map() method
class my_blk extends uvm_reg_block;
`uvm_object_utils(my_blk)
endclass
UVM RAL Predictor
• We know that the UVM Register model maintains the latest design register values in it. but how
Register model will get to know the latest values?
• This will be done by the UVM RAL component Predictor.
• UVM RAL Predictor predicts the register access done through the register model and updates the
RAL Model registers.
• UVM RAL provides the base class uvm_reg_predictor. uvm_reg_predictor updates the register
model based on observed transactions published by a monitor.
• Any register access done through the register model is predicted and updated inherently by the base
classes (implicit prediction)
• Prediction of the register values in the model can also be done explicitly using an external predictor
which also takes care of interface bus transactions not occurring through the register model (explicit
prediction)
Implicit prediction
• Implicit prediction only requires the integration of the register model with one or more bus
sequencers
• Updates to the mirror are predicted automatically (i.e., implicitly) by the register model after
the completion of each read, write, peek, or poke operation
Explicit prediction
• Explicit prediction requires the register model to be integrated with both the bus sequencers
and corresponding bus monitors
• In this mode, the implicit prediction is turned off and all updates to the mirror are predicted
externally (i.e., explicitly) by a uvm_reg_predictor component, one for each bus interface
UVM Register Model Adapter
• With the UVM Register model, we do design register access, i.e WRITE to the design
register or READ from the design register by calling RAL methods.
• Finally, these transactions have to be placed to design bus, this will be done by RAL
component Adapter.
• The RAL adapter acts as a converter between the RAL model and Interface.
• It converts transactions of RAL methods to Interface/Bus transactions.
• The Adapter converts between register model read, write methods and the interface-specific
transactions
• The transaction adapter is implemented by extending the uvm_reg_adapter class and
implementing the reg2bus() and bus2reg() methods
UVM RAL reg2bus
• reg2bus method converts the RAL transactions to Interface (bus) transactions
UVM RAL bus2reg
• bus2reg method converts the Interface (bus) transactions to RAL transactions
class tb_env extends uvm_env;
reg_model regmodel;
uvm_reg_predictor#(ahb_trans) ahb2reg_predictor;
reg2ahb_adapter reg_adapter;
ahb_agent ahb;
virtual function void build_phase(uvm_phase phase);
ahb2reg_predictor = new(“ahb2reg_predictor”, this);
endfunction
virtual function void connect_phase(uvm_phase phase);
if (regmodel.get_parent() == null) begin
reg_adapter = reg2ahb_adapter::type_id::create(“reg_adapter”,,get_full_n
ame());
...
ahb2reg_predictor.map = regmodel.AHB;
ahb2reg_predictor.adapter = reg_adapter;
ahb.monitor.ap.connect(ahb2reg_predictor.bus_in);
end
endfunction
Integrating RAL to Bus Agent
• Once after the RAL implementation, RAL has to be connected with the Bus Agent.
• This section describes connecting RAL with the sequencer and monitor of the bus.
Integrating Bus Sequencers
• All integration approaches require a register model to be configured with one or more bus
sequencers.
• The register model becomes a property of a uvm_reg_sequence subtype that executes
• Directly on a bus sequencer, if there is only one bus interface providing access to the DUT
registers
• As a virtual sequence, if there are one or more bus interfaces providing access to the DUT
registers;
• As a register sequence running
Integrating the Register Model with a Bus Monitor
• By default, the register model updates its mirror copy of the register values implicitly.
• Every time a register is read or written through the register model, its mirror value is
updated.
• If other agents on the bus interface perform read and write transactions outside the context of
the register model
• The register model must learn of these bus operations to update its mirror accordingly.
• Integration is accomplished by first instantiating a uvm_reg_predictor component
• uvm_reg_predictor component is then connected to the bus monitor’s analysis port
class tb_env extends uvm_env;
reg_model regmodel;
uvm_reg_predictor#(ahb_trans) ahb2reg_predictor;
reg2ahb_adapter reg_adapter;
ahb_agent ahb;
virtual function void build_phase(uvm_phase phase);
ahb2reg_predictor = new(“ahb2reg_predictor”, this);
endfunction
virtual function void connect_phase(uvm_phase phase);
if (regmodel.get_parent() == null) begin
reg_adapter = reg2ahb_adapter::type_id::create(“reg_adapter”,,get_full_nam
e());
...
ahb2reg_predictor.map = regmodel.AHB;
ahb2reg_predictor.adapter = reg_adapter;
ahb.monitor.ap.connect(ahb2reg_predictor.bus_in);
end
...
endfunction
Register defines
• UVM register library has the defines declared in it.
• These are being used in the RAL model base classes, user can override these defines.
uvm_reg_defines are,
1. `UVM_REG_ADDR_WIDTH
2. `UVM_REG_DATA_WIDTH
3. `UVM_REG_BYTENABLE_WIDTH
4. `UVM_REG_CVR_WIDTH
Register defines
1. UVM_REG_ADDR_WIDTH
• Maximum address width in bits
Default value is 64
• Used to define the
uvm_reg_addr_t type
2. UVM_REG_DATA_WIDTH
• Maximum data width in bits
Default value is 64
• Used to define the
uvm_reg_data_t type
3. UVM_REG_BYTENABLE_WIDTH
• Maximum number of byte enable bits
• Default value is one per byte in
`UVM_REG_DATA_WIDTH
• Used to define the
uvm_reg_byte_en_t type
4. UVM_REG_CVR_WIDTH
• Maximum number of bits in a
uvm_reg_cvr_t coverage model set
• Default value is 32
THANK YOU

Universal Verification Methodology - Used to describe the Hardware Digital Circuits

  • 1.
  • 2.
    What is verificationmethodology • Methodology is a systematic way of doing things with a set of standard rules and guidelines • Verification methodology means while doing verification we have to follow some rules and regulations • Different verification methodologies • AVM-Advanced verification methodology • RVM-Reference verification methodology • OVM-Open verification methodology • VMM-Verification methodology manual • UVM-Universal verification methodology
  • 3.
    What is Universalverification methodology • It is a methodology for functional verification using system verilog • It complete with a supporting library of sv code • UVM was created by Accellera based on OVM version (Open verification methodology) • UVM library contains: which can be used for shorthand notation of complex implementation • Component classes for building testbench components like generator/driver/monitor etc. • Reporting classes for logging, Factory for object substitution. • Synchronization classes for managing concurrent process. • Policy classes for printing, comparing, recording, packing, and unpacking of uvm_object based. • TLM Classes for transaction level interface. • Sequencer and Sequence classes for generating realistic stimulus. • And Macros
  • 4.
    Introduction • UVM isa methodology based on SystemVerilog language and is not a language on its own. • It enables efficiency in terms of reuse and is also currently part of IEEE 1800.2 working group. • Modularity and Reusability – The methodology is designed as modular components (Driver, Sequencer, Agents , env etc) which enables reusing components across unit level to multi-unit or chip level verification as well as across projects. • Separating Tests from Testbenches – Tests in terms of stimulus/sequencers are kept separate from the actual testbench hierarchy and hence there can be reuse of stimulus across different units or across projects • Simulator independent – The base class library and the methodology is supported by all simulators and hence there is no dependence on any specific simulator
  • 5.
    Continu,,, • Sequence methodology--gives good control on stimulus generation. There are several ways in which sequences can be developed which includes randomization, layered sequences, virtual sequences etc which provides a good control and rich stimulus generation capability. • Config mechanisms--simplify configuration of objects with deep hierarchy. The configuration mechanism helps in easily configuring different testbench components based on which verification environment uses it and without worrying about how deep any component is in testbench hierarchy • Factory mechanisms--simplifies modification of components easily. Creating each components using factory enables them to be overridden in different tests or environments without changing underlying code base.
  • 6.
    Difference sv anduvm • SystemVerilog classes allow Object Orientated Programming (OOP) techniques to be applied to testbenches. The UVM itself is a library of base classes which facilitate the creation of structured testbenches. • reporting mechanism- helps in debugging environment with many agents.here we can filter and log messages with the help of verbosity .by changing the verbosity , printing of messages can be controlled.severity of messages are fatal , error , warning , info , file name and line number where file and line are default. Like sv we don't have to give display command again and again within a class. • Base class library - here we have inbuilt methods like compare, copy , print , reporting mechanism , macros which extended class inherits since all the extended class are derived from same base class.
  • 7.
    Continu,,, • Configuration class- config class is a database where we configure all the parameters needed throughout the hierarchy using set and get .sv we don't not have configuration facility. • Phasing - since all the components are derived from uvm_ component , phasing helps in synchronisation of each and every component before proceeding to next phase. • TLM - in SV mailbox is used for passing the message between component but here tlm that is transaction level modelling which supports multiple languages and it is port to port connection not like mailbox which is component to component connection.
  • 8.
    Continu,,, • Factory -here factory is class that manufactures components and objects which gives the ability to modify and no of objects that makes TB hierarchy in more predictable manner.overriding of components or objects becomes much more easy using factory concept using create() method instead of new() method • Virtual sequencer and sequences - for keeping the independency between TB writer and test case writer .here test case writer don't have to worry about the path in order to start a sequence which can not be done in SV. • Reusable and more portable . No need to be dependent on test.able to change the objects from top level .
  • 9.
  • 10.
    uvm_void: • It isthe base class for all UVM classes. • This class is the top of the class inheritance hierarchy. • System verilog has no need for a class that serves as a base of all classes, but specman e need to have a common root base class, so they added it to the UVM library, but it turns out it is not needed in the UVM uvm_object: • The uvm_object class is the base class for all UVM data and hierarchical classes. • Its primary role is to define a set of methods for such common operations as create, copy, compare, print, and record
  • 11.
    Contin… uvm_transaction: • The ~uvm_transaction~is the root base class for UVM transactions, which, unlike ~uvm_components~, are transient in nature. • It extends <uvm_object> to include a timing and recording interface. • Simple transactions can derive directly from ~uvm_transaction~, while sequence-enabled transactions derive from ~uvm_sequence_item~ uvm_report_object: • The uvm_report_object provides an interface to the UVM reporting facility. • Through this interface, components issue the various messages that occur during simulation. • A report consists of an id string, severity, verbosity level, and the textual message itself. • They may optionally include the filename and line number from which the message came.
  • 12.
    uvm_transaction vs uvm_sequence_item •uvm_transaction is the base class for modeling any transaction which is derived from uvm_object . • uvm_sequence_item is an extension of uvm transaction class. • A sequence item is nothing but a transaction that groups some information together and also adds some other information like: sequence id (provide id for sequence_item then easily identify by the driver), and transaction id, etc. • It is recommended to use uvm_sequence_item for implementing sequence based stimulus. • Proper sequencer and Driver communication won't Happen if it is not extending from sequence_item.
  • 13.
  • 14.
  • 15.
    Sequence_item: • The sequence-itemis written by extending the uvm_sequence_item, uvm_sequence_item inherits from the uvm_object via the uvm_transaction class. • Therefore uvm_sequence_item is of an object type. • The sequence-item consist of data fields required for generating the stimulus. • In order to generate the stimulus, the sequence items are randomized in sequences. • Therefore data properties in sequence items should generally be declared as rand and can have constraints defined. • Data fields represent the following types of information, 1. Control Information – a type of transfer, transfer size, etc ex:rand bit wr; 2. Payload Information – data content of the transfer ex: rand bit [7:0] wdata 3. Configuration Information – mode of operation, error behavior, etc 4. Analysis Information – fields used to capture information from DUT, ex: output bit[7:0]rdata; as analysis information fields will be used for capturing response, except these fields the other fields can be declared as rand and can have constraints associated with it.
  • 16.
    class mem_seq_item extendsuvm_sequence_item; //Control Information rand bit [3:0] addr; rand bit wr_en; rand bit rd_en; //Payload Information rand bit [7:0] wdata; //Analysis Information bit [7:0] rdata; //Utility and Field macros, `uvm_object_utils_begin(mem_seq_item) `uvm_field_int(addr,UVM_ALL_ON) `uvm_field_int(wr_en,UVM_ALL_ON) `uvm_field_int(rd_en,UVM_ALL_ON) `uvm_field_int(wdata,UVM_ALL_ON) `uvm_object_utils_end //Constructor function new(string name = "mem_seq_item"); super.new(name); endfunction //constaint, to generate any one among write and read constraint wr_rd_c { wr_en != rd_en; }; endclass Sequence_item example
  • 17.
    Sequence: • A sequencegenerates a series of sequence_item’s and sends it to the driver via sequencer, Sequence is written by extending the uvm_sequence • A uvm_sequence is derived from an uvm_sequence_item • a sequence is parameterized with the type of sequence_item, this defines the type of the item sequence that will send/receive to/from the driver. class write_sequence extends uvm_sequence #(mem_seq_item); .... .... endclass
  • 18.
    class mem_sequence extendsuvm_sequence#(mem_seq_item); `uvm_object_utils(mem_sequence) //Constructor function new(string name = "mem_sequence"); super.new(name); endfunction virtual task body(); req = mem_seq_item::type_id::create("req"); //create the req (seq item) begin (); start_item assert(req.randomize()); //randomize the req finish_item(req); end endtask endclass Sequence example
  • 19.
    Sequencer • The sequencercontrols the flow of request and response sequence items between sequences and the driver • Sequencer and driver uses TLM Interface to communicate transactions • uvm_sequencer and uvm_driver base classes have seq_item_export and seq_item_port defined respectively, User needs to connect them using TLM connect method driver.seq_item_port.connect(sequencer.seq_item_export); • A sequencer can be written by extending the uvm_sequencer parameterized with the seq_item type. class mem_sequencer extends uvm_sequencer#(mem_seq_item); `uvm_sequencer_utils(mem_sequencer) function new (string name, uvm_component parent); super.new(name, parent); endfunction : new endclass : mem_sequencer
  • 20.
    class mem_sequencer extendsuvm_sequencer#(mem_seq_item); `uvm_object_utils(mem_sequencer) //Constructor function new(string name = "mem_sequencer"); super.new(name); endfunction endclass Sequencer example
  • 21.
    UVM Driver • Adriver is written by extending the uvm_driver • uvm_driver is inherited from uvm_component, Methods and TLM port (seq_item_port) are defined for communication between sequencer and driver • The uvm_driver is a parameterized class and it is parameterized with the type of the request sequence_item and the type of the response sequence_item • UVM_Driver Methods • get_next_item • This method blocks until a REQ sequence_item is available in the sequencer. • try_next_item • This is a non-blocking variant of the get_next_item() method. It will return a null pointer if there is no REQ sequence_item available in the sequencer. • item_done • The non-blocking item_done() method completes the driver-sequencer handshake and it should be called after a get_next_item() or a successful try_next_item() call.
  • 22.
    class mem_driver extendsuvm_driver #(mem_seq_item); virtual mem_if vif; `uvm_component_utils(mem_driver) // Constructor function new (string name, uvm_component parent); super.new(name, parent); endfunction : new function void build_phase(uvm_phase phase); super.build_phase(phase); if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif)) `uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"}); endfunction: build_phase // run phase virtual task run_phase(uvm_phase phase); forever begin seq_item_port.get_next_item(req); //respond_to_transfer(req); drive(); seq_item_port.item_done(); end endtask : run_phase// drive virtual task drive(); req.print(); `DRIV_IF.wr_en <= 0; `DRIV_IF.rd_en <= 0; @(posedge vif.DRIVER.clk); `DRIV_IF.addr <= req.addr; if(req.wr_en) begin `DRIV_IF.wr_en <= req.wr_en; `DRIV_IF.wdata <= req.wdata; end end endtask : drive endclass : mem_driver Driver example
  • 23.
    Output Monitor • Theuser-defined monitor is extended from uvm_monitor, uvm_monitor is inherited by uvm_component • Output monitor is a passive entity that samples the DUT signals through the virtual interface and converts the signal level activity to the transaction level • Monitor samples DUT signals but does not drive them • The monitor should have an analysis port (TLM port) and a virtual interface handle that points to DUT signals.
  • 24.
    class mem_monitor extendsuvm_monitor; // Virtual Interface virtual mem_if vif; uvm_analysis_port #(mem_seq_item) item_collected_port; // Placeholder to capture transaction information. mem_seq_item trans_collected; `uvm_component_utils(mem_monitor) // new - constructor function new (string name, uvm_component parent); super.new(name, parent); trans_collected = new(); item_collected_port = new("item_collected_port", this); endfunction : new function void build_phase(uvm_phase phase); super.build_phase(phase); if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif)) `uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"}); endfunction: build_phase // run phase virtual task run_phase(uvm_phase phase); item_collected_port.write(trans_collected); endtask : run_phase endclass : mem_monitor Monitor class example
  • 25.
    Agent • a user-definedagent is extended from uvm_agent, uvm_agent is inherited by uvm_component • An agent typically contains a driver, a sequencer, and a monitor • Agents can be configured either active or passive Active agent • Active agents generate stimulus and drive to DUT • An active agent shall consists of all the three components driver, sequencer, and monitor Passive agent • Passive agents sample DUT signals but do not drive them A passive agent consists of only the monitor • An agent can be configured as ACTIVE/PASSIVE by using a set config method, the default agent will be ACTIVE. the set config can be done in the env or test. // connect_phase function void connect_phase(uvm_phase phase); if(get_is_active() == UVM_ACTIVE) begin driver.seq_item_port.connect(sequencer.seq_item_export); end endfunction : connect_phase
  • 26.
    class mem_agent extendsuvm_agent; //declaring agent components mem_driver driver; mem_sequencer sequencer; mem_monitor monitor; // UVM automation macros for general components `uvm_component_utils(mem_agent) // constructor function new (string name, uvm_component parent); super.new(name, parent); endfunction : new // build_phase function void build_phase(uvm_phase phase); super.build_phase(phase); if(get_is_active() == UVM_ACTIVE) begin driver = mem_driver::type_id::create("driver", this); sequencer = mem_sequencer::type_id::create("sequencer", this); end monitor = mem_monitor::type_id::create("monitor", this); endfunction : build_phase // connect_phase function void connect_phase(uvm_phase phase); if(get_is_active() == UVM_ACTIVE) begin driver.seq_item_port.connect(sequencer.seq_item_export); end endfunction : connect_phase endclass : mem_agent Agent with example
  • 27.
    Scoreboard • The user-definedscoreboard is extended from uvm_scoreboard, uvm_scoreboard is inherited by uvm_component. • the scoreboard will check the correctness of the DUT by comparing the DUT output with the expected values • the scoreboard will receive the transactions from the Monitors implemented inside agents • Monitor and scoreboard will communicate via TLM ports and exports • Scoreboard shall compare the DUT output values with, • The golden reference values • The values Generated from the reference model Environment • The user-defined environment is derived from uvm_env, uvm_env is inherited from uvm_component. • The environment is the container class, It contains one or more agents, as well as other components such as the scoreboard, top-level monitor, and checker.
  • 28.
    class mem_scoreboard extendsuvm_scoreboard; `uvm_component_utils(mem_scoreboard) uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export; // new - constructor function new (string name, uvm_component parent); super.new(name, parent); endfunction : new function void build_phase(uvm_phase phase); super.build_phase(phase); item_collected_export = new("item_collected_export", this); endfunction: build_phase // write virtual function void write(mem_seq_item pkt); $display("SCB:: Pkt recived"); pkt.print(); endfunction : write endclass : mem_scoreboard Scoreboard with example
  • 29.
    class mem_model_env extendsuvm_env; mem_agent mem_agnt; `uvm_component_utils(mem_model_env) // new - constructor function new(string name, uvm_component parent); super.new(name, parent); endfunction : new // build_phase function void build_phase(uvm_phase phase); super.build_phase(phase); mem_agnt = mem_agent::type_id::create("mem_agnt", this); endfunction : build_phase endclass : mem_model_env Environment class example
  • 30.
    Test • The user-definedtest is derived from uvm_test, uvm_test is inherited from uvm_component. • The test defines the test scenario for the testbench • test class contains the environment, configuration properties, class overrides etc • A sequence/sequences are created and started in the test • The UVM testbench is activated when the run_test() method is called, the global run_test() task should be specified inside an initial block. • There can be many user-defined test cases.Among multiple test cases, a particular test case can be selected and execute on two methods, 1. by specifying the test name as an argument to run_test(); example: run_test("mem_model_test"); 2. by providing the UVM_TESTNAME command line argument example: <SIMULATION_COMMANDS> +UVM_TESTNAME=mem_model_test initial begin run_test(); end
  • 31.
    class mem_model_test extendsuvm_test; `uvm_component_utils(mem_model_test) mem_model_env env; mem_sequence seq; function new(string name = "mem_model_test",uvm_component parent=null); super.new(name,parent); endfunction : new virtual function void build_phase(uvm_phase phase); super.build_phase(phase); env = mem_model_env::type_id::create("env", this); seq = mem_sequence::type_id::create("seq"); endfunction : build_phase task run_phase(uvm_phase phase); seq.start(env.mem_agnt.sequencer); endtask : run_phase endclass : mem_model_test Test class example
  • 32.
    Top TestBench top isthe module, it connects the DUT and Verification environment components. Typical Testbench_top contains, • DUT instance • interface instance • run_test() method • virtual interface set config_db • clock and reset generation logic • wave dump logic
  • 33.
    module tbench_top; //clock andreset signal declaration bit clk; bit reset; //clock generation always #5 clk = ~clk; //reset Generation initial begin reset = 1; #5 reset =0; end //creatinng instance of interface, inorder to connect DUT and testcase mem_if intf(clk,reset); //DUT instance, interface signals are connected to the DUT ports memory DUT ( .clk(intf.clk), .reset(intf.reset), .addr(intf.addr), .wr_en(intf.wr_en), .rd_en(intf.rd_en), .wdata(intf.wdata), .rdata(intf.rdata) ); //enabling the wave dump initial begin uvm_config_db#(virtual mem_if)::set(uvm_root::get(),"*","mem_intf",intf); end initial begin run_test(); end endmodule Top_module example
  • 34.
    uvm_component • Quasi StaticEntity (after build phase it is available throughout the simulation) • Always tied to a given hardware(DUT Interface) Or a TLM port • Having phasing mechanism for control the behavior of simulation • uvm_component have two arguments, a name and a uvm_component parent. • Components are instantiated at start of simulation uvm_object • Dynamic Entity (create when needed, transfer from one component to other & then dereference) • Not tied to a given hardware or any TLM port • No phasing mechanism • uvm_object have one arguments i.e name • Objects are instantiated at run time Difference between uvm_component and uvm_object
  • 35.
    UVM Class Hierarchy MessageReport Hierarchy and Phases Name
  • 36.
  • 37.
  • 38.
    Phases • Depending ofthe testbench architecture, there will be several components and there needs to be some ordered way of constructing these as well as executing • Phases are not used in objects because objects wont present throughout the simulation, they just created when they needed. • Each components goes through predefined set of phases and it cannot proceed to the next phase until all components finish their execution in current phase • Phases are represented by callback methods, A set of predefined phases and corresponding callbacks are provided in uvm_component. The Method can be either a function or task. • Any class deriving from uvm_component may implement any or all of these callbacks, which are executed in a particular order • So, uvm phases acts as a synchronizing mechanism in the life cycle of simulation • All phases can be grouped into 3 categories 1.Build_phase 2. Run-time phase 3.Clean-up phase
  • 39.
  • 40.
    Build_phase • Build_phase areexecuted at start of simulation • Purpose is to construct the testbench components • All build_phase methods are functions, therefore execute in zero-simulation time • Build_phase is top-down approach Connect_phase • Used to make TLM connections between components • It has to occur after build_phase • It works from bottom to up in testbench component hierarchy Virtual function void build_phase(uvm_phase phase); super.build_phase(phase); endfunction Virtual function void connect_phase(uvm_phase phase); super.build_phase(phase); driv.seq_item_port.connect(seqr.seq_item_export) endfunction
  • 41.
    end_of_elaboration • Used tomake any final adjustments to tb connectivity, tb configuration before simulation starts • Used to display environment hierarchy • This phase executes from bottom_up 2.Run_Phase • The UVM Testbench stimulus is generated and executed during the run time phases which follows the build phases. • Run phase was present in OVM as well but additional other phases were added to UVM to give finer run-time for tests, scoreboard and other components. virtual function void end_of_elaboration_phase(uvm_phase phase); uvm_top.print_topology(); uvm_report_info(get_full_name(),”end_of_elaboration”,uvm_LO W); endfunction task run_phase(uvm_phase phase); :::::::::: endtask
  • 42.
    start_of_simulation • It isa function which occurs before the time consuming part of the testbench begins. • It is intended to be used for displaying banners; Testbench topology; or configuration information. It is called in bottom up order. run_Phase: • The run phase occurs after the start_of_simulation phase and is used for the stimulus generation and checking activities of the Testbench. • The run phase is implemented as a task, and all uvm_component run tasks are executed in parallel. • Transactors such as driver and monitor will nearly always use this phase. virtual function void start_of_simulation phase(uvm_phase phase); uvm_report_info(get_full_name(),”start_of_simulation”,uvm_LO W); endfunction task run_phase(uvm_phase phase); :::::::::: endtask
  • 43.
    pre_reset: • pre_reset phasestarts at the same time as the run phase. • Its purpose is to take care of any activity that should occur before the reset. • E.g. waiting for a power signal to go active. reset: • Specially for DUT or Interface specific reset behavior. • This phase would be used to generate reset to put the DUT/Interface into a default state. post_reset: • This phase is intended for any activity required just after the reset phase. task reset_phase(uvm_phase phase); :::::::::: endtask task pre_reset_phase(uvm_phase phase); :::::::::: endtask task post_reset_phase(uvm_phase phase); :::::::::: endtask
  • 44.
    pre_configure: • This phaseis intended for anything that is required to prepare for the DUT configuration process after the DUT is out of reset. configure: • configure phase is used to put the DUT into a known state before the stimulus could be applied to the DUT. • For example – programming the control registers of the device for a particular test scenario. post_configure: • This phase is intended to wait for the effect of the configuration to propagate through the DUT. task configure_phase(uvm_phase phase); :::::::::: endtask task pre_configure_phase(uvm_phase phase); :::::::::: endtask task post_configure_phase(uvm_phase phase); :::::::::: endtask
  • 45.
    pre_main: • pre_main isused to ensure that all the components needed to generate the stimulus are ready to do so. main: • main phase is where the stimulus specified by the Test case is generated and applied to the DUT. It completes in two conditions: One is the stimulus gets exhausted and another is when timeout occurs. Sequences are started in this phase to generate the stimulus. post_main: • Used for any final act after the main phase. task main_phase(uvm_phase phase); :::::::::: endtask task pre_main_phase(uvm_phase phase); :::::::::: endtask task post_main_phase(uvm_phase phase); :::::::::: endtask
  • 46.
    pre_shutdown: • This phaseis acts like a buffer to apply any stimulus before the shutdown phase starts. shutdown: • The shutdown phase is to ensure that the effects of stimulus generated during the main phase has propagated through the DUT and that the resultant data has drained away. • It might also be used to execute the time consuming sequences that read status registers. post_shutdown: • post_shutdown is intended for any final activity before exiting the run phase. After it UVM Testbench starts the cleanup phase. task shutdown_phase(uvm_phase phase); :::::::::: endtask task pre_shutdown_phase(uvm_phase phase); :::::::::: endtask task post_shutdown_phase(uvm_phase phase); :::::::::: endtask
  • 47.
    Clean up Phase: •The clean up phases are used to extract information from Scoreboards and Functional Coverage Monitors to determine whether the test case has passed and/or reached its coverage goals immediately it will be cleaned the stimulus, it will give space for another stimulus • The clean up phases are implemented as functions and therefore take zero time to execute. • They work from the bottom to the top of the component hierarchy. extract: • The extract phase is used to retrieve and process information from Scoreboards and Functional Coverage Monitors. • This may include the calculation of statistical information used by the report phase. • This phase is usually used by Analysis side components.
  • 48.
    check: • This phaseis also used by the Analysis Components. • This phase is used to check if the DUT behaved correctly and to find any error that may have occurred during the stimulus execution. report: • The report phase is used to display the results of the simulation to the standard output or to write the results to file. • This phase is usually used by Analysis Components. final: • The final phase is used to complete any other outstanding actions that the Testbench has not already completed.
  • 49.
    How UVM Phasingis triggered? • To start a UVM Testbench, the run_test() method has to be called from the static part of the Testbench. • It is usually called from within an initial block in the top level module of the Testbench. • Once the run_test() method is called, it constructs the root componentof the UVM environment & then triggers/initiates the UVM Phasing process. • A phase starts only when all components in the previous phase have dropped their objections. • A phase continues to execute until all components have dropped their objections in the current phase // Top level Testbench module module top_tb; .... .... // UVM start up: initial begin uvm_config_db #(virtual bus_if)::set(null, "*", "BUS_vif" , BUS); run_test("bidirect_bus_test"); end endmodule: top_tb
  • 50.
    • The run_test()method can be passed with a string argument, which in the above code is “bidirect_bus_test”, to define the default type name which is used as the root node of the Testbench Hierarchy. • In addition, run_test() method also checks for a command line plusarg called UVM_TESTNAME and uses that plusarg string to lookup a factory registered uvm_component to override any default type name. • Hence to execute the “bidirect_bus_test” using command line plusarg, we’ve to use the following command line: • % <simulator executable> +UVM_TESTNAME=bidirect_bus_test // Top level Testbench module module top_tb; .... .... // UVM start up: initial begin uvm_config_db #(virtual bus_if)::set(null, "*", "BUS_vif" , BUS); run_test("bidirect_bus_test"); end endmodule: top_tb
  • 51.
    Which phase takesmore time and why ? • Run phase takes more time because they are the major phases to consume simulation time. • The time taken for each test can be different, because all tests have different aspects of design Phase Synchronization • By default all components allow all other components to complete a phase before all components move to next phase VIP1 VIP2 VIP3 Reset configure main shutdown Reset Reset configure configure main main shutdown shutdown time Note:In Phase domain each component phases are independent of another component phase- do not use phase domain
  • 52.
    How simulation endsin UVM methodology? • UVM has a phased execution which consists of a set of build phases, run phases and check phases. • The run() phase is where the actual test simulation happens and during this phase every component can raise an objection in beginning and hold it until it is done with its activity. • Once all components drops the objection, the run() phase completes and then check() phase of all components execute and then the test ends. • This is how a normal simulation ends, but there are also controls on simulation timeouts to terminate the run() phase if some component hangs due to a bug in design or testbench. • When the run() phase starts, a parallel timeout timer is also started. • If the timeout timer reaches one of the specified timeout limits before the run() phase completes, the run() phase will timeout, an error message will be issued and then all phases post run() will get executed and test ends after that.
  • 53.
  • 54.
    Why we gofor uvm reporting • Verilog $display doesn’t allow filtering and control of messages. • Changing the verbosity on the command line doesn’t require that u recompile and re-elaborate the design to observe different trace messages • If we are using verilog system tasks we need to re-compile the whole RTL again to activate it which consumes time, energy & resources. • Uvm reporting services are built into all components and are derived from a parent uvm_report_object class • UVM Reporting or Messaging has a rich set of message-display commands & methods to alter the numbers & types of messages that are displayed without re-compilation of the design.
  • 55.
    reporting • UVM Reportingor Messaging has a rich set of message-display commands & methods to alter the numbers & types of messages that are displayed without re-compilation of the design. • UVM Reporting also includes the ability to mask or change the severity of the message to adapt the required environment condition. • UVM Reporting has the concepts of Severity, Verbosity and Simulation Handing Behavior. Each of them can be independently specified and controlled. • Components are inherited from uvm_report_object, hence components already have methods and functions to display messages
  • 56.
    Concepts of reporting •Severity • Severity indicates importance • Examples are Fatal, Error, Warning & Info • Verbosity • Verbosity indicates filter level • Examples are None, Low, Medium, High, Full & Debug • Simulation Handling Behavior • Simulation handling behavior controls simulator behavior • Examples are Exit, Count, Display, Log, Call Hook & No Action • Simulation Handling Behavior in-fact is the Action taken by the Simulator which is dependent on Severity being produced by the Verification Environment. We’ll see more details shortly about it.
  • 57.
    Reporting methods • Fourbasic reporting functions that can be used with different verbosity levels • id -- a unique id to form a group of messages. • message -- The message text • verbosity -- the verbosity of the message, indicating its relative importance. If this number is less than or equal to the effective verbosity level, then the report is issued, subject to the configured action and file descriptor settings. • filename/line -- If required to print filename and line number from where the message is issued, use macros, `__FILE__ and `__LINE__. virtual function void uvm_report_info (string id,string message,int verbosity=UVM_MEDIUM,string filename="",int line=0) virtual function void uvm_report_warning (string id,string message,int verbosity=UVM_MEDIUM,string filename="",int line=0) virtual function void uvm_report_error(string id,string message,int verbosity=UVM_LOW, string filename="",int lin e=0) virtual function void uvm_report_fatal (string id,string message,int verbosity=UVM_NONE, string filename="",int li ne=0)
  • 58.
    Reporting method examples •Four basic reporting functions that can be used with different verbosity levels • UVM has six levels of verbosity with each one represented by an integer. • UVM verbosity level is required only for uvm_info, cant be used for uvm_warning, uvm_error, uvm_fatal uvm_report_info (string id,string message,int verbosity=UVM_MEDIUM,string filename="",int line= 0) uvm_report_warning (string id,string message,int verbosity=UVM_MEDIUM,string filename="",int l ine=0) uvm_report_error (string id,string message,int verbosity=UVM_LOW, string filename="",int line=0) uvm_report_fatal (string id,string message,int verbosity=UVM_NONE, string filename="",int line=0) uvm_report_info (get_type_name (), $sformatf ("None level message"), UVM_NONE); uvm_report_info (get_type_name (), $sformatf ("Low level message"), UVM_LOW); uvm_report_info (get_type_name (), $sformatf ("Medium level message"), UVM_MEDIUM); uvm_report_info (get_type_name (), $sformatf ("High level message"), UVM_HIGH); uvm_report_info (get_type_name (), $sformatf ("Full level message"), UVM_FULL); uvm_report_info (get_type_name (), $sformatf ("Debug level message"), UVM_DEBUG); uvm_report_warning (get_type_name (), $sformatf ("Warning level message")); uvm_report_error (get_type_name (), $sformatf ("Error level message")); uvm_report_fatal (get_type_name (), $sformatf ("Fatal level message"));
  • 59.
    uvm_actions • uvm_action areused to control the simulation behavior • These methods associate the specified action or actions with reports of the given severity, id, or severity-id pair. • Following are the actions defined: UVM_NO_ACTION -- Do nothing UVM_DISPLAY -- Display report to standard output UVM_LOG -- Write to a file UVM_COUNT -- Count up to a max_quit_count value before exiting UVM_EXIT -- Terminates simulation immediately UVM_CALL_HOOK -- Callback the hook method .
  • 60.
    Messages Verbosity: • Fundamentallythe Verbosity level describes how verbose a Testbench can be. • The default Verbosity is UVM_MEDIUM. • There are different Verbosity level being supported by UVM. • In case of default Verbosity level i.e. UVM_MEDIUM, any messages with UVM_HIGH or above are filtered out. UVM_NONE 0 UVM_LOW 100 UVM_MEDIUM (Default) 200 UVM_HIGH 300 UVM_FULL 400 UVM_DEBUG 500 Highest priority messages UVM_NONE lowest priority messages UVM_DEBUG
  • 61.
    How to changeverbosity settings • Verbosity settings can be modify by using command line switches and method calls 1. Changing verbosity using Cmd_line switch • The advantage of cmd_line switches is that if the design has been compiled into a simulation executable • The design can be re-simulated using a different verbosity setting without recompiling the design and testbench • The most common option is cmd_line switch +UVM_VERBOSITY= <verbosity> • Where <verbosity> can be anyone of the following UVM_NONE, UVM_LOW, UVM_MEDIUM, UVM_HIGH, UVM_DEBUG • Changing the verbosity for specific component using cmd_line option +uvm_set_verbosity=<comp>,<id>,<verbosity>,<phase>
  • 62.
    • Change theverbosity to UVM_LOW for all components in the agent simv +UVM_TESTNAME=test1 +uvm_set_verbosity=uvm_test_top.e.agnt.*,_ALL_,UVM_LOW,run Changing verbosity using method calls • Using verbosity method calls that selectively change verbosity settings for specific components or for entire component hierarchy • function void set_report_verbosity_level (int verbosity_level) function void set_report_severity_action (uvm_severity severity,uvm_action action) function void set_report_id_action (string id,uvm_action action) function void set_report_severity_id_action (uvm_severity severity,string id,uvm_action action)
  • 63.
    Using get_type_name, get_full_name,get_name • There are three built in methods which is used to call inside the reporting macros • They are get_name(), get_type_name(), get_full_name() • get_name(): returns the name of the object `uvm_error(get_name(), “driver“) // :drv [D] driver • get_full_name(): returns the full hierarchical name of this object `uvm_error(get_full_name(), “driver“) // :top.test.env.agent1.drv[DRV]driver • get_type_name(): returns the type name of the object `uvm_error(get_type_name(), “driver“) // :drv [DRV]driver
  • 64.
  • 65.
    Configuration database • Configdb allows passing of objects and data to various components in the testbench. • It is built on top of the UVM resource database, uvm_resource_db • uvm_resource_db is a data sharing mechanism where hierarchy is not important. • The database is essentially a lookup table which uses a string as a key and where you can add and retrieve entries. • resource database makes use of type-parameterized classes. • This means that T in the class header must be replaced by the actual type of the resource you want to work with. • Each type of resource, therefore, gets its own specialized class. class uvm_resource_db#(type T=uvm_object)
  • 66.
    Continu,, • The uvm_config_dbclass is the recommended way to access the resource database. • A resource is any piece of information that is shared between more than one component or object. • We use uvm_config_db::set to put something into the database and uvm_config_db::get to retrieve information from the database. • The uvm_config_db class is parameterized, so the database behaves as if it is partitioned into many type-specific "mini databases." • There are no limitations on the the type - it could be a class, a uvm_object, a built in type such as a bit, byte, or a virtual interface. • There are two typical uses for uvm_config_db. • The first is to pass virtual interfaces from the DUT to the test, and the second is to pass configuration classes down through the testbench.
  • 67.
    Functions in resource_db •There are several common functions of the uvm_resource_db class that allow you to add or retrieve data. Methods Description get_by_type This function gets the resource by the type specified by the parameter so the only argument is the scope. get_by_name This function gets a resource by using both the scope and name given when it was added to the database. set This function creates a new resource in the database with a value, scope, and name that will be used for retrieval. read_by_name This function locates a resource by scope and name and returns the value through an output argument. read_by_type This function locates the resource using only the scope as a lookup and returns the value through an output argument. write_by_name This function locates the resource by scope and name and writes the value to it. If the resource does not exist then it will be created like the set function. write_by_type This function locates the resource by the scope and writes the value to it. If the resource does not exist it is created.
  • 68.
    WHEN IS THECONFIGURATION DATABASE USED? • The uvm_config_db is used when hierarchy is important. • With the uvm_config_db, the user not only can add an object to the database, but can also specify, with great detail, the level of access to retrieval by specifying the hierarchy • The classic example of uvm_config_db usage is with sharing a virtual interface. • A SystemVerilog interface is instantiated at the top level of the testbench and connects to the ports of the device under test (DUT). • For the UVM testbench to be able to drive to driver or monitor this interface, it needs to have access to it. • The various interface instantiations can be added to the database with the access level controlled, since it can then be retrieved by the appropriate component only if it is in the specified hierarchy. • Virtual interfaces are not the only use for the configuration database. Any object can be stored and retrieved. • Other common uses of the configuration database include sharing configuration objects or setting whether an agent is active or passive.
  • 69.
    HOW IS DATASTORED AND RETRIEVED? • configdatabase, there are only two functions that are most commonly used with the configuration database: • • set – adds an object to the uvm_config_db • • get – retrieves an object from the uvm_config_db • Note that all the methods of the uvm_config_db class are static so they must be called with the scope resolution operator, as is the case with the uvm_resource_db. • Once again, type parameterization is used so the actual type for the resource, T, must be given. • Also noteworthy, the default parameter type of the uvm_resource_db is uvm_object, whereas the default type for the uvm_config_db is int class uvm_config_db#(type T=int) extends uvm_resource_db#(T)
  • 70.
    Contin…… • set() functionhas four arguments • The get() function which is used to retrieve items from the database. • It is important to note that objects are not removed from the database when you call get(). • The actual variable is passed in as an inout formal function argument and so is performed as a copy-in-copy-out operation. Static function void set(uvm_component cntxt, string inst_name, string field_name, Tvalue) cntxt: The context is the hierarchical starting point of where the database entry is accessible. string inst_name:The instance name is the hierarchical path that limits accessibility of the database entry. String field_name:The field name is the label used as a lookup for the database entry. Tvalue: The value to be stored in the database of the parameterized type. By default the type is int. Static function void get(uvm_component cntxt, string inst_name, string field_name, inoutTvalue) cntxt:The context (cntxt) is the starting point for the search. string inst_name: The instance name in this case can be an empty string since it is relative to the context String field_name:The field name is the label given when the object was added to the database
  • 71.
    Contin…… • An interfacehas been instantiated in the top level and now needs to be added to the uvm_config_db using the set()function. • The most basic way to do this is to use the set() function and allow the virtual interface to be widely accessible from anywhere within the testbench. • The first argument is the context (cntxt) which is the starting point of the lookup search. • The example uses uvm_root::get() to acquire the top-level so the search will start at the top of the hierarchy in this case. • Normally "this" would be used as the context if the call to set() is within a class, but to set the virtual interface correctly in the database, the code has to be placed inside a module, so there would be no class context. • The second argument is the instance name. In this example, the interface is being made globally available amongst all components so the wildcard, "*", is used. • The third argument is the field name which is a label used for lookup. • Finally, the value argument is the actual instance of the interface. uvm_config_db#(virtual tb_intf)::set(uvm_root::get(),"*", "dut_intf", vif
  • 72.
    Contin…… • In mostcases, you do not want to make a database entry globally available. • Since a global scope is essentially a single namespace, it makes reuse more difficult if everything is stored in this scope. • To restrict its access, use the hierarchical path in conjunction with the wildcard character as shown below • Adding other objects into the uvm_config_db is just as straightforward as adding a virtual interface • The important thing to remember is that each entry needs a unique field name or label (if the global scope is being used), or the path needs to be limited in such a way that non-unique labels do not conflict as the scopes are now limited to specific areas of the naming hierarchy uvm_config_db#(TYPE)::set(this,"*.path","label", value)
  • 73.
    Contin…… • uvm_config_db: avirtual interface, an integer value, and a configuration object. Also, there is a generic call to the get() function. • To retrieve the integer value the label would be "retry_count" and the value stored in this entry would be assigned to the rty_cnt property in the object that is calling the get() function.
  • 74.
    Config database methods •set() • get() • exists() • wait_modified() • Convenience tasks
  • 75.
    set() • Static functionof the class uvm_Config_db to set a variable in the configuration database static function void set ( uvm_component cntxt, string inst_name, string field_name, T value); virtual function void build_phase (uvm_phase phase); ... uvm_config_db #(int) :: set (null, "uvm_test_top.m_env.m_apb_agent", "cov_enable", 1); ... endfunction // Set virtual interface handle under name "apb_vif" available to all components below uvm_test_top, indicated by the * uvm_config_db #(virtual apb_if) :: set (null, "uvm_test_top.*", "apb_vif", apb_if); // Set an int variable to turn on coverage collection for all components under m_apb_agent uvm_config_db #(int) :: set (null, "uvm_test_top.m_env.m_apb_agent.*", "cov_enable", 1); // Consider you are in agent's build_phase then you may achieve the same effect by uvm_config_db #(int) :: set (this, "*", "cov_enable", 1);
  • 76.
    • Static functionof the class uvm_Config_db to set a variable in the configuration database static function bit get ( uvm_component cntxt, string inst_name, string field_name, T value); // Get virtual interface handle under name "apb_vif" into local virtual interface handle at m_env level uvm_config_db #(virtual apb_if) :: get (this, "*", "apb_vif", apb_if); // Get int variable fails because no int variable found in given scope uvm_config_db #(int) :: get (null, "uvm_test_top", "cov_enable", cov_var); get() static function bit exists ( uvm_component cntxt, string inst_name, string field_name, bit spell_chk); exists() // Check if interface handle exists at the given scope if (! uvm_config_db #(virtual apb_if) :: exists (this, "*", "apb_vif")) `uvm_error ("VIF", "Could not find an interface handle", UVM_MEDIUM)
  • 77.
    Convenience tasks static taskwait_modified ( uvm_component cntxt, string inst_name, string field_name); class my_agent extends uvm_agent; virtual task run_phase (uvm_phase phase); ... // Waits until loopCount variable gets a new value uvm_config_db #(int) :: wait_modified (this, "", "loopCount"); endtask endclass class my_env extends uvm_env; my_agent m_apb_agent; virtual task main_phase (uvm_phase phase); ... // Update loopCount variable in database for (int i = 0; i < N; i++) begin ... uvm_config_db #(int) :: set (this, "m_apb_agent", "loopCount", i); end endtask endclass wait_modified() typedef uvm_config_db #(uvm_bitstream_t) uvm_config_int; typedef uvm_config_db #( string) uvm_config_string; typedef uvm_config_db #(uvm_object) uvm_config_object; typedef uvm_config_db #(uvm_object_wrappet) uvm_config_wrapper;
  • 78.
  • 79.
    Introduction • As perthe recommended UVM methodology, we should never construct new components and/or transactions using new() class constructor. • Instead, it is recommended that – we should make calls to a look-up table to create the requested components and transactions. • This special look-up table is called “Factory” in UVM. Entries to this look-up table are made by registering the components and/or transactions while defining them. • To create a component/transaction using Factory, create() method is used. • The purpose of factory in UVM is to change the behaviour of the testbench without any change in code or without any compilation. • Basically it's for overriding purpose where you can override any object/component or class into the another without any further modification.
  • 80.
    Contin… • UVM Factoryfacilitates an object of one type to be substituted with an object of derived type without having to change the structure of the Testbench or modify the Testbench code. • This behavior is called “overriding” and there are following types of overriding is possible with UVM Factory 1. Type Overriding(set_type_override or Global override) 2. Instance Overriding(set_inst_override) • Overriding helps to replace one transaction/sequence with another to generate new scenarios or conditions without making any change in the Testbench structure/code. • Similarly, a new version of the components can be brought into the Testbench without any change in the structure of the Testbench & beauty of all this is that – it happens all the fly at the run time.
  • 81.
    • Registering uvm_objectwith factory `uvm_object_utils(sequence) • Using create() to instantiate objects • <type>::type_id::create(“<name>”); seq=sequence::type_id::create(“seq”); this keyword not used for objects, objects doesn’t have any hierarchy • Registering uvm_component with factory `uvm_component_utils(monitor) • Using create() to instantiate components • <type>::type_id::create(“<name>”, <parent>); mon=monitor::type_id::create(“mon”, this); • this keyword is required, because testbench component have a hierarchy • this keyword represents parent Factory registrations for object and component
  • 82.
    Why do weneed factory? • Consider a case in which you have a agent and driver, monitor are instantiated inside the agent. • Now you want to change the base driver with the extended driver, you can the extend the base driver and create a new driver class. • Now to use this extended driver class you need to change the code in the agent class also • e.g new instantiation of the extended driver class, if not then again you need to extend the agent class and use extended driver class, which is too much of overhead • To avoid this we use factory. In factory by registering the driver class we can now override the base driver with extended driver without changing the code in the agent class.
  • 83.
    Components/objects creating usingnew() calling the new() is not recommended and limits reuse Class driver extends uvm_component; `uvm_component_utils(driver) function new(string name, uvm_component parent); super.new(name,parent); endfunction :::::: virtual task driver_transfer(); :::::: endclass Class agent extends uvm_component; `uvm_component_utils(agent) driver my_drv; function new(string name, uvm_component parent); super.new(name,parent); my_drv=new(“my_drv”, this); endfunction :::::: virtual task driver_transfer(); :::::: endclass Suppose user wants to add extra data or trying to override the driver task using new() Class my_project extends driver; `uvm_component_utils(my_project) function new(string name, uvm_component parent); super.new(name,parent); endfunction :::::: virtual task driver_transfer(); Super.drive_transfer(); :::::: endclass • Agent instantiates previous driver not the my_project driver • To add this m_project driver in agent need to extend the agent class & potentially other clases need to instantiate dis driver • Sine driver, agent clases need to be extended dis makes code modification all over the testbench • Uvm factory introduces overriding option by using factory concept overriding the exact driver from outside the agent instead of using new(), user needs to use create() • The main difference between new() and create() is, by using create we can overriding the code without any modification ,but by using new() its not possible
  • 84.
    Components/objects creating usingcreate() calling the create() Class driver extends uvm_component; `uvm_component_utils(driver) function new(string name, uvm_component parent); super.new(name,parent); endfunction :::::virtual task driver_transfer(): endclass Class agent extends uvm_component; `uvm_component_utils(agent) driver my_drv; function new(string name, uvm_component parent); super.new(name,parent); my_drv=new(“my_drv”, this); endfunction :::::: virtual task driver_transfer(); :::::: endclass • We will register the components & objects using type_id and while creating the instances of components or objects we will create them by the factory method “create” which is called using type_id • type_id of driver is overridden with child_driver type_id, inside agent create method is called with child_driver type_id hence the instance of child_driver is created. • With the help of the factory, we can override the type of underlying components or objects from the top-level component without having to edit the code. class test extends uvm_test; .... function void build_phase(uvm_phase phase); .... // calling factory overriding method to override the type_id of driver with child_driver type_id set_type_override_by_type(driver::get_type(),child_driver::get_type()); endfunction Class child_driver extends driver; `uvm_component_utils(child_driver) function new(string name, uvm_component parent); super.new(name,parent); endfunction :::::: virtual task driver_transfer(); Super.drive_transfer(); :::::: Endclass The code inside the agent, the driver instance will be created as the create method is called with type_id of driver later in one test case suppose we need to override this particular driver with child_driver, we can override it from top-level component (test) using the factory as shown below.
  • 85.
    Example for factoryreuse • Consider a case when you are moving from one project to another. • For example, USB 2.0 to USB 3.0. You understand that you leverage everything from USB 2.0 testbench and use it as USB 3.0 testbench with exception of driver. • It will so much of a pain to replace each instance of driver class. • Now, this is where factory comes to your rescue. • When you override the old driver class with the new driver class using factory method set_type_override, all instances of old driver class will be replaced by the new instance of USB3.0 driver class.
  • 86.
    Factory methods set_type_override_by_type set_inst_override_by_type Instance override Typeoverride set_type_override_by_name set_inst_override_by_name set_type_override_by_type(original_type::get_type(), substitute_type::get_type(), replace=1) factory.set_type_override_by_name(“original_name", “substitute_name") ; <original_type>::type_id::set_inst_override(<substitute_type>::get_type(), <path_string>); factory.set_inst_override_by_name("a_packet","bad_pac ket", "*");
  • 87.
    Type Overriding: • Intype-override, overriding the current class type(original class) with other class type(derived class or substitute_type) • where “replace” is a bit which is when set equals to 1, enables the overriding of an existing override else existing override is honoured. <original_type>::type_id::set_type_override(<substitute_type>::get_type(), replace); class my_test extends uvm_test; `uvm_component_utils(my_test) env e; function new (string name, uvm_component parent); super.new(name, parent); endfunction: new function void build_phase(uvm_phase phase); super.build_phase(phase); my_driver::type_id::set_type_override(my_updated_driver::get_typ e(),1); (or) //set_type_overidde_by_type(my_driver::get_type(),my_updated_d river()); e = env::type_id::create("e", this); endfunction: build_phase task run_phase (uvm_phase phase); ... ... ... endtask: run_phase endclass: my_test //Overriding my_driver class with my_updated_driver • Note here is the order of 2 commands i.e. set_type_override() to be placed before the create() command inside the build_phase() of the my_test class. • Only with this order of commands the substitution will get into effect. • In case the order is reversed, original driver in the code i.e. my_driver will be constructed instead of the intended driver i.e. my_updated_driver.
  • 88.
    Instance override • InInstance Overriding, as name indicates it substitutes ONLY a particular instance of the component OR a set of instances with the intended component. • The instance to be substituted is specified using the UVM component hierarchy. path should be agent1 * check only below components path should be agent1* check agent1 and below components Agent_top agent1 agent2 drv2 mon2 seqr2 drv1 mon1 seqr1 <original_type>::type_id::set_inst_override(<substitute_type>::get_type(), <path_string>);
  • 89.
    Instance Override class my_testextends uvm_test; `uvm_component_utils(my_test) env e; function new (string name, uvm_component parent); super.new(name, parent); endfunction: new function void build_phase(uvm_phase phase); super.build_phase(phase); my_driver::type_id::set_inst_override(my_updated_driver::get_type(), "top.e.agent.drvr"); set_inst_overidde_by_type(my_driver::get_type(),my_updated_driver(),”*”) or set_inst_overidde_by_type(my_driver::get_type(),my_updated_driver(),”agen t.*”); or set_inst_overidde_by_type(my_driver::get_type(),my_updated_driver(),”agen t*”); e = env::type_id::create("e", this); endfunction: build_phase task run_phase (uvm_phase phase); ... ... ... endtask: run_phase endclass: my_test • Objects or sequence related objects are generally only used with type override • since the instance override approach relates to a position in the UVM Testbench component hierarchy which objects do not take part in.
  • 90.
    • For complexUVM Testbench Environment, it is often useful to print out the structure of the testbench in tabular form, that were registered with the Factory. • A great technique to view the structural composition of the Testbench classes and the Factory setup is to call the this.print() and factory.print() methods in the end_of_elaboration_phase() (as shown in Code below) from the top-level testbench. • By the time the end_of_elaboration_phase() executes, the entire environment has already been built and connected • So these print() methods show what had been built in the Testbench and the types that were registered with the factory. Debugging the UVM Testbench Structure & Factory Content function void end_of_elaboration_phase(uvm_phase phase); super.end_of_elaboration_phase(phase); this.print(); factory.print(); endfunction: end_of_elaboration_phase
  • 91.
  • 92.
    • They are2 ways to implement important transaction methods 1. First method is using field_macros 2. Second method is using manual coding techniques by overriding build in do_methods • Using field macros is relatively simple but they can be inefficient during simulation & difficult to debug if something does go wrong • Cadence and mentor uvm experts avoid using field macros due to their coding and simulation inefficiencies Transaction methods in sequence_item
  • 93.
    Utility & fieldmacros example class ABC extends uvm_object; rand bit [15:0] m_addr; rand bit [15:0] m_data; `uvm_object_utils_begin(ABC) `uvm_field_int(m_addr, UVM_DEFAULT) `uvm_field_int(m_data, UVM_DEFAULT) `uvm_object_utils_end function new(string name = "ABC"); super.new(name); endfunction endclass //field macros are invoked inside the utility macros class ABC extends uvm_object; `uvm_object_utils(ABC) rand bit [15:0] m_addr; rand bit [15:0] m_data; `uvm_field_utils_begin(ABC) // error dis line `uvm_field_int(m_addr, UVM_DEFAULT) `uvm_field_int(m_data, UVM_DEFAULT) `uvm_field_utils_end function new(string name = "ABC"); super.new(name); endfunction endclass Output:Error because uvm_object_utils macro also calls uvm_field_macros no need to define as uvm_field_utils_begin, its illegal
  • 94.
    Utility macros • Utilitymacros are used to declare either object or component going to store in factory • Field_macros are used inside the utility macros • If we specify begin-end, it wont be block , if it is not blocked then every time we need to change fields • Once it blocks by using begin-end, then particular fields will freeze with particular i.e., int, enum etc uvm_object_utils_begi n(); :::::::; uvm_object_utils_end begin-end , whatever inside fields we r going to write that will be block uvm_component_utils_be gin(); :::::::; uvm_object_utils_end
  • 95.
    Utility macros For simpleobjects with no field macros, use `uvm_object_utils(TYPE) For simple objects with field macros, use `uvm_object_utils_begin(TYPE) `uvm_field_* macro invocations here `uvm_object_utils_end For parameterized objects with no field macros, use `uvm_object_param_utils(TYPE) For parameterized objects, with field macros, use `uvm_object_param_utils_begin(TYPE) `uvm_field_* macro invocations here `uvm_object_utils_end For simple objects with no field macros, use `uvm_component_utils(TYPE) For simple objects with field macros, use `uvm_component_utils_begin(TYPE) `uvm_field_* macro invocations here `uvm_component_utils_end For parameterized objects with no field macros, use `uvm_component_param_utils(TYPE) For parameterized objects, with field macros, use `uvm_component_param_utils_begin(TYPE) `uvm_field_* macro invocations here `uvm_component_utils_end
  • 96.
    UVM Field Macros •The `uvm_field_* macros are invoked inside of the `uvm_*_utils_begin and `uvm_*_utils_end macro blocks to form “automatic” implementations of the core data methods: copy, compare, pack, unpack, record, print, and sprint. • By using the macros, you do not have to implement any of the do_* methods inherited from uvm_object. • The field macros expand into general inline code that is not as run-time efficient nor as flexible as direct implementions of the do_* methods. • Each `uvm_field_* macro is named according to the particular data type it handles: integrals, strings, objects, queues, etc., and each has at least two arguments: FIELD and FLAG. FIELD-int FLAG-UVM_DEFAULT `uvm_object_utils_begin(ABC) `uvm_field_int(m_addr, UVM_DEFAULT) `uvm_field_int(m_data, UVM_DEFAULT) `uvm_object_utils_end
  • 97.
    Field_macros FIELD description FIELDTYPE Description `UVM_FIELD_* MACROS Macros that implement data operations for scalar properties. `uvm_field_int Implements the data operations for any packed integral property. `uvm_field_object Implements the data operations for an uvm_object-based property. `uvm_field_string Implements the data operations for a string property. `uvm_field_enum Implements the data operations for an enumerated property. `uvm_field_real Implements the data operations for any real property. `uvm_field_event Implements the data operations for an event property. • Macros that implement data operations for scalar properties.
  • 98.
    Field_macros FIELD description FIELDTYPE Description `UVM_FIELD_SARRAY_* MACROS Macros that implement data operations for one-dimensional static array properties. `uvm_field_sarray_int Implements the data operations for a one-dimensional static array of integrals. `uvm_field_sarray_object Implements the data operations for a one-dimensional static array of uvm_object-based objects. `uvm_field_sarray_string Implements the data operations for a one-dimensional static array of strings. `uvm_field_sarray_enum Implements the data operations for a one-dimensional static array of enums. `UVM_FIELD_ARRAY_* MACROS Macros that implement data operations for one-dimensional dynamic array properties. `uvm_field_array_int Implements the data operations for a one-dimensional dynamic array of integrals. `uvm_field_array_object Implements the data operations for a one-dimensional dynamic array of uvm_object-based objects. `uvm_field_array_string Implements the data operations for a one-dimensional dynamic array of strings. `uvm_field_array_enum Implements the data operations for a one-dimensional dynamic array of enums.
  • 99.
    Field_macros FIELD description FIELDTYPE Description `UVM_FIELD_QUEUE_* MACROS Macros that implement data operations for dynamic queues. `uvm_field_queue_int Implements the data operations for a queue of integrals. `uvm_field_queue_object Implements the data operations for a queue of uvm_object-based objects. `uvm_field_queue_string Implements the data operations for a queue of strings. `uvm_field_queue_enum Implements the data operations for a one-dimensional queue of enums. `UVM_FIELD_AA_*_STRING MACROS Macros that implement data operations for associative arrays indexed by string. `uvm_field_aa_int_string Implements the data operations for an associative array of integrals indexed by string. `uvm_field_aa_object_string Implements the data operations for an associative array of uvm_object-based objects indexed by string. `uvm_field_aa_string_string Implements the data operations for an associative array of strings indexed by string
  • 100.
    Field_macros FIELD description FIELDTYPE Description `UVM_FIELD_AA_*_INT MACROSMacros that implement data operations for associative arrays indexed by an integral type. `uvm_field_aa_object_int Implements the data operations for an associative array of uvm_object-based objects indexed by the int data type. `uvm_field_aa_int_int Implements the data operations for an associative array of integral types indexed by the int data type. `uvm_field_aa_int_int_unsign ed Implements the data operations for an associative array of integral types indexed by the int unsigneddata type. `uvm_field_aa_int_integer Implements the data operations for an associative array of integral types indexed by the integer data type. `uvm_field_aa_int_integer_un signed Implements the data operations for an associative array of integral types indexed by the integer unsigned data type.
  • 101.
    Field_macros FIELD description RECORDINGMACROS The recording macros assist users who implement the uvm_object::do_record method. `uvm_record_attribute Vendor-independent macro for recording attributes (fields) to a vendor-specific transaction database. `uvm_record_field Macro for recording name-value pairs into a transaction recording database. PACKING MACROS The packing macros assist users who implement the uvm_object::do_packmethod. PACKING - WITH SIZE INFO `uvm_pack_intN Pack an integral variable. `uvm_pack_enumN Pack an integral variable. `uvm_pack_sarrayN Pack a static array of integrals. `uvm_pack_arrayN Pack a dynamic array of integrals. `uvm_pack_queueN Pack a queue of integrals.
  • 102.
    Field_macros FIELD description PACKING- NO SIZE INFO `uvm_pack_int Pack an integral variable without having to also specify the bit size. `uvm_pack_enum Pack an enumeration value. `uvm_pack_string Pack a string variable. `uvm_pack_real Pack a variable of type real. `uvm_pack_sarray Pack a static array without having to also specify the bit size of its elements. `uvm_pack_array Pack a dynamic array without having to also specify the bit size of its elements. `uvm_pack_queue Pack a queue without having to also specify the bit size of its elements.
  • 103.
    Field_macros FIELD description UNPACKING- WITH SIZE INFO `uvm_unpack_intN Unpack into an integral variable. `uvm_unpack_enumN Unpack enum of type TYPE into VAR. `uvm_unpack_sarrayN Unpack a static (fixed) array of integrals. `uvm_unpack_arrayN Unpack into a dynamic array of integrals. `uvm_unpack_queueN Unpack into a queue of integrals. UNPACKING - NO SIZE INFO `uvm_unpack_int Unpack an integral variable without having to also specify the bit size. `uvm_unpack_enum Unpack an enumeration value, which requires its type be specified. `uvm_unpack_string Pack a string variable. `uvm_unpack_real Unpack a variable of type real. `uvm_unpack_sarray Unpack a static array without having to also specify the bit size of its elements. `uvm_unpack_array Unpack a dynamic array without having to also specify the bit size of its elements. `uvm_unpack_queue Unpack a queue without having to also specify the bit size of its elements.
  • 104.
    Field_macros FIELD description FIELDTYPE Description `uvm_field_aa_int_byte Implements the data operations for an associative array of integral types indexed by the byte data type. `uvm_field_aa_int_byte_unsig ned Implements the data operations for an associative array of integral types indexed by the byte unsigned data type. `uvm_field_aa_int_shortint Implements the data operations for an associative array of integral types indexed by the shortint data type. `uvm_field_aa_int_shortint_un signed Implements the data operations for an associative array of integral types indexed by the shortint unsigned data type. `uvm_field_aa_int_longint Implements the data operations for an associative array of integral types indexed by the longint data type. `uvm_field_aa_int_longint_un signed Implements the data operations for an associative array of integral types indexed by the longint unsigned data type. `uvm_field_aa_int_key Implements the data operations for an associative array of integral types indexed by any integral key data type. `uvm_field_aa_int_enumkey Implements the data operations for an associative array of integral types indexed by any enumeration key data type.
  • 105.
    Field_macros FLAG description FLAGDescription UVM_ALL_ON Set all operations on (default) UVM_DEFAULT Use the default flag settings UVM_NOCOPY Do not copy this field UVM_NOCOMPARE Do not compare this field UVM_NOPRINT Do not print this field UVM_NODEFPRINT Do not print the field if it is the same as its UVM_NOPACK Do not pack or unpack this field UVM_PHYSICAL Treat as a physical field. Use physical setting in policy class for this field UVM_ABSTRACT Treat as an abstract field. Use the abstract setting in the policy class for this field UVM_READONLY Do not allow the setting of this field from the set_*_local methods
  • 106.
    A radix forprinting and recording can be specified by OR’ing one of the following constants in the FLAG argument FLAG Description UVM_BIN Print/record the field in binary (base-2) UVM_DEC Print/record the field in decimal (base-10) UVM_UNSIGNE D Print/record the field in unsigned decimal (base-10) UVM_OCT Print/record the field in octal (base-8). UVM_HEX Print/record the field in hexadecimal (base-16) UVM_STRING Print/record the field in string format UVM_TIME Print/record the field in time format
  • 107.
    • Using field_automation_macrosare not recommended these days, because it introduce a lot of additional code and reduces simulation performance • It is recommended to use do_macros, user can implement the functions called do_print, do_record, do_compare, do_pack, do_unpack • They are 6 do_macros in sequence_item 1. do_print 2. do_record 3. do_copy 4. do_compare 5. do_pack 6. do_unpack 7. clone 8. create do_methods in sequence_item
  • 108.
  • 109.
    do_methods in sequence_item printvirtual function void do_print(uvm_printer printer); super.do_print(printer); printer.print_string("m_bool", m_bool.name()); printer.print_field_int("m_mode", m_mode, $bits(m_mode), UVM_HEX); printer.print_string("m_name", m_name); endfunction sprint `uvm_info(get_type_name(), $sformatf("Contents: %s", obj.sprint()), UVM_LOW) convert2string virtual function string convert2string(); string contents = ""; $sformat(contents, "%s m_name=%s", contents, m_name); $sformat(contents, "%s m_bool=%s", contents, m_bool.name()); $sformat(contents, "%s m_mode=0x%0h", contents, m_mode); foreach(m_data[i]) Begin $sformat(contents, "%s m_data[%0d]=0x%0h", contents, i, m_data[i]); end return contents;
  • 110.
    do_methods in sequence_item Copy// Implementation of "do_copy". A generic uvm_object called "rhs“ // is received and type casted into Packet called "_pkt". Then // m_addr is copied from _pkt to the variable in current class virtual function void do_copy(uvm_object rhs); Packet _pkt; super.do_copy(rhs); $cast(_pkt, rhs); m_addr = _pkt.m_addr; `uvm_info(get_name(), "In Packet::do_copy()", UVM_LOW) endfunction Compare function void _compare(Object obj1, obj2); if (obj2.compare(obj1)) `uvm_info("TEST", "obj1 and obj2 are same", UVM_LOW) else `uvm_info("TEST", "obj1 and obj2 are different", UVM_LOW) endfunction pack virtual function void do_pack(uvm_packer packer); super.do_pack(packer); packer.pack_field_int(m_addr, $bits(m_addr)); packer.pack_field_int(m_wdata, $bits(m_wdata)); packer.pack_field_int(m_rdata, $bits(m_rdata)); packer.pack_field_int(m_wr, $bits(m_wr)); Endfunction
  • 111.
    do_methods in sequence_item unpack// Now unpack the packed int array into the second object, and display m_val3 = m_pkt2.unpack_ints(m_ints); `uvm_info(get_type_name(), $sformatf("unpacked m_val3=0x%0h", m_val3), UVM_LOW) m_pkt2.print(); clone function void build_phase(uvm_phase phase); // Create obj1, but only declare handle for obj2 Object obj2; Object obj1 = Object::type_id::create("obj1"); obj1.randomize(); `uvm_info("TEST", $sformatf("Obj1.print: %s", obj1.convert2string()), UVM_LOW) // Use $cast to clone obj1 into obj2 $cast(obj2, obj1.clone()); `uvm_info("TEST", "After clone", UVM_LOW) `uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW) endfunction
  • 112.
  • 113.
    Writing sequence usingdo_macros • We know that sequence calls the start_item and finish_item inside the task body • Instead of writing all these statements in code by simply calling uvm_sequence macros • At compile time these macros will be substituted , with calls to start_item() and finish_item() • uvm_do_macros are used to reduce the no. of lines in code by creating item, randomizing it and automatically calling the required tasks to start given sequence or sequence_item
  • 114.
    uvm sequence macros MacroDescription `uvm_do(Item/Seq) This macro takes seq_item or sequence as argument. On calling `uvm_do() the above-defined 6 steps will be executed. `uvm_create(Item/Seq) This macro creates the item or sequence. `uvm_send(Item/Seq) create() and randomize() are skipped, rest all other steps are executed. `uvm_rand_send(Item/Seq) Only create() is skipped, rest all other steps are executed. `uvm_do_with(Item/Seq,Constraints) This macro performs above 6 steps along with constraints defined in second argument. `uvm_rand_send_with(Item/Seq,Constraints) create() is skipped, rest all other steps are executed along with constraints defined in second argument. `uvm_do_pri(Item/Seq,Priority ) Performs `uvm_do() with priority mentioned. 1.create_item() / create req 2.wait_for_grant() 3.randomize the req 4.send the req 5.wait for item done 6.get response.
  • 115.
    uvm sequence macros MacroDescription `uvm_do_pri_with(Item/Seq,Constraints,Priority) Performs `uvm_do() along with constraints defined and priority mentioned. `uvm_send_pri(Item/Seq,Priority) create() and randomize() are skipped, rest all other steps are executed with priority mentioned. `uvm_rand_send_pri(Item/Seq,Priority) Only create() is skipped, rest all other steps are executed with priority mentioned. `uvm_rand_send_pri_with(Item/Seq,Priority, Constraints) create() is skipped, rest all other steps are executed along with constraints defined with priority mentioned. `uvm_declare_p_sequencer(SEQUENCER) This macro is used to declare a variable p_sequencer whose type is specified by SEQUENCER. by using p_sequencer handle, properties of sequencer can be accessed. 1.create_item() / create req 2.wait_for_grant() 3.randomize the req 4.send the req 5.wait for item done 6.get response.
  • 116.
    Writing sequence usingdo_macros //`uvm_do class mem_sequence extends uvm_seque nce#(mem_seq_item); `uvm_object_utils(mem_sequence) //Constructor function new(string name = "mem_seque nce"); super.new(name); endfunction virtual task body(); `uvm_do(req) endtask endclass //`uvm_create and `uvm_send class mem_sequence extends uvm_sequence#(mem _seq_item); `uvm_object_utils(mem_sequence) //Constructor function new(string name = "mem_sequence"); super.new(name); endfunction virtual task body(); `uvm_create(req) assert(req.randomize()); `uvm_send(req); endtask endclass
  • 117.
    Writing sequence usingdo_macros //`uvm_rand_send class mem_sequence extends uvm_sequen ce#(mem_seq_item); `uvm_object_utils(mem_sequence) //Constructor function new(string name = "mem_sequen ce"); super.new(name); endfunction virtual task body(); `uvm_create(req) `uvm_rand_send(req) endtask endclass //`uvm_do_with class mem_sequence extends uvm_sequence#(mem _seq_item); `uvm_object_utils(mem_sequence) //Constructor function new(string name = "mem_sequence"); super.new(name); endfunction virtual task body(); `uvm_do_with(req,{req.wr_en == 1;}) endtask endclass
  • 118.
    Writing sequence usingdo_macros //`uvm_rand_send_with class mem_sequence extends uvm_sequen ce#(mem_seq_item); `uvm_object_utils(mem_sequence) //Constructor function new(string name = "mem_sequen ce"); super.new(name); endfunction virtual task body(); `uvm_create(req) `uvm_rand_send_with(req,{req.rd_en == 1;} ) endtask //calling sequence’s inside sequence class wr_rd_seq extends uvm_sequence#(mem_seq_item); write_sequence wr_seq; read_sequence rd_seq; `uvm_object_utils(wr_rd_seq) //Constructor function new(string name = "wr_rd_seq"); super.new(name); endfunction virtual task body(); `uvm_do(wr_seq) `uvm_do(rd_seq) endtask endclass
  • 119.
  • 120.
    Need for m_sequencer,p_sequencer • In SystemVerilog based OVM/UVM methodologies, the sequence is an object with limited life time unlike a component which has a lifetime through out simulation. • The sequence is created , started and once done, de-referenced from memory and this can be any time in the duration of a test. • So if we want to access anything from the testbench hierarchy (which are components) - the sequence would need a handle to the sequencer on which the sequence is running. • Note that sequencer is a component • m_sequencer is a handle of type uvm_sequencer_base which is available by default in a sequence • To access the real sequencer on which sequence is running , you would need to typecast the m_sequencer to the physical sequencer which is generally called p_sequencer (though you could name it any) • We do not use them only in virtual sequences. These can be used in any sequence, if we need any functionality of sequencer in sequence.
  • 121.
    Example for m_sequencer,p_sequencer • Example where a sequence(object) wants to access a clock monitor which is available with its sequencer (component) • //Typecast the m_sequencer base type to p_sequencer because get access to clock monitor //A test_sequencer class derived from base UVM sequencer //Lets say, it has a clock monitor component to access clock. class test_sequencer_c extends uvm_sequencer; clock_monitor_c clk_monitor; endclass //Here is a test sequence that runs on the test_sequencer //Lets say, sequence need access to sequencer to get access to clock monitor class test_sequence_c extends uvm_sequence; test_sequencer_c p_sequencer; //p_sequencer need to declare, m_sequencer by default it available clock_monitor_c my_clock_monitor; task pre_body() //Typecast the m_sequencer base type to p_sequencer if(!$cast(p_sequencer, m_sequencer)) begin `uvm_fatal("Sequencer Type Mismatch:", " Worng Sequencer"); end //get access to clock monitor my_clock_monitor = p_sequencer.clk_monitor; endtask endclass
  • 122.
    m_sequencer, p_sequencer • m_sequenceris a generic sequencer pointer of type uvm_sequencer_base. It will always exist for a uvm_sequence and is initialized when the sequence is started. • The p_sequencer is a type specific sequencer pointer, created by registering the sequence to a sequencer using the `uvm_declare_p_sequencer macros. • Being type specific, you will be able to access anything added to the sequencer (i.e. pointers to other sequencers, etc.). • p_sequencer will not exist if the `uvm_declare_p_sequencer macros isn’t used. • m_sequencer is a handle of type uvm_sequencer_base while p_sequencer is a handle of type user_defined_sequencer.
  • 123.
    contin,,,, • The user_defined_sequenceris a grandchild of uvm_sequencer_base class. • When we start a sequence, we provide an object handle of our user_defined_sequencer. • Internally, in start method, this child class object is assigned into parent handle called m_sequencer. • So, a static casting occurs such that a parent class handle points to child class object (m_sequencer = user_defined_sequencer_object). • Now, when referring to sequence, if a p_sequencer is defined, the macro `uvm_declare_p_sequencer expands to a function that declares a user_defined_sequencer handle known as p_sequencer. • This function then casts the m_sequencer (parent class handle) back to p_sequencer (child class handle) using dynamic casting ($cast).
  • 124.
    m_sequencer vs p_sequencer m_sequencer •For a sequence, m_sequencer is a handle to the sequencer on which the sequence runs on. • m_sequencer exists by default in UVM sequences • It's set automatically when you call start() p_sequencer • For a sequence, p_sequencer is a specific-type of sequencer which you would like the sequence to run on • P_sequencer doesn’t exist by default in sequences declare using `uvm_declare_psequencer(“sequencer_t ype”) • Implement a function to set a value
  • 125.
  • 126.
    Why Sequence isnot directly connected to Driver in UVM • In SV, the Transgenerator is randomizing all the transactions sending all these transactions at a time to the driver through mailbox. • Here we don’t know that driver is getting all transactions and sending to DUT, the driver doesn’t give any response • In UVM , the sequence is generating and randomizing each transaction, before sending transaction to driver it sends request to driver through sequencer • For each transfer it gets the response from the driver • Driver gives response like get_next_item and item_done responses to sequence through this sequencer
  • 127.
    What is VirtualSequence • Virtual sequence is a container which consists of different Sequences inside in it • Virtual sequence is used to start multiple sequences on different sequencers • When multiple sequences are there, then virtual sequence should be used • Inside the Virtual sequence, different sequences ,sequencers and Virtual sequencer handles are declared
  • 128.
    What is VirtualSequencer • It just contains the handles of different sequencers • When multiple sequencers are there in Environment then Virtual sequencer should be required • Virtual Sequencer is a Sequencer that is not connected to the UVM Driver itself, but contains the handles of the target Sequencer in the Testbench hierarchy.
  • 129.
    When we requireVirtual Sequence/Virtual Sequencer Same stimuli Different stimulus
  • 130.
    When we needVirtual Sequence/Virtual Sequencer Scenario-1 • Let us consider a simple case where we are integrating two such blocks: two sequencers driving these two blocks. • From the top-level test, we will need a way to control these two sequencers. • This can be achieved by using a virtual sequencer and virtual sequences. Scenario-2 • Another scenario let’s consider the DUT is having 2 different interface ports, in this situation, there would be 2 Agents serving each interface port inside the UVM Testbench. • Virtual Sequence will co-ordinate & synchronize the Transactions for the 2 Agents to generate the simulation uses cases using the corresponding Sub-Sequences. • Virtual Sequence decides which Agent’s Sequence will start first and the order of Sub-Sequences execution. • We can say, Virtual Sequence acts like a Controller of the simulation data being generated for the DUT.
  • 131.
    How virtual sequenceworks VIRTUAL SEQUENCE SUB SEQUENCES PRIMITIVE SEQUENCES Do not create any Transaction items Create Transaction items WR_sequence RD_sequence WR_sequence RD_sequence WR_sequence RD_sequence VIRTUAL SEQUENCE APB_SEQ AHB_SEQ AXI_SEQ
  • 132.
    Virtual Sequence implementation •By using 2 approaches we can implement Virtual Sequence • 1st Approach: Virtual sequence will contain handles of Sequencers on which subsequences are to be executed(Stand alone Virtual Sequence, without virtual sequencer) • 2nd Approach: Virtual sequence will run on Virtual Sequencer, Virtual sequencer will contain sequencers handle(with virtual sequencer)
  • 133.
    Virtual Sequence implementationapproach-1 (without virtual sequencer & starting virtual sequence on null )
  • 134.
    Virtual Sequence implementation •Virtual Sequence declaration which includes target Sequencers handles • The way a Virtual Sequence starts the Sub-Seqs on target Sequencers • The way a Virtual Sequence is started from a Test class As shown in the diagram, Virtual Sequence contains two Sequencer handles i.e. SQR_AHB & SQR_AXI. • There are 2 Agents i.e. AHB Agent & AXI Agent which physically contains 2 Sequencers. • These 2 Sequencers are assigned to the Sequencer handles inside Virtual Sequence in a Test. • As per the shown diagram, Virtual Sequence also creates two Sequences which are to be run on the Sequencers of the respective Agents.
  • 135.
    Virtual Sequence implementation /////Base Virtual Sequence Class class base_vseq extends uvm_sequence #(uvm_sequence_item); `uvm_object_utils(base_vseq) /// Target Agent Sequencers uvm_sequencer #(ahb_txn) SQR_AHB; uvm_sequencer #(axi_txn) SQR_AXI; /// Constructor function new (string name = "base_vseq"); super.new(name); endfunction: new endclass: vseq_base ///// Virtual Sequence Class class my_vseq extends base_vseq; `uvm_object_utils(my_vseq) /// Constructor function new (string name = "my_vseq"); super.new(name); endfunction: new /// Sequence Body Task task body(); ahb_seqeunce ahb_seq; axi_sequence axi_seq; ahb_seq = ahb_sequence::type_id::create("ahb_seq"); axi_seq = axi_sequence::type_id::create("axi_seq"); fork abh_seq.start(SQR_AHB); axi_seq.start(SQR_AXI); join • Create a “Base Virtual Sequence” which may contain the handles of all the required target Sequencers. • Later this Base Virtual Sequence can be extended to create the Virtual Sequence with the code to start the Sub-Sequences on the target Sequencers. • we got two classes i.e. & my_vseq class. base_vseq is the base virtual sequence class and my_vseq is the intended Virtual Sequence. • Base virtual sequence contains the handle of the two target Sequencers i.e. SQR_AHB & SQR_AXI. • Virtual Sequence class is extended from the Base Virtual Sequenceclass. • It creates the two Sub-Sequences i.e ahb_seq & axi_seq using the Factory mechanism. • Later inside the body() task the Sub-Sequences are started on the target Agent’s Sequencer by the Virtual Sequence.
  • 136.
    Inside the Testand how the Virtual Sequence is started from the Test? ///// Base Test Class class base_test extends uvm_test; `uvm_component_utils(base_test); /// Environment Class Instantiation top_env Env; /// Constructor function new (string name = "base_test", uvm_component parent = null); super.new(name, parent); endfunction: new /// Build Phase function void build_phase (uvm_phase phase); Env = top_env::type_id::create("Env"); endfunction: build_phase /// Method to Connect Sequencer Handles in VSEQ function void init_vseq (base_vseq vseq); vseq.SQR_AHB = test.env.ahb_agent.SQR_AHB; vseq.SQR_AXI = test.env.axi_agent.SQR_AXI; endfunction: init_vseq endclass: base_test ///// Main Test class test extends base_test; `uvm_component_utils(test) /// Constructor function new (string name = "test", uvm_component parent = null); super.new(name, parent); endfunction: new /// Run Phase task run_phase (uvm_phase phase); /// Create the Virtual Sequence my_vseq vseq = my_vseq::type_id::create("vseq"); phase.raise_objection(this); /// Virtual Sequence Initialization init_vseq(vseq); /// Start the Virtual Sequence vseq.start(null); phase.drop_objection(this); endclass: test • In the test base class i.e. base_test, shown UVM code, a method i.e. init_vseq() is created • which is used to assign the sequencer handles to the handles in classes derived from the virtual sequence base class. • Inside the main test which is derived from the base test, Virtual Sequence is created using Factory. • Later, the initialization method i.e. init_vseq() is being called to connect the Sequencers handle. • Finally Virtual Sequence is started using “Null” since this Virtual Sequence is NOT started on any particular Sequencer.
  • 137.
    Virtual Sequence implementationapproach-2 (with virtual sequencer & starting virtual sequence on virtual sequencer)
  • 138.
    Virtual Sequence implementation •Virtual Sequencer is a Sequencer that is not connected to the UVM Driver itself but contains the handles of the target Sequencer in the Testbench hierarchy. • In the diagram below, there is an example UVM Testbench environment to show the Virtual Sequencer’s application and 2nd approach of Virtual Sequence Implementation: Virtual Sequencer is the part of the Environment i.e. “Env”. Virtual Sequencer contains the handles of the target Sequencers i.e. & which are physically located inside the Agents i.e. AHB Agent & AXI Agent respectively. These target Sequencers handles assignment will be done during the connect phase of the Environment class.. Virtual Sequence is located outside the Environment class & it is created in the run_phase() method of the Test. The Virtual Sequence is designed to run on the Virtual Sequencer & Virtual Sequence also gets the handles of the target Sequencers from the Virtual Sequencer.
  • 139.
    Virtual Sequencer class //Virtual Sequencer Class class virtual_seqr extend uvm_sequencer; `uvm_component_utils(virtual_seqr) // Target Sequencer Handles ahb_seqr SQR_AHB; axi_seqr SQR_AXI; // Constructor function new (string name = "virtual_seqr", uvm_component parent); super.new(name, parent); endfunction: new endclass: virtual_seqr • Virtual Sequencer i.e. “virtual_seqr” class is declared by extended the UVM base class uvm_sequencer. • Target Sequencer handles are also declared inside it.
  • 140.
    Virtual Sequence class //Base Virtual Sequence class base_vseq extends uvm_sequence #(uvm_sequence_item); `uvm_object_utils(base_vseq) // Virtual Sequencer Handle virtual_seqr v_sqr; // Target Sequencers Handle ahb_seqr SQR_AHB; axi_seqr SQR_AXI; // Constructor function new (string name = "base_vseq"); super.new(name); endfunction: new // Body Task (Assign target sequencers handle) task body(); if (!$cast(v_sqr, m_sequencer)) begin `uvm_error(get_full_name(), "Virtual Seqr pointer cast failed") end SQR_AHB = v_sqr.SQR_AHB; SQR_AXI = v_sqr.SQR_AXI; endtask: body endclass: base_vseq // Virtual Sequence class my_vseq extends base_vseq; `uvm_object_utils(my_vseq) // Constructor function new (string name = "my_vseq"); super.new(name); endfunction: new // Body Task(starting the sub-sequences) task body(); // Assigning the Sub-Sequencer Handles super.body; // Sub-Sequence Creation & Execution ahb_sequence ahb_seq; axi_sequence axi_seq; ahb_seq = ahb_sequence::type_id::create("ahb_seq"); axi_seq = axi_sequence::type_id::create("axi_seq"); repeat(30) begin ahb_seq.start(SQR_AHB); axi_seq.start(SQR_AXI); end endtask: body endclass: my_vseq First, a Base Virtual Sequence will be declared & later Virtual Sequence will be derived from the base virtual sequence.
  • 141.
    Environment class // Environment Classclass Environment extends uvm_env; `uvm_component_utils(Environment) // Virtual Sequencer Handle virtual_seqr v_sqr; // Agents Handles ahb_agent AHB_AGNT; axi_agent AXI_AGNT; // Constructor function new (string name = "Environment", uvm_component parent); super.new(name, parent); endfunction: new // Build Phase function void build_phase (uvm_phase phase); v_sqr = virtual_seqr::type_id::create("v_sqr"); AHB_AGNT=ahb_agent::type_id::create("AHB_AGNT"); AXI_AGNT = axi_agent::type_id::create("AXI_AGNT"); endfunction: build_phase // Connect Phase function void connect_phase (uvm_phase phase); v_sqr.SQR_AHB = AHB_AGNT.m_sequencer; v_sqr.SQR_AXI = AXI_AGNT.m_sequencer; endfunction: connect_phase • In the Environment class i.e. “Environment”, Virtual Sequencer is instantiated & built along with two Agents i.e. “AHB_AGNT” & “AXI_AGNT”. • Target Sequencer handles are also assigned in the connect_phase(). • Usage of a flexible & handy feature of UVM i.e. “m_sequencer” is being shown which by default points to the UVM Sequencer derived from the uvm_sequencer.
  • 142.
    Environment class // MainTest class Test extends uvm_test; `uvm_component_utils(Test) // Instantiations my_vseq vseq; Environment Env; // Constructor function new (string name = "Test", uvm_component parent = null); super.new(name, parent); endfunction: new // Build Phase function void build_phase (uvm_phase phase); Env = Environ::type_id::create("Env"); endfunction: build_phase // Run Phase task run_phase (uvm_phase phase); // Create the Virtual Sequence & Environment vseq = my_vseq::type_id::create("vseq"); phase.raise_objection(this); // Start the Virtual Sequence vseq.start(Env.v_sqr); phase.drop_objection(this); endtask: run_phase • Virtual Sequence is started on the Virtual Sequencer from the Test • In the Test class i.e. “Test”, both Environment & Virtual Sequence i.e. “Environment” & “my_vseq” are instantiated and created. • Finally, Virtual Sequence is started on the Virtual Sequencer which exists inside the Environment.
  • 143.
  • 144.
    UVM Sequence ArbitrationMechanism • Multiple sequences can interact concurrently with a driver connected to a single interface. • The sequencer supports an arbitration mechanism to ensure that at any point of time only one sequence has access to the driver. • The choice of which sequence can send a sequence_item is dependent on a user-selectable sequencer arbitration algorithm. • There are six built-in sequencer arbitration mechanisms that are implemented in UVM. • There is also an additional hook to implement a user-defined algorithm. • The sequencer has a method called set_arbitration() that can be called to select which algorithm the sequencer should use for arbitration.
  • 145.
    Contin,,, • The sixalgorithms that can be selected are as follows: 1.SEQ_ARB_FIFO (Default if none specified). • If this arbitration mode is specified, then the sequencer picks sequence items in a FIFO order from all sequences running on the sequencer. • Example: if seq1, seq2, and seq3 are running on a sequencer, it will pick an item from seq1 first, followed by seq2, and then seq3 if available, and continue. 2.SEQ_ARB_WEIGHTED • If this arbitration mode is selected, sequence items from the highest priority sequence are always picked first until none available, then the sequence items from the next priority sequence, and so on. • If two sequences have equal priority, then the items from them are picked in random order.
  • 146.
    Continu,,, 3.SEQ_ARB_RANDOM • If thisarbitration mode is selected, sequence items from different sequences are picked in random order by ignoring all priorities. 4.SEQ_ARB_STRICT_FIFO • This is similar to SEQ_ARB_WEIGHTED except that if two sequences have the same priority, then the items from those sequences are picked in a FIFO order rather than in random order. 5.SEQ_ARB_STRICT_RANDOM • This is similar to SEQ_ARB_RANDOM except that the priorities are NOT ignored. • The items are picked randomly from sequences with the highest priority first followed by next and in that order. 6.SEQ_ARB_USER • This algorithm allows a user to define a custom algorithm for arbitration between sequences. • This is done by extending the uvm_sequencer class and overriding the user_priority_arbitration() method.
  • 147.
    How to prioritizea sequence? • The priority is specified by passing an argument to the start() method of the sequence. • The priority is decided based on relative values specified for different sequences. For Example: • If two sequences are started as follows, the third argument specifies the priority of the sequence. • seq_1.start(m_sequencer, this, 700); //Highest priority • seq_2.start(m_sequencer, this, 500); //Next Highest priority • seq_3.start(m_sequencer, this, 300); //Lowest priority among three sequences
  • 148.
  • 149.
    Sequencer and drivercommuniction • In UVM, there is a mechanism to be followed when we want to send the transactions from the sequencer to the Driver in order to provide stimulus to the DUT. • The transfer of request and response sequence items between sequences and their target driver is facilitated by a TLM communication mechanism implemented in the sequencer. • A particular Sequence is directed to run on a Sequencer which in turns further breaks down into a series of transaction items • These transaction items are needs to be transferred to the Driver where these transaction items are converted into cycle based signal/pin level transitions.
  • 150.
    Sequencer side operation •To send a sequence_item to a driver there are four steps that need to occur: Step 1 – Creation: Creating the “transaction item” with the declared handle using factory mechanism. Step 2 - Ready - start_item():The start_item() call is made, passing the sequence_item handle as an argument. This call blocks until the sequencer grants the sequence and the sequence_item access to the driver. Step 3 – Set:The sequence_item is prepared for use, usually through randomization, but it may also be initialised by setting properties directly. Step 4 - Go - finish_item(): The finish_item() call is made, which blocks until the driver has completed its side of the transfer protocol for the item. No simulation time should be consumed between start_item() and finish_item(). Step 5 - Response - get_response():This step is optional, and is only used if the driver sends a response to indicate to indicate that it has completed transaction associated with the sequence_item. The get_response() call blocks until a response item is available from the sequencers response FIFO.
  • 151.
    Sequencer and drivercommuniction • These are the operational steps from a Sequence which we want to execute using a Sequencer that is connected to a Driver inside an “Agent”. • Whole of this process is shown in the Figure 1 & Figure 2 below: Fig: Driver & Sequencer Interaction for Transaction Exchange Fig: Transaction Execution Flow Between a Sequencer, Driver & Virtual Interface
  • 152.
    Driver side operation •Steps are made by the Driver in order to complete the communication with Sequencer(Sequence) Declaring the “transaction item” with a handle. get_next_item(): Calling the “get_next_item(<transaction_item_handle>)“. Default transaction_handle is “req”. “get_next_item()” blocks the processing until the “req” transaction object is available in the sequencer request FIFO & later “get_next_item” returns with the pointer of the “req” object. try_next_item(): This is a non-blocking variant of the get_next_item() method. It will return a null pointer if there is no REQ sequence_item available in the sequencers request FIFO. However, if there is a REQ sequence_item available it will complete the first half of the driver-sequencer handshake and must be followed by an item_done() call to complete the handshake. Next, Driver completes its side protocol transfer while working with the virtual interface. item_done(): Calling the “item_done()” OR “item_done(rsp)“. It indicates to the sequencer the completion of the process. “item_done” is a non-blocking call & can be processed with an argument or without an argument. If a response is expected by the Sequencer/Sequence then item_done(rsp) is called. It results in Sequencer response FIFO is updated with the “rsp” object handle.
  • 153.
  • 154.
    Introduction • Transaction-Level Modeling(TLM) is used for communication among modules. • TLM is the concept in which transaction based methods are implemented, these methods can be used for communication between the modules • The UVM provides TLM library with transaction-level interfaces, ports, exports, imp ports, and analysis ports. • All these TLM elements are required to send a transaction, receive transaction, and transport from one component to another. where each one plays its unique role. • TLM Interfaces consists of methods for sending and receiving the transaction • All different types of TLM Ports are used like PIPES to connect between the components
  • 155.
    Continu,,, • The UVMTLM library provides, • TLM1 – The TLM1 ports provide blocking and non-blocking pass-by-value transaction-level interfaces. • TLM2 – The TLM2 sockets provide blocking and non-blocking transaction-level interfaces with well-defined completion semantics. • Sequencer Port – A push or pull port, with well-defined completion semantics. • Analysis – The analysis interface is used to perform non-blocking broadcasts of transactions to connected components
  • 156.
    Advantages • Data istransferred at high level of abstraction. Transactions which are developed by extending the uvm_sequence_item can be transferred between components using method calls. • These methods are not hierarchical fixed, so that components can be reused. • The advantages of TLM interfaces are 1) Higher level abstraction 2) Reusable. Plug and play connections. 3) Maintainability 4) Less code. 5) Easy to implement. 6) Faster simulation. 7) Connect to SystemC. 8) Can be used for reference model development.
  • 157.
    Continu,, • connection arebetween port and exports not in component • these are parameterized by xtn class. • multiple language supported(system c) where mailbox is only SV construct. • mailbox is only unidirectional.
  • 158.
    TLM1 • UVM TLMprovides unidirectional and bidirectional, 1. TLM interfaces 2. ports 3. exports 4. imp ports 5. analysis portss 6. FIFOs • Each TLM interface is either blocking, non-blocking, or a combination of these two. 1. Blocking – Blocking TLM methods call will not return until the transaction has been successfully sent or retrieved 2. Non-blocking – Non-Blocking TLM methods call attempts to convey a transaction without consuming simulation time 3. Combination – A combination interface contains both the blocking and nonblocking variants.
  • 159.
    Continu,, • TLM isused for communication among the components. • The most basic TLM operation allows one component to sends a transaction packet to another component. • Let’s consider the two components producer and consumer, where the producer generates and sends the transaction packet to the consumer through TLM ports. • Symbolic representation of TLM Ports is shown below,
  • 160.
    TLM methods &declarations TLM Tlm interfac es ports exports analysis Tlm fifo blocking Non-blockin g Transport put() get() peek() try_put( ) try_get() try_peek() can_put( ) can_get( ) can_peek() transport () nb_transport () new() new() imports new() Write() new() flush() size() used() is_empty( ) is_full() Unidirectional port uvm_*_port#(T) Bidirectional port uvm_*_port#(REQ, RSP) Unidirectional export uvm_*_export#(T) Bidirectional export uvm_*_export#(REQ, RSP) Unidirectional import uvm_*_import#(T) Bidirectional import uvm_*_import#(REQ, RSP) uvm_analysis_export# (T) uvm_analysis_port#(T) uvm_analysis_import# (T) Unidirectional import uvm_tlm_fifo#(T) Bidirectional import uvm_tlm_analysis_fifo#( T)
  • 161.
    • put() isone of the TLM method which can be used to communicate between a Producer& a Consumer. • port is the interface which calls put() method and export is the interface which provides the implementation of put() method. • That way, TLM communication is independent of the Producer and Consumer abstraction level. TLM interface put methods /////////// Producer ////////////////////////// class producer extends uvm_component; `uvm_component_utils(producer) uvm_blocking_put_port #(txn) my_port; function new (string name, uvm_component parent); super.new(name, parent); my_port = new (“my_port”, this); endfunction: new task run_phase(uvm_phase phase); for (int packet = 1; packet<11; packet++) begin txn t; t = txn::type_id::create(“t”, this); `uvm_info(“PID”, $sformatf(“Packet no. %d is sent”, packet), UVM_LOW) my_port.put(t); #10; end endtask: run_phase endclass: producer /////////// Consumer ////////////////////////// class consumer extends uvm_component; `uvm_component_utils(consumer) uvm_blocking_put_imp #(txn, consumer) my_export; function new (string name, uvm_component parent); super.new(name, parent); my_export = new(“my_export”, this); endfunction: new task put (txn t); case(t.kind) t.READ: $display(“Read transaction”); t.WRITE: $display(“Write transaction”); endcase endtask: put endclass: consumer p1.my_port.connect(c1.my_expor t);
  • 162.
    • In termsof purpose, get() method performs the same the put() does – only difference is that get() pulls the transaction from the Producer yet put()push the transaction to theConsumer. TLM interface get methods ///////////// Producer /////////////////// class producer extends uvm_component; `uvm_component_utils(producer) ///// Export Declaration uvm_blocking_get_imp #(my_txn, producer) my_export; //// Constructor function new (string name, uvm_component parent); super.new(name, parent); my_export = new(“my_export”, this); endfunction: new //// get() task implementation task get(output my_txn t); my_txn tmp; tmp = my_txn::type_id::create(“tmp”, this); t = tmp; `uvm_info (“PID”, $sformatf(“Transaction type %s is sent”, t.kind), UVM_LOW) endtask: get endclass: producer /////////// Consumer /////////////////// class consumer extends uvm_component; `uvm_component_utils(consumer) //// Port Declaration uvm_blocking_get_port #(my_txn) my_port; function new (string name, uvm_component parent); super.new(name, parent); my_port = new(“my_port”, this); endfunction: new task run_phase(uvm_phase phase); for (int i=1; i<11; i++) begin my_txn txn; `uvm_info("CID", $sformatf("Transaction no. %0d is asked for", i), UVM_LOW) my_port.get(txn); #10; `uvm_info(“CID”, $sformatf(“Transaction type %s is received”, txn.kind), UVM_LOW) end endtask: run_phase endclass: consumer c1.my_port.connect(p1.my_expor t);
  • 163.
    TLM interface methods •peek • peek method obtain a transaction without consuming it • Calling <port>.peek(trans) retrieve transaction from other component • peek() method call is a blocking call. • The returned transaction is not consumed. A subsequent peek or get will return the same transaction Non-blocking methods • try_put • the try_put method is used to send a transaction to another component without blocking the execution • Calling <port>.try_put(trans) sends a transaction to another component, if possible • try_put method returns 1 If the component is ready to accept the transaction, otherwise it returns 0
  • 164.
    TLM interface methods can_put •can_put() method call returns 1 if the component is ready to accept the transaction, otherwise, it returns 0 • no argument must be passed to can_put, this method is used to see whether the other component is ready to accept the trans try_get • the try_get method is used to retrieve transaction from another component without blocking the execution • Calling <port>.try_get(trans) retrieve transaction from another component, if possible • try_get method returns 1 if the transaction is available, otherwise, it returns 0 can_get • can_get() method call returns 1 if the component can get transaction immediately, otherwise, it returns 0 • no argument must be passed to can_get, this method is used to see whether any transaction is available to get
  • 165.
    TLM interface methods transport •Calling <port>.transport(req, resp) method executes the given request and returns the response in the given output argument • The transport method call is blocking, it may block until the operation is complete nb_transport • Calling <port>.nb_transport(req,resp) the method executes the given request and returns the response in the given output argument, if possible • If for any reason the operation could not be executed immediately, then a 0 must be returned, otherwise 1
  • 166.
    TLM Export • TheTLM Export is a port that forwards a transaction from a child component to its parent • The TLM Export has unidirectional and bidirectional ports • An export can be connected to any compatible child export or imp port. • It must ultimately be connected to at least one implementation of its associated interface • Export Methods • new • This is a constructor method used for the creation of TLM Export. TLM Export clases uvm_*_export#(T) //unidirectional export class uvm_*_export #(REQ,RSP) //bidirectional export class Type parameters, T – The type of transaction to be communicated by the export REQ – The type of request transaction to be communicated by the export RSP – The type of response transaction to be communicated by the export function new (string name,uvm_component parent,int min_size=1,int max_size=1);
  • 167.
    • The TLMImp Port is used to receive the transactions at destination • TLM Imp Ports has unidirectional and bidirectional ports • import Methods -new • This is a constructor method used for the creation of TLM import. • TRANSPORT IMP CONSTRUCTOR • MASTER AND SLAVE IMP CONSTRUCTOR TLM port clases uvm_*_imp #(T,IMP) //unidirectional port class uvm_*_imp #(REQ, RSP, IMP, REQ_IMP, RSP_IMP) //bidirectional port class Type parameters, T – The type of transaction to be communicated by the imp IMP – The type of the component implementing the interface. That is the class to which this imp will delegate. REQ_IMP – The component type that implements the request side of the interface. Defaults to IMP. For master and slave imps only. RSP_IMP – The component type that implements the response side of the interface. Defaults to IMP. For master and slave imps only. function new(string name, IMP imp) function new(string name, IMP imp, REQ_IMP req_imp=imp, RSP_IMP rsp_imp=imp) TLM Import
  • 168.
    • The TLMFIFO provides storage for the transactions between two independently running processes. • We have seen put and get methods operates with only one outstanding transaction at a time i.e it is allowed to send the transaction Only after consumption of the previously sent transaction, in this case, the sender and receiver must be in sync else lead to blocking in one of the components. • What if the case where the sender needs not wait for the receiver acknowledgment • It just wants to store it in memory and the receiver can consume whenever required. • In this sender and the receiver needs not to be in sync. Yes With TLM FIFO it is possible. TLM FIFO
  • 169.
    • In TLMFIFO, the sender pushes the transactions to FIFO and whenever it required reiver pops it out or fetches from the FIFO • Transactions are put into the FIFO via the put_export method • Transactions are fetched from the FIFO via the get_peek_export method • As its FIFO (First In First Out), transactions are fetched from the FIFO in the order they are put TLM FIFO Classes Continu,,, uvm_tlm_analysis_fifo #(T) uvm_tlm_fifo #(T)
  • 170.
    TLM FIFO Methods •size:The size indicates the maximum size of the FIFO,a value of zero indicates no upper bound Calling size() returns the size of the FIFO A return value of 0 indicates the FIFO capacity has no limit • used: Returns the number of entries put into the FIFO • is_empty: Returns 1 when there are no entries in the FIFO, 0 otherwise • is_full: Returns 1 when the number of entries in the FIFO is equal to its size, 0 otherwise • flush: Calling flush method will Remove all entries from the FIFO after the flush method call used method returns 0 and the is_empty method returns 1
  • 171.
    TLM Analysis_fifo • Ananalysis_fifo is a uvm_tlm_fifo#(T) with an unbounded size and a write Method • It can be used any place a uvm_analysis_imp is used • Typical usage is as a buffer between a uvm_analysis_port in an initiator component and TLM1 target component TLM Analysis FIFO Classes • uvm_tlm_analysis_fifo#(T) An analysis_fifo is a uvm_tlm_fifo#(T) with an unbounded size and a write method
  • 172.
    Continu,,, The analysis_export providesthe write method to all connected analysis ports and parent exports analysis_export #(T) Method:function void write (T t) • Write() method: • Calling <port>.write(trans) method will broadcasts a transaction to any number of listeners. • Write method call is a nonblocking call • As along with the interface methods, TLM ports are required for the communication between the components.
  • 173.
  • 174.
    Introduction • UVM RegisterLayer is also referred to as UVM Register Abstraction Layer (UVM RAL) which is defined as the conversion of register sequences into bus sequences • Need to write and read from those registers in order to verify the proper working of the design. • It is mainly used to identify the bugs in the testbench instead of doing traditional verification. • The UVM Register Layer provides a standard base class libraries that enable users to implement the object-oriented model to access the DUT registers and memories. • RAL model can be used with multiple interfaces, memory implementation inside the RAL model and their access methods, information on predictor model to predict the register value based on their operation, etc.. • It has also a list of ready-made UVM register sequences. • By merely configuring these sequences users can access and verify the functionality of all the design registers and memories.
  • 175.
    How do verificationengineers make use of RAL? • Basically all test benches has to mimic the design behavior in order to verify the demanded functionality of the design. • Writing and Reading of registers is taken care by the TLM FIFO feature of the UVM. • But the problem arises when hardware has to modify the register value. • For example an IN register has to reflect the value of the bit on the pin which has to be read from the hardware. • The replicated design in the test bench has to modify some “virtual register” based on the hardware changes, that has to be compared with the actual register bank for it’s functional correctness. • That “Virtual register” bank inside the test bench is abstracted as the Register Abstraction Layer(RAL). • Generally UVM scoreboards make use of RAL to compare the register values inside RTL with the required value inside the test bench.
  • 176.
    For register access,can’t we proceed without RAL? • Yes, we can. • But RAL provides a set of base classes and methods with a set of rules which easies the effort required for register access.
  • 177.
  • 178.
    UVM Register ModelOverview • The register model is composed of a hierarchy of blocks that map to the design hierarchy • Which means the RAL model consists of design register fields, registers, and memory. • Blocks can contain, • registers • register files • memories • other blocks • UVM RAL library provides the base class of each and each class has the default built-in methods in it. • Register classes cant be used as it is, they must be specialized via extensions to provide an abstract view that corresponds to the design registers and memories
  • 179.
    Continu,,, uvm_reg shall consistof one or more uvm_reg_field uvm_reg_file shall consist of one or more uvm_reg uvm_reg_block shall consist of one or more uvm_reg_file or uvm_mem
  • 180.
    Mapping of registermodel components to the environmental components Due to a large number of registers in design, this specialization shall be done by a register model generator
  • 181.
    register model generator •Register model generators are outside the scope of the UVM library. • A register model can be written or it can be created using a register generator application. • Writing or Generating the register model is based on a design register specification. • Writing a register model is easy, but complex designs will be having hundreds or thousands of registers. in that case, writing the register model is tedious. • The easiest way to construct a register model is by using a register generation application or automation tool. • Automation tools will generate the register model by taking register specification as input, this includes reg name, width, register fields, access permissions, etc. • There are paid and free register generators available. • some of them are RGM – Register and Memory Package by Cadence, ralgen by Synopsys, Semifore’s RDL, Duolog’s graphical Bitwise, Agnisys’ IDesignSpec, Mentor Graphics’ Certes Testbench Studio, and Magillem MRV (Magillem Register View).
  • 182.
    Mapping UVM RALelements with design elements • Design block containing four registers, which have two, three, one and one fields respectively, an internal memory, and external memory. The architecture of the CPU shows that it consists of multiple modules • uvm_reg_block: shall be used to represent each design module each module consists of many registers • uvm_reg_file: shall group the module registers each module consists of registers • uvm_reg shall be used to implement the design register each register consists of one or multiple fields • uvm_reg_field type shall be used for field implementation
  • 183.
    RAL Building blocks •There are four blocks, which can be used to build basic RAL. They are • Register block • Register file • Register • Register Filed
  • 184.
    1. Register block •The reg block is written by extending the uvm_reg_block. • A block corresponds to a design component/hierarchy with its own interface(s), registers, register files, memories, and sub-blocks. • In simple words, a block can be referred to as a design module with its own interface(s), registers, register files, memories, and sub-blocks. • And it will be having unique address decoding to route the access to it. • A register model is an instance of a register block, which may contain any number of registers, register files, memories, and other blocks.
  • 185.
    2. Register file •The reg file is written by extending the uvm_reg_file. • The reg file shall be used to group the number of registers or register files. • The block diagram shows the register file consists of reg_file_0, reg_file_1 and reg_file_2. • Each register file consists of a set of registers. ( registers are shown only in reg_file_2 • Assume that there are registers in reg_file_0 and reg_file_1 )
  • 186.
    3. Register • Theuvm 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.
  • 187.
    4. Register field •The register field is declared with the type uvm_reg_filed. • Fields represent a contiguous set of bits. • All data values are modeled as fields. • A field is contained within a single register but may have different access policies. access policies are explained in the next sections.
  • 188.
  • 189.
    Register Access Methods •UVM RAL library classes have built-in 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 • Back door access uses simulator database access routines and this happens in zero simulation time
  • 190.
  • 191.
    Register Access Methods readand 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. Front_door access back_door access
  • 192.
    Register Access Methods Peekand poke • peek() reads the DUT register value using a backdoor • poke() writes a value to DUT register using backdoor 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.
  • 193.
    Register Access Methods 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. mirror • mirror() reads the updated DUT register values. • The mirroring can be performed in the front door or back door( peek() ).
  • 194.
    Register Access Methods 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. reset • reset() sets the register desired and mirrored value to the pre-defined reset value
  • 195.
    Constructing Register Model 1.Register Field • Register fields are declared with uvm_reg_field class type. • 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 (refer to register example) uvm_reg_field reg_name;
  • 196.
    2. Register • Theregister is constructed by writing a class extended from the uvm_reg class. • There must be one class per unique register type • The name of the register type class must be unique within the scope of its declaration • The uvm_reg_field::configure() method shall be called from the register type build method class my_reg extends uvm_reg; rand uvm_reg_field Field_0; rand uvm_reg_field Field_1; endclass class my_reg extends uvm_reg; virtual function build(); this.Field_0 = my_reg::type_id::create(.name(“Field_0”), .parent(null),.contxt(get_full_name())) ; this.Field_0.configure(this, ...); endfunction endclass
  • 197.
    3. Register File •A register file type is constructed by writing a class extended from the uvm_reg_file class • The name of the register file type class must be unique within the scope of its declaration • Register files can contain other register files. • The build() method shall call the configure() method for all register and register file class properties • specifying get_block() for the parent block and this for the parent register file class my_reg extends uvm_reg; virtual function build(); this.Field_0 = my_reg::type_id::create(.name(“Field_0”), .parent(null),.contxt(get_full_name())) ; this.Field_0.configure(this, ...); endfunction endclass class my_reg_file extends uvm_reg_file; `uvm_object_utils(my_reg_file) endclass
  • 198.
    3. Register File classreg_file extends uvm_reg_file; virtual function build(); uvm_reg_block blk = get_block(); this.rf = reg_file_0::type_id::create(.name($psprintf(“%s.rf1”, get_name())).p arent(null),.contxt(blk.get_full_name())); this.rf.configure(get_block(), this, ...); this.rf.build(); this.rf.add_hdl_path(); endfunction endclass Register file has 2 methods 1. map() method 2.set_offset() method
  • 199.
    a. map method() •A virtual map() function, with uvm_reg_map and address offset arguments map() method shall call uvm_reg_map::add_reg() • For all register class properties, adding the value of the address offset argument to the offset of the register in the register file map() method shall call the map() method of all register file class properties • Adding the value of the address offset argument to the offset of the register file base offset • The map() method may call the add_hdl_path() method for all register or register file class properties virtual function map(uvm_reg_map mp, uvm_reg_addr_t offset); mp.add_reg(this.reg_0, base_addr + 'h0); mp.add_reg(this.reg_1, base_addr + 'h4; this.rf.map(mp, base_addr + 'h100); endfunction
  • 200.
    b. set_offset() method •A virtual set_offset() function, with a uvm_reg_map and address offset arguments, may also be implemented • The set_offset() method shall call the set_offset() method for all register and register file class properties virtual function set_offset(uvm_reg_map mp, uvm_reg_addr_t offset); this.reg_0.set_offset(mp, base_addr + 'h0); this.reg_1.set_offset(mp, base_addr + 'h4); this.rf.set_offset(mp, base_addr + 'h100); endfunction
  • 201.
    Memory Types • Amemory type is constructed using a class extended from the uvm_mem class • The name of the memory type class must be unique within the scope of its declaration class my_mem extends uvm_mem; `uvm_object_utils(my_mem) endclass
  • 202.
    Register block • Ablock is constructed by writing a class extended from the uvm_reg_block class • The name of the block type class must be unique within the scope of its declaration • The block type must contain a class property for each, • named address map • register • register file • memory • sub-block • These shall have the rand attribute • The build() method shall instantiate all named address maps by calling the uvm_reg_block::create_map() method class my_blk extends uvm_reg_block; `uvm_object_utils(my_blk) endclass
  • 203.
    UVM RAL Predictor •We know that the UVM Register model maintains the latest design register values in it. but how Register model will get to know the latest values? • This will be done by the UVM RAL component Predictor. • UVM RAL Predictor predicts the register access done through the register model and updates the RAL Model registers. • UVM RAL provides the base class uvm_reg_predictor. uvm_reg_predictor updates the register model based on observed transactions published by a monitor. • Any register access done through the register model is predicted and updated inherently by the base classes (implicit prediction) • Prediction of the register values in the model can also be done explicitly using an external predictor which also takes care of interface bus transactions not occurring through the register model (explicit prediction)
  • 204.
    Implicit prediction • Implicitprediction only requires the integration of the register model with one or more bus sequencers • Updates to the mirror are predicted automatically (i.e., implicitly) by the register model after the completion of each read, write, peek, or poke operation Explicit prediction • Explicit prediction requires the register model to be integrated with both the bus sequencers and corresponding bus monitors • In this mode, the implicit prediction is turned off and all updates to the mirror are predicted externally (i.e., explicitly) by a uvm_reg_predictor component, one for each bus interface
  • 205.
    UVM Register ModelAdapter • With the UVM Register model, we do design register access, i.e WRITE to the design register or READ from the design register by calling RAL methods. • Finally, these transactions have to be placed to design bus, this will be done by RAL component Adapter. • The RAL adapter acts as a converter between the RAL model and Interface. • It converts transactions of RAL methods to Interface/Bus transactions. • The Adapter converts between register model read, write methods and the interface-specific transactions • The transaction adapter is implemented by extending the uvm_reg_adapter class and implementing the reg2bus() and bus2reg() methods
  • 206.
    UVM RAL reg2bus •reg2bus method converts the RAL transactions to Interface (bus) transactions UVM RAL bus2reg • bus2reg method converts the Interface (bus) transactions to RAL transactions class tb_env extends uvm_env; reg_model regmodel; uvm_reg_predictor#(ahb_trans) ahb2reg_predictor; reg2ahb_adapter reg_adapter; ahb_agent ahb; virtual function void build_phase(uvm_phase phase); ahb2reg_predictor = new(“ahb2reg_predictor”, this); endfunction virtual function void connect_phase(uvm_phase phase); if (regmodel.get_parent() == null) begin reg_adapter = reg2ahb_adapter::type_id::create(“reg_adapter”,,get_full_n ame()); ... ahb2reg_predictor.map = regmodel.AHB; ahb2reg_predictor.adapter = reg_adapter; ahb.monitor.ap.connect(ahb2reg_predictor.bus_in); end endfunction
  • 207.
    Integrating RAL toBus Agent • Once after the RAL implementation, RAL has to be connected with the Bus Agent. • This section describes connecting RAL with the sequencer and monitor of the bus.
  • 208.
    Integrating Bus Sequencers •All integration approaches require a register model to be configured with one or more bus sequencers. • The register model becomes a property of a uvm_reg_sequence subtype that executes • Directly on a bus sequencer, if there is only one bus interface providing access to the DUT registers • As a virtual sequence, if there are one or more bus interfaces providing access to the DUT registers; • As a register sequence running
  • 209.
    Integrating the RegisterModel with a Bus Monitor • By default, the register model updates its mirror copy of the register values implicitly. • Every time a register is read or written through the register model, its mirror value is updated. • If other agents on the bus interface perform read and write transactions outside the context of the register model • The register model must learn of these bus operations to update its mirror accordingly. • Integration is accomplished by first instantiating a uvm_reg_predictor component • uvm_reg_predictor component is then connected to the bus monitor’s analysis port
  • 210.
    class tb_env extendsuvm_env; reg_model regmodel; uvm_reg_predictor#(ahb_trans) ahb2reg_predictor; reg2ahb_adapter reg_adapter; ahb_agent ahb; virtual function void build_phase(uvm_phase phase); ahb2reg_predictor = new(“ahb2reg_predictor”, this); endfunction virtual function void connect_phase(uvm_phase phase); if (regmodel.get_parent() == null) begin reg_adapter = reg2ahb_adapter::type_id::create(“reg_adapter”,,get_full_nam e()); ... ahb2reg_predictor.map = regmodel.AHB; ahb2reg_predictor.adapter = reg_adapter; ahb.monitor.ap.connect(ahb2reg_predictor.bus_in); end ... endfunction
  • 211.
    Register defines • UVMregister library has the defines declared in it. • These are being used in the RAL model base classes, user can override these defines. uvm_reg_defines are, 1. `UVM_REG_ADDR_WIDTH 2. `UVM_REG_DATA_WIDTH 3. `UVM_REG_BYTENABLE_WIDTH 4. `UVM_REG_CVR_WIDTH
  • 212.
    Register defines 1. UVM_REG_ADDR_WIDTH •Maximum address width in bits Default value is 64 • Used to define the uvm_reg_addr_t type 2. UVM_REG_DATA_WIDTH • Maximum data width in bits Default value is 64 • Used to define the uvm_reg_data_t type 3. UVM_REG_BYTENABLE_WIDTH • Maximum number of byte enable bits • Default value is one per byte in `UVM_REG_DATA_WIDTH • Used to define the uvm_reg_byte_en_t type 4. UVM_REG_CVR_WIDTH • Maximum number of bits in a uvm_reg_cvr_t coverage model set • Default value is 32
  • 213.