Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
UVM Testbench - Architectural Design
SPI
I/F
For the Design: IRQ
• What does it do?
• What are the use cases?
• Which test cases are required?
• What type of stimulus scenarios are required?
• What represents correct behavior?
• What kind of functional coverage do I need?
UVC Structural Building Block
Sequencer
DUT
seq_item
Driver
Sends stimulus
to Driver
UVC(agent)
Configuration
Object Monitor
UVC(agent)
Configuration
Object Monitor
Sequencer
DUT
Driver Sequencer
Driver
UVCs are Protocol-Specific: The Agent
class dut_agent extends uvm_component;
`uvm_component_utils(dut_agent)
dut_agent_cfg m_cfg;
uvm_analysis_port #(dut_txn) ap;
dut_monitor m_monitor;
dut_driver m_driver;
uvm_sequencer #(dut_txn) m_seqr;
…
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db #(dut_agent_cfg)::get(this,“”,“config”,m_cfg))
`uvm_fatal(“Config fatal”,“Can’t get config”);
if(m_cfg.active == UVM_ACTIVE) begin
m_seqr = uvm_sequencer#(dut_txn)::type_id::create(“seqr”,this);
m_driver = dut_driver::type_id::create(“driver”,this);
end UVC(agent)
…
Configuration
endfunction Object Monitor
UVC(agent)
Configuration
Object Monitor
Sequencer
DUT
Driver
The Environment
class my_env extends uvm_env;
`uvm_component_utils(my_env)
agent1 m_agent1;
agent2 m_agent2;
my_scoreboard m_scoreboard;
my_env_config m_cfg;
agent1 m_agent1;
agent2 m_agent2;
my_scoreboard m_scoreboard;
my_env_config m_cfg;
DUT
The Base Test
class my_test_base extends uvm_test;
`uvm_component_utils(my_test_base)
my_env m_env;
my_env_config m_cfg;
my_agent1_config m_a1_cfg;
my_agent2_config m_a2_cfg;
endclass
DUT
The Actual Test
class my_test extends uvm_test_base;
`uvm_component_utils(my_test)
my_virt_seq m_vseq;
endclass
DUT
A Word About Phasing
• UVM adds 12 new phases in parallel common
build
with run_phase connect
reset
class my_phase_test extends uvm_test_base;
`uvm_component_utils(my_phase_test) post_reset
pre_config
task XXX_phase(uvm_phase phase); config
phase.raise_objection(this, “Starting Phase”);
// Start sequence(s) post_config
run
// begin-end / fork-join pre_main
phase.drop_objection(this, “Finished Phase”); main
endtask
post_main
endclass pre_shutdown
shutdown
• Drivers and monitors should just post_shutdown
check
final
jumping
Architecture Summary
• Agents are protocol-specific
• Environments define the testbench topology
• Which agents and how many
• Other components
• Base Test instantiates env and handles
default configuration
• Extend the base test to define your test
• Tweek configuration and/or factory settings
• Start (virtual) sequence(s)
• Test handles phase objections
• Keep to basic phasing
Advanced UVM
Customization: Understanding
the Factory and Configuration
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
Two Customization Mechanisms
• Factory
• Allows test to change the type of a desired component
or object
• Typically set up at start of simulation
• Configuration
• Allows parents to define properties for children
- Static (build-time) – Highest parent “wins”
- Dynamic (run_time) – Last set “wins”
• All UVM components get their own configuration
- Optionally use to configure their children
Create() vs. New()
• SystemVerilog objects must be constructed
comp1
comp2
No‘;’“;”
No
comp2
Registering with the Factory
• Static methods in wrapper
get_type() returns
the type “handle”
comp2
All Instances
Overridden
Overriding an Instance
Instance Name
Instance Changed
Using Parameterized Types
Parameterized type
Using Parameterized Types
Tests are Components, too!
• run_test() creates the test from the factory
module top;
...
...
run_test();
end
endmodule: top
module top;
...
initial
begin: blk
...
run_test();
end
endmodule: top
initial begin
uvm_config_db #(virtual ahb_if)::set(null, “uvm_test_top”, “AHB”, AHB);
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
How TLM Works
• TLM is all about communication through method calls
• A TLM port specifies
the “API” to be used
• A TLM export supplies
the implementation of
class tr extends
the methods uvm_transaction;
…
• Connections are
Port
between ports/exports,
not components
Export
• Transactions are objects
• Ports & exports are
parameterized by the
transaction type being
communicated
How TLM Works
• TLM is all about communication through method calls
• A TLM port specifies
the “API” to be used
• A TLM export supplies
the implementation of
the methods
• Connections are
Port
between ports/exports,
not components
Export
• Transactions are objects
• Ports & exports are
parameterized by the
transaction type being
communicated
Hierarchical Connections
• Port-to-Export
Hierarchical Connections
• Port-to-Export
• port.connect(export);
Hierarchical Connections
• Port-to-Export
• port.connect(export);
• Port-to-Port
Hierarchical Connections
• Port-to-Export
• port.connect(export);
• Port-to-Port
• child.port.connect(
parent_port);
Hierarchical Connections
• Port-to-Export
• port.connect(export);
• Port-to-Port
• child.port.connect(
parent_port);
• Export-to-Export
Hierarchical Connections
• Port-to-Export
• port.connect(export);
• Port-to-Port
• child.port.connect(
parent_port);
• Export-to-Export
• parent_export.connect(
child.export);
Hierarchical Connections
• Port-to-Export
• port.connect(export);
• Port-to-Port
• child.port.connect(
parent_port);
• Export-to-Export
• parent_export.connect(
child.export);
• Last Export is actually
an ‘imp’
Analysis Communication
• Analysis ports support 1:many connections
• All write() functions
called in zero time
• Used by coverage
collectors and Analysis
Port
scoreboards
• uvm_subscriber has
built-in analysis_export
Analysis of Multiple Streams
• Choice 1: Use imp suffixes defined via macro
• Declare macros outside
of component
• Instantiate suffixed imps
• Implement write_SUFFIX
methods
• Write methods are
functions
• Can’t synchronize
between streams
Analysis of Multiple Streams
• Choice 2: Use embedded fifos
• Declare analysis exports
• Connect exports to fifos
• Run_phase must
actively pull from fifos
TLM Summary
• Every port must eventually connect to an
implementation (imp)
• You’ll mostly only use two port/export
connections
From To
analysis monitor.ap subscriber.analysis_export
sequencer/driver driver.seq_item_port sequencer.seq_item_export
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
Separating Stimulus from the Testbench
• A key to reusability is to separate Behavior
from Structure
• Transactions (a.k.a. Sequence Items) are the
main communication vehicle across the
boundary
Behavior
Structure
DUT
Review: Sequences
• Decouple stimulus specification
from structural hierarchy
• Add/remove/modify stimulus
scenarios independent of testbench u1
• Simplify test writer API u1 s1
• Sequences define
transaction streams
• May start on any sequencer s3 s5
s2
• Sequences can call children
• Sequences & transactions s4
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item;
`uvm_object_utils(bus_item)
Virtual method
do_copy()
do_compare()
convert2string()
do_print()
do_record()
do_pack()
endfunction: do_copy
do_unpack()
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item;
`uvm_object_utils(bus_item)
endfunction: do_copy
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item;
`uvm_object_utils(bus_item)
endfunction: do_copy
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item;
`uvm_object_utils(bus_item)
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item;
`uvm_object_utils(bus_item)
endfunction: do_compare
endendclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item)
endfunction: do_compare
endendclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item)
endendclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
endfunction: convert2string
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
s = super.convert2string();
endfunction: convert2string
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
s = super.convert2string();
$sformat(s,
"%s\n delay \t%0d\n addr \t%0h\n op_code \\
\t%s\n slave_name \t%s\n",
s, delay, addr, op_code.name(), slave_name);
Returns enum value
as a string
endfunction: convert2string
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
s = super.convert2string();
Iterate through
$sformat(s, array values
"%s\n delay \t%0d\n addr \t%0h\n op_code \\
\t%s\n slave_name \t%s\n",
s, delay, addr, op_code.name(), slave_name);
foreach(data[i]) begin
$sformat(s, "%s data[%0d] \t%0h\n", s, i, data[i]);
end
endfunction: convert2string
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
s = super.convert2string();
$sformat(s,
"%s\n delay \t%0d\n addr \t%0h\n op_code \\
\t%s\n slave_name \t%s\n",
s, delay, addr, op_code.name(), slave_name);
foreach(data[i]) begin
$sformat(s, "%s data[%0d] \t%0h\n", s, i, data[i]);
end
$sformat(s, "%s response \t%0b\n", s, response);
return s; USAGE:
endfunction bus_item A;
`uvm_info(“FOO”,A.convert2string(), UVM_NONE)
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
do_print()
function void do_print(uvm_printer printer);
printer.m_string = convert2string();
endfunction: do_print
USAGE:
bus_item A;
A.print();
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
do_print()
do_record()
function void do_record(uvm_recorder
recorder);
super.do_record(recorder);
Record inherited
data members
endfunction: do_record
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
do_print()
do_record()
function void do_record(uvm_recorder
recorder);
super.do_record(recorder);
`uvm_record_field("delay", delay)
`uvm_record_field("addr", addr)
`uvm_record_field("op_code", op_code.name())
`uvm_record_field("slave_name", slave_name)
Simulator-specific implementation
Questa uses $add_attribute
endfunction: do_record
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
do_print()
do_record()
function void do_record(uvm_recorder
recorder);
super.do_record(recorder);
`uvm_record_field("delay", delay)
`uvm_record_field("addr", addr)
`uvm_record_field("op_code", op_code.name())
`uvm_record_field("slave_name", slave_name)
foreach(data[i]) begin Iterate through array values
`uvm_record_field($sformatf("data[%0d]", i), data[i])
end
`uvm_record_field("response", response)
endfunction: do_record
USAGE:
uvm_config_db#(int)::set(this,”*”,”recording_detail”, UVM_FULL);
endclass: bus_item
Designing a Sequence Item: Methods
do_copy()
class bus_item extends uvm_sequence_item; do_compare()
`uvm_object_utils(bus_item) convert2string()
do_print()
do_record()
function void do_pack(uvm_packer packer); do_pack()
super.do_pack(packer); do_unpack()
…
endfunction: do_pack
See the
Online Methodology Cookbook
for details
endclass: bus_item
Sequence Item Composition
bus_item my_bus_item
extends uvm_sequence_item; extends bus_item;
rand int delay; bit status;
rand logic[31:0] addr; logic[31:0] result;
rand op_code_enum op_code; my_bus_item
rand logic[31:0] data[]; extends bus_item;
bus_item_pair
extends uvm_sequence_item;
rand bus_item a;
rand bus_item b;
Modeling Sequence Items
• Encapsulate the information needed to
process an operation
• Whatever that means for your application
• Helper functions
• do_copy() • do_print()
• do_compare() • do_record()
• convert2string() • do_pack()/do_unpack()
• Do not use `uvm_field* macros
• Decreases performance
• Hinders debug
• Use inheritance for similar transactions
• Use composition when needed
Summary: Rules for Sequence Items
• Define sequence items by specifying data
members only
• Do not override pre/mid/post_do
• Create items via their type_id
my_item::type_id::create(“tx”);
• Execute items using start_item()/finish_item()
Advanced UVM
The Proper Care and
Feeding of Sequences
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
Separating Stimulus from the Testbench
• A key to reusability is to separate Behavior
from Structure
Behavior
Structure
DUT
Sequences
• Decouple stimulus specification
from structural hierarchy
• Add/remove/modify stimulus
scenarios independent of testbench u1
• Simplify test writer API u1 s1
• Sequences define
transaction streams
• May start on any sequencer s3 s5
s2
• Sequences can call children
• Sequences & transactions s4
task body();
uvm_config_db#(int)::get(this, “”, “num”, num);
req = req_t::type_id::create(“req”); Create request
for_int i = 0; i < num; i++) begin Initiate Driver Handshake
start_item(req);
if(!req.randomize()) begin Late Randomization
`uvm_error(“body”, “rand failure”) Note: begin-end around `uvm_<msg>
end
finish_item(req);
end Send transaction
endtask
endclass
Review: Sequence/Driver Handshake
my_seq1 driver
start_item(req); get_next_item(req);
finish_item(req);
item_done();
start_item(req); get_next_item(req);
finish_item(req);
item_done();
get_response(rsp);
put_response(rsp);
`uvm_component_utils(test1)
my_env my_env_h;
...
Create sequence
via factory
Review: Starting a Sequence
`uvm_component_utils(test1)
my_env my_env_h;
...
`uvm_component_utils(test1)
my_env my_env_h;
...
Path to sequencer
Can Start Sequence from Environment too
`uvm_component_utils(my_env)
Factory enables test to
my_agent my_agent_h; choose what default
... sequence to run
task body();
iseq = init_seq::type_id::create(“iseq”);
…
endtask
endclass
Hierarchical Sequences
task body();
iseq = init_seq::type_id::create(“iseq”);
…
endtask
endclass
Hierarchical Sequences
task body();
iseq = init_seq::type_id::create(“iseq”);
…
endtask Run on test_seq’s
sequencer
endclass
Hierarchical Sequences
task body();
iseq = init_seq::type_id::create(“iseq”);
task body();
iseq = init_seq::type_id::create(“iseq”);
eseq = exec_seq::type_id::create(“eseq”);
iseq.start( m_sequencer, this );
…
endtask
endclass
Hierarchical Sequences
task body();
iseq = init_seq::type_id::create(“iseq”);
eseq = exec_seq::type_id::create(“eseq”);
iseq.start( m_sequencer, this );
eseq.start( m_sequencer, this );
…
endtask
endclass
“top.env.agent.sequencer.test_seq.eseq”
Summary: General Rules
• Start sequences using seq.start(sequencer)
• Use seq_item_port.get_next_item/item_done
in the driver
• Use try_next_item/item_done if driver must perform
idle cycles
• Use uvm_config_db#()::get() to configure
sequences
• Sequence and Driver must agree on response
path, if any
Advanced UVM
Layered Sequences
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
Sequences & Sequencers
• Most sequences run on sequencers
• One sequencer per agent
• Env may define default sequence
• Can be overridden via factory Test
Monitor Driver
DUT
Sequences & Sequencers
• Tests require coordinating multiple
sequences on multiple DUT interfaces
Test
Env
Sequencer Sequencer
Driver Driver
DUT
Virtual Sequences
typedef uvm_sequence #(uvm_sequence_item)
uvm_virtual_sequence; 2 1
tseq
Useful typedef B A
class myvseq_base extends uvm_virtual_sequence;
... Env
agent2 agent1
a_sequencer_t a_sequencer; Sqr
b_sequencer_t b_sequencer; Handles for target Drvr Drvr
sequencers
task body();
...
aseq.start( a_sequencer , this ); Start sequences on target
bseq.start( b_sequencer , this ); sequencers
endtask
endclass class my_test extends uvm_test;
…
my_seq vseq = my_seq::type_id::create("vseq");
Start vseq with
null sequencer vseq.a_sequencer = env.agent1.sequencer;
vseq.b_sequencer = env.agent2.sequencer;
vseq.start( null );
endclass
Virtual Sequence Initialization
class test_base extends uvm_test;
`uvm_component_utils(test_top_base) 2 1
tseq
B A
env_top m_env;
Env
agent2 agent1
function new(string name = "test_top_base", Sqr
uvm_component parent = null);
Drvr Drvr
super.new(name, parent);
endfunction
endclass: test_base
Extend base test Virtual Sequence in a Test
class init_vseq_test extends test_base;
`uvm_component_utils(init_vseq_test) 2 1
tseq
B A
function new(string name = "init_vseq_test",
uvm_component parent = null); Env
agent2 agent1
super.new(name, parent); Sqr
endfunction
Drvr Drvr
phase.raise_objection(this);
init_vseq(vseq);
vseq.start(null);
phase.drop_objection(this);
endtask: run_phase
endclass: init_vseq_test
Virtual Sequence in a Test
class vseq_A_B extends myvseq_base;
`uvm_object_utils(vseq_A_B) 2 1
tseq
B A
function new(string name = "vseq_A_B");
super.new(name); Env
agent2 agent1
endfunction Sqr
Drvr Drvr
task body();
a_seq a = a_seq::type_id::create("a");
b_seq b = b_seq::type_id::create("b");
fork
a.start(A);
b.start(B);
join
endtask: body
endclass: vseq_A_B
Layered Protocols
• Hierarchical Protocols (PCI Express, USB3.0,
MIPI LLI…)
• Transaction Layer
• Transport Layer
• Physical Layer
• Protocol-Independent
• Generic Layer (e.g. TLM2.0 GP)
• Specific Protocol (e.g. AMBA AHB)
• All require Sequence Items to be
deconstructed and reconstructed
high
• One-to-many
low
low
• Many-to-one high
low
high
Tests Start Sequences
• Want to execute sequences at the top layer
• Test starts sequence on sequencer
• Reuse as much as possible
• Protocol UVC on the bus
• Sequencers/monitors at higher layers
L1
L1_mon D2
D1
D3
Monitor
Sequencer
function void connect_phase(uvm_phase phase); Driver DUT
dut_agent.ap.connect(d2l_mon.analysis_export);
endfunction
The Layered UVC
class D2L_layer extends uvm_subscriber #(dut_txn);
`uvm_component_utils(D2L_layer)
uvm_analysis_port#(L1_item) ap;
D2L_monitor m_mon;
uvm_sequencer#(dut_txn) m_seqr;
myXL_seq xlseq;
dut_agent d_agent;
…
function void build_phase(uvm_phase phase);
m_seqr = uvm_sequencer#(dut_txn)::type_id::create(“dut_seqr”, this);
m_mon = D2L_monitor::type_id::create(“m_mon”, this);
ap = new(“ap”, this);
...
endfunction
UVC(layer) UVC(agent)
L1_seqr Sequencer
DUT
xl Driver
Sqr
The Layered UVC
class D2L_layer extends uvm_subscriber #(dut_txn);
`uvm_component_utils(D2L_layer)
uvm_analysis_port#(L1_item) ap;
D2L_monitor m_mon;
uvm_sequencer#(dut_txn) m_seqr;
myXL_seq xlseq;
dut_agent d_agent;
…
function void connect_phase(uvm_phase phase);
m_mon.ap.connect(ap);
analysis_export.connect(m_mon.analysis_export);
endfunction
D2L_layer layer_agent;
dut_agent d_agent;
…
UVC2
UVC (layer)
(layer) UVC(layer) UVC(agent)
…
dut_agent d_agent;
…
function void build_phase(uvm_phase phase);
m_seqr = uvm_sequencer#(dut_txn)::type_id::create(“dut_seqr”, this);
m_mon = D2L_monitor::type_id::create(“m_mon”, this);
ap = new(“ap”, this);
d_agent = dut_agent::type_id::create(“d_agent”, this);
...
endfunction
endclass
UVC(layer)
UVC(layer) UVC(agent)
UVC(layer) UVC(agent)
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
What is a Test?
• The environment is the “Testbench”
• Defines what components are
needed to verify the DUT
• Specifies defaults
Test
• The test’s job is to
Env Cov
“tweak” the testbench
• Configuration
• Factory overrides Sequencer
Monitor Driver
DUT
Defaults in OVM
Get defaults
function void build_phase(uvm_phase phase);
super.build_phase();
set_config_int(“e.nslaves”,4);
my_slave::type_id::set_inst_override( err_slave::get_type(),
Override slave[0] type “e.slave[0]” );|
test_seq::type_id::set_type_override( test1_seq::get_type());
endfunction Override default sequence type
endclass
ENV:
if(!uvm_config_db #(int)::get(this,“”,“nslaves”,nslaves))
nslaves = 2;
Setup and Invoke Test
module top;
...
dut_if dut_if1 ();
+UVM_TESTNAME=“my_test1”
run_test();
end
endmodule: top
Complex Environment, Simple Test
Virtual Sequence
Test
embodies “the test”
DUT
Simple Test
+UVM_TESTNAME=“seq_test_by_type”
`uvm_component_utils(seq_test_by_type);
my_env e;
virt_seq vseq_h;
endfunction
Select Scoreboard to
endclass match Virtual Sequence
+uvm_set_type_override=virt_seq,foo_virtseq
Extended Test
`uvm_component_utils(cov_test);
endclass
Explicit sequence type
override
Phase Objections
• Components or Sequences can raise or drop
objections
• Phase continues until all raised objections are
dropped
• Objection must be raised at beginning of the
phase
Objections are Hierarchical
• Objections are raised up the hierarchy
2
1
1 1
raise_objection()
1
Agent raise_objection()
Sequencer
raise_objection()
raise_objection()
Monitor
raise_objection()
Objections are Hierarchical
• Objections are raised up the hierarchy
• Objections are
dropped hierarchically too
• When a component’s 12
count = 0, 1 1
drop_objection()
wait for drain_time
1
to elapse Agent drop_objection()
Sequencer
drop_objection()
drop_objection()
Monitor
drop_objection()
Using Objections
Test
Env
Cov Score
Sqr Sqr
Cov Cov
DUT
Advanced UVM
Setting Up the Register Layer
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
UVM Registers are Layered
• UVM Register Layer provides
protocol-independent
register-based layering
Configuration
Predict Object Monitor
RegSeq Sequencer
DUT
Driver
31:14 13 12 11 10 9 8 7 6:0
Registers contain R R/W R/W R/W R/W R/W R/W R R/W
Reserved ASS IE LSB TxNeg RxNeg GoBsy Rsrv Char_Len
bits & fields
class csr_reg extends uvm_reg;
`uvm_object_utils(csr_reg)
uvm_reg_field reserved;
rand uvm_reg_field char_len;
Address Map
points to Registers
R R/W R/W R/W R/W R/W R/W R R/W
Reserved ASS IE LSB TxNeg RxNeg GoBsy Rsrv Char_Len
Registers, Blocks & Maps
R R/W R/W R/W R/W R/W R/W R R/W
class spi_reg_block extends uvm_reg_block; Reserved ASS IE LSB TxNeg RxNeg GoBsy Rsrv Char_Len
`uvm_object_utils(spi_reg_block)
Address Map
rand csr_reg csr;
points to Registers
uvm_reg_map APB_map; // Block map
Register Block
function new(string name = "spi_reg_block");
super.new(name, UVM_NO_COVERAGE); contains Maps
endfunction
One Map per
virtual function void build();
csr = csr_reg::type_id::create("csr");
physical interface
csr.configure(this, null, "");
csr.build();
csr.add_hdl_path_slice("csr", 0, 7);
csr.add_hdl_path_slice(“csr_dff.q”, 0, 7, “GATES”);
endclass: spi_reg_block
Registers, Blocks & Maps
R R/W R/W R/W R/W R/W R/W R R/W
class soc_block extends uvm_reg_block; Reserved ASS IE LSB TxNeg RxNeg GoBsy Rsrv Char_Len
`uvm_object_utils(soc_block)
Address Map
spi_reg_blk spi_regs;
wsh_reg_blk wsh_regs; contains Registers
function new(string name = "soc_block"); Register Block
super.new(name, UVM_NO_COVERAGE);
endfunction contains Maps
virtual function void build(); One Map per
default_map = create_map("", 0, 1,
UVM_LITTLE_ENDIAN);
physical interface
access registers
• Handle for target sequencer
SQR
• Handle for register layer adapter
• A block can have > 1 map
UVC(agent)
Sequencer
DUT
Driver
How Do Register Accesses Work?
• When an explicit register access method is
called
• The register layer uses a generic register command:
- Address, Data, Read or Write
• This is then sent through a layering
to the target bus agent
• The layering has to convert: B
Monitor
place in the adapter
RegSeq
• Extended from Sequencer
Driver B DUT
uvm_reg_adapter Reg
How Do Register Accesses Work?
• The predictor updates the value of the
register model
• Bus transaction (from monitor) converted back to Reg
transaction
• Write: Value that was written to DUT is reflected
• Read: Value that was read from DUT is reflected
• The predictor then writes the Reg
RegSeq
Sequencer
DUT
Driver
Register Adapter Class Example
class reg2ahb_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg2ahb_adapter) reg2bus() converts register
function new(string name = "reg2ahb_adapter");
operation to bus item
super.new(name); Note single access only
endfunction
Another DUT
Another DUT
APB ANI
Another DUT
APB ANI
APB ANI
UVM Register Package Generation
Register Definitions
Customer Example
Early in project:
335 Registers 11,500 lines
Final project:
1,000 Registers 35,000+ lines
of Register Package code
16
Register Assistant* Overview
• Central, Scalable & Extensible
Register/Memory Datamodel
• Enables easy specification of registers
• Manages register changes
• Eliminates hand coding & resultant mistakes
• Completely customizable
• Automatically Generates
Register Outputs
• UVM and OVM
• Synthesizable RTL
• Documentation
• Extensive roadmap Supports the entire design team
* Included with Certe Testbench Studio
Register Documentation Generation
• Communicate the register layer to all team
members
• Final documents auto-generated
• Customizable content & style
Summary
Template
Generated
SQR
UVC(agent)
Predict Generated by
Monitor Register
Assistant
RegSeq
Sequencer DUT
Driver
Advanced UVM
Register-Based Testing
Tom Fitzpatrick
Verification Evangelist
academy@mentor.com
www.verificationacademy.com
Register-Based Analysis Components
• The register model mirror is used by analysis
components
• Scoreboards to check current DUT configuration
- Where this may affect the checking algorithm
• Functional coverage monitors
- What is the configuration at a triggered sample?
• Analysis components use the register model
passively:
• get()
• Backdoor read() or peek() accesses
• Alternatively they look up the register model
values using
• uvm_reg_field.value
• uvm_reg.value – aggregate of field values
Register Scoreboard Guidelines
• Scoreboard needs a handle to the register
model
• Scoreboard accesses register values
• via handle to register (spi_rm.ctrl.ie.value)
• or calling register.get() (spi_rm.ctrl.ie.get())
• Scoreboard checks DUT register contents
• Compare observed data vs.
register contents
• Compare DUT contents vs.
expected
- via peek access to DUT
• Use predict() to set mirrored
value
- Use mirror() to read & check
- Via scoreboard or sequence
Scoreboard Checking
• Actual vs. register contents
rdata = spi_rm.rxtx_reg.get();
if(rdata != mosi_data)
error = 1;
covergroup combination_cov;
option.per_instance = 1;
ASS: coverpoint spi_rm.ctrl_reg.ass.value[0];
IE: coverpoint spi_rm.ctrl_reg.ie.value[0];
LSB: coverpoint spi_rm.ctrl_reg.lsb.value[0];
TX_NEG: coverpoint spi_rm.ctrl_reg.tx_neg.value[0];
RX_NEG: coverpoint spi_rm.ctrl_reg.rx_neg.value[0];
// Suspect character lengths - there may be more ....
CHAR_LEN: coverpoint spi_rm.ctrl_reg.char_len.value[6:0] {
bins LENGTH[] = {0, 1, [31:33], [63:65], [95:97], 126, 127};
}
CLK_DIV: coverpoint spi_rm.divider_reg.ratio.value[15:0] {
bins RATIO[] = {16'h0, 16'h1, 16'h2, 16'h4, 16'h8,
16'h10, 16'h20, 16'h40, 16'h80};
}
COMB_CROSS: cross ASS, IE, LSB, TX_NEG, RX_NEG, CHAR_LEN, CLK_DIV;
endgroup: combination_cov Covergroup checks that all interesting SPI
Master configurations have been checked.
Functional Coverage Monitor Example
class spi_reg_functional_coverage extends
uvm_subscriber #(apb_seq_item);
`uvm_component_utils(spi_reg_functional_coverage)
spi_reg_block spi_rm;
endclass: covergroup_wrapper
Monitor with Wrapped Covergroup
class spi_reg_functional_coverage extends
uvm_subscriber #(apb_seq_item);
`uvm_component_utils(spi_reg_functional_coverage)
spi_reg_block spi_rm;
combination_cov_wrapper cg;
Aggregated Sequences:
Sequence Name Description
uvm_reg_mem_shared_access_seq Runs reg_access_seq and
mem_access_seq on all the registers and
memories available in that block
uvm_reg_mem_built_in_seq Runs all of the built in sequences on a block
Register Summary
• Register Block contains
• Register model • Address Map
- Fields • Sub-blocks
• Register analysis components have register
block pointer
• Access via get() or backdoor read/peek
• Use model.reg.value directly
• Wrap covergroups to increase flexibility
• Use built-in test sequences for sanity
checking
• Registers and Memories