Error-[SV-LCM-PND] Package not defined ../sv/yapp_if.sv, 18 yapp_if, "yapp_pkg::" Package scope resolution failed. Token 'yapp_pkg' is not a package. Originating module 'yapp_if'. Move package definition before the use of the package.
place hw_top.svbefore or afteryapp_router.sv doesn't matter, the compiler (xrun, vcs)
can compile them successfully.
Conclusion
Always place package before DUT is preferred choice during
compiling.
Transaction Level Modeling
(TLM)
Blocking methods are defined with
get() or put()tasks to allow
them to consume time
Non-blocking methods are defined with
try_get() or try_put()functions as they execute in zero time
Uni-Directional TLM
Methods Reference
Method
Description
Syntax
put()
Blocking put
virtual task put(iput TR t);
try_put()
Nonblocking put -return 1 if successful -return 0 if
not
virtual function bit try_put(input TR t);
can_put()
Nonblocking test put -return 1 if put would be
successful -return 0 if not
virtual function bit can_put();
get()
Blocking get
virtual task get(output TR t);
try_get()
Nonblocking get -return 1 if successful -return 0 if
not
virtual function bit try_get(output TR t);
can_get()
Nonblocking test get -return 1 if get would be
successful -return 0 if not
virtual function bit can_get();
peek()
Blocking peek
virtual task peek(output TR t);
try_peek()
Nonblocking peek -return 1 if successful -return 0 if
not
virtual function bit try_peek(output TR t);
can_peek
Nonblocking test peek -return 1 if peek would be
successful -return 0 if not
virtual function bit can_peek();
The peek() methods are similarly to the
get() methods, but copy the transaction
instead of removing it. The transaction is not
consumed, and a subsequent get or peek
operation will return the same transaction
Selected Connector and
Method Options
put
try_put
can_put
get
try_get
can_get
peek
try_peed
can_peek
uvm_put_*
✓
✓
✓
uvm_blocking_put_*
✓
uvm_nonblocking_put_*
✓
✓
uvm_get_*
✓
✓
✓
uvm_blocking_get_*
✓
uvm_nonblocking_get_*
✓
✓
uvm_get_peek_*
✓
✓
✓
✓
✓
✓
uvm_blocking_get_peek_*
✓
✓
uvm_nonblocking_get_peek_*
✓
✓
✓
✓
in the connectors above, * can be replaced by
port, imp, or export
All the methods for a specific connector type MUST
be implemented. If you define an uvm_put connection between
two compoents, then the component with the uvm_put_imp
object must provide implementations of ALL three put
methods, put, try_put and
can_put, even if these methods are not explicitly
called
TLM FIFO
The TLM FIFO is a FIFO component wrapped in get and
putimp connectors. This has the benefit of
data storage as well as providing implementations of the communication
methods. Components connected to the TLM FIFO are in control of data
transfer and can simply defined port connectors to initiate read and
write operations on the FIFO
uvm_tlm_fifo
The TLM FIFO object is effectively a FIFO component instantiated
between and connected to two components. The FIFO contains
imp connectors for the standard TLM put and
get/peek interfaces, therefore the user does
not have to defineimp ports or communication methods and
the FIRO takes care of data storage
The advantages are:
The user does not need to define communication methods or
imp connectors
The FIFO provides data storage between the write
(put) and read
(get/peek) components
There are a number of built-in methods for checking FIFO status
The disadvantages are:
The user must now initiate both sides of the transfer (both
get/peek and put) to complete the
transaction
Two connections must be made (both sides of the FIFO) rather than
one
The put_export and get_peek_export
connection objects have alternatives which provide subsets of the full
connector. For example, blocking_put_export and
nonblocking_put_export can replace put_export.
blocking_get_export, nonblocking_get_export
and get_export (as well as others) can replace
get_peek_export.
built-in methods
Method
Description
Syntax
new
Standard component constructor with an additional third argument,
size, which sets the maximum FIFO size. Default size is 1.
A size of 0 is an unbounded FIFO
function new(string name, uvm_component parent=null, int size=1);
size
Return size of FIFO. 0 indicates unbounded
FIFO
virtual function int size()
used
Return number of entries written to the FIFO
virtual function int used();
is_empty
Return 1 if used() is 0, otherwise
0
virtual function bit is empty();
is_full
Return 1 if used() is equal to size,
otherwise 0
virtual function bit is_full()
flush
Delete all entries from the FIFO, upon which used() is
0 and is_empty() is
1
virtual funciton void flush();
1 2 3
class uvm_tlm_fifo #(type T=int) extends uvm_tlm_fifo_base #(T); ... endclass
virtualclass uvm_tlm_fifo_base #(type T=int)extends uvm_component; uvm_put_imp #(T, this_type) put_export; uvm_get_peek_imp #(T, this_type) get_peek_export; uvm_analysis_port #(T) put_ap; uvm_analysis_port #(T) get_ap; // The following are aliases to the above put_export uvm_put_imp #(T, this_type) blocking_put_export; uvm_put_imp #(T, this_type) nonblocking_put_export; // The following are all aliased to the above get_peek_export, which provides // the superset of these interfaces. uvm_get_peek_imp #(T, this_type) blocking_get_export; uvm_get_peek_imp #(T, this_type) nonblocking_get_export; uvm_get_peek_imp #(T, this_type) get_export; uvm_get_peek_imp #(T, this_type) blocking_peek_export; uvm_get_peek_imp #(T, this_type) nonblocking_peek_export; uvm_get_peek_imp #(T, this_type) peek_export; uvm_get_peek_imp #(T, this_type) blocking_get_peek_export; uvm_get_peek_imp #(T, this_type) nonblocking_get_peek_export; functionnew(string name, uvm_component parent = null); super.new(name, parent); put_export = new("put_export", this); blocking_put_export = put_export; nonblocking_put_export = put_export; get_peek_export = new("get_peek_export", this); blocking_get_peek_export = get_peek_export; nonblocking_get_peek_export = get_peek_export; blocking_get_export = get_peek_export; nonblocking_get_export = get_peek_export; get_export = get_peek_export; blocking_peek_export = get_peek_export; nonblocking_peek_export = get_peek_export; peek_export = get_peek_export; put_ap = new("put_ap", this); get_ap = new("get_ap", this); endfunction
Analysis FIFO
uvm_tlm_analysis_fifo
uvm_tlm_analysis_fifo is a specialization of
uvm_tlm_fifo
Intended to buffer write transactions between the UVC monitor
analysis port and scoreboard
It has the following characteristics:
Unbounded (size=0)
analysis_export connector replaces
put_export
Support the analysis write method
By declaring the FIFO in the scoreboard, we can get
directly from the FIFO output.
However the write side connection of the FIFO to the interface UVC
monitor analysis port must be made (usually) in the testbench which has
visibility of both UVC and scoreboard components. The connection is made
using a connect method call inside the connect phase method
class uvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T);
// Port: analysis_export #(T) // // The analysis_export provides the write method to all connected analysis // ports and parent exports: // //| function void write (T t) // // Access via ports bound to this export is the normal mechanism for writing // to an analysis FIFO. // See write method of <uvm_tlm_if_base #(T1,T2)> for more information.
// Function: new // // This is the standard uvm_component constructor. ~name~ is the local name // of this component. The ~parent~ should be left unspecified when this // component is instantiated in statically elaborated constructs and must be // specified when this component is a child of another UVM component.
functionnew(string name , uvm_component parent = null); super.new(name, parent, 0); // analysis fifo must be unbounded analysis_export = new("analysis_export", this); endfunction
functionvoid write(input T t); void'(this.try_put(t)); // unbounded => must succeed endfunction
endclass
Analysis Port Broadcast
Analysis ports can (uniquely) be connected to any number of
imp connectors, including zero
The analysis port in component yapp_monitor will be
connected to both monitor_one and monitor_two
components. Each of the receiving components has an analysis imp
object and a write communication method declared. The write method
must have the same signature - i.e., they must be void functions called
write with a single input argument of type yapp_packet, but
their implementations can be completely different.
When the send_yapp port is connected to both
mone_in and mtwo_in imps, then a single write call
from yapp_monitor executes both write implementations in
monitor_one and monitor_two
For non-analysis connections
the TLM object must be connected to a single destination only, i.e.,
each non-analysis object has exactly one connect call.
An object may be used multiple times as the argument to a
connect, but exactly once as the caller. This is called
many-to-one connection. For example, many ports can be
connected to a one imp connector
For analysis connections
a TLM object can be connected to any number of destinations i.e.,
one analysis object can call connect many times. This is called
one-to-many connection. For example, one port can be
connected to many imp connectors. one-to-many is only
allowed for analysis connections. An analysis connection can also
NOT call connect. An unconnected TLM object is also only
allowed for analysis connections
Bi-Directional TLM
Transport Connection
Connector syntax
1 2 3
uvm_blocking_transport_XXX #(type REA, type RSP) uvm_nonblocking_transport_XXX #(type REQ, type RSP) uvm_transport_XXX #(type REQ, type RSP)
FIFO/analysis FIFOs do not perform any cloning on input transactions.
Therefore, you will need to check that the UVC monitors collect every
transaction into a different instance to avoid overwriting data in the
FIFOs
class uvm_reg_map extends uvm_object; // Function: add_submap // // Add an address map // // Add the specified address map instance to this address map. // The address map is located at the specified base address. // The number of consecutive physical addresses occupied by the submap // depends on the number of bytes in the physical interface // that corresponds to the submap, // the number of addresses used in the submap and // the number of bytes in the // physical interface corresponding to this address map. // // An address map may be added to multiple address maps // if it is accessible from multiple physical interfaces. // An address map may only be added to an address map // in the grand-parent block of the address submap. // externvirtualfunctionvoid add_submap (uvm_reg_map child_map, uvm_reg_addr_t offset);
// Function: add_mem // // Add a memory // // Add the specified memory instance to this address map. // The memory is located at the specified base address and has the // specified access rights ("RW", "RO" or "WO"). // The number of consecutive physical addresses occupied by the memory // depends on the width and size of the memory and the number of bytes in the // physical interface corresponding to this address map. // // If ~unmapped~ is TRUE, the memory does not occupy any // physical addresses and the base address is ignored. // Unmapped memories require a user-defined ~frontdoor~ to be specified. // // A memory may be added to multiple address maps // if it is accessible from multiple physical interfaces. // A memory may only be added to an address map whose parent block // is the same as the memory's parent block. // externvirtualfunctionvoid add_mem (uvm_mem mem, uvm_reg_addr_t offset, string rights = "RW", bit unmapped=0, uvm_reg_frontdoor frontdoor=null);
The generic register item is implemented as a struct in order to
minimise the amount of memory resource it uses. The struct is defined as
type uvm_reg_bus_op and this contains 6 fields:
Property
Type
Comment/Description
addr
uvm_reg_addr_t
Address field, defaults to 64 bits
data
uvm_reg_data_t
Read or write data, defaults to 64 bits
kind
uvm_access_e
UVM_READ or UVM_WRITE
n_bits
unsigned int
Number of bits being transferred
byte_en
uvm_reg_byte_en_t
Byte enable
status
uvm_status_e
UVM_IS_OK, UVM_IS_X, UVM_NOT_OK
1 2 3 4 5 6 7 8 9
typedefstruct { uvm_access_e kind; // Kind of access: READ or WRITE. uvm_reg_addr_t addr; // The bus address. uvm_reg_data_t data; // The data to write. // The number of bits of <uvm_reg_item::value> being transferred by this transaction. int n_bits; uvm_reg_byte_en_t byte_en; // Enables for the byte lanes on the bus. uvm_status_e status; // The result of the transaction: UVM_IS_OK, UVM_HAS_X, UVM_NOT_OK. } uvm_reg_bus_op;
The register class contains a build method which is used
to create and configure the
fields.
this build method is not called by the UVM
build_phase, since the register is an
uvm_object rather than an uvm_component
1 2 3 4 5 6
// // uvm_reg constructor prototype: // functionnew (string name="", // Register name intunsigned n_bits, // Register width in bits int has_coverage); // Coverage model supported by the register
As shown above, Register width is 32 same with the
bus width, lower 14 bit is configured.
RTL
1 2
`define SPI_CTRL_BIT_NB 14 reg [`SPI_CTRL_BIT_NB-1:0] ctrl; // Control and status register
Register Maps
Two purpose of the register map
provide information on the offset of the registers, memories and/or
register blocks
identify bus agent based sequences to be executed ???
There can be several register maps within a block, each one can
specify a different address map and a different
target bus agent
register map has to be created which the register
block using the create_map method
1 2 3 4 5 6 7 8 9 10 11 12 13
// // Prototype for the create_map method // function uvm_reg_map create_map(string name, // Name of the map handle uvm_reg_addr_t base_addr, // The maps base address intunsigned n_bytes, // Map access width in bytes uvm_endianness_e endian, // The endianess of the map bit byte_addressing=1); // Whether byte_addressing is supported // // Example: // AHB_map = create_map("AHB_map", 'h0, 4, UVM_LITTLE_ENDIAN);
The n_bytes parameter is the word size (bus
width) of the bus to which the map is associated. If a register's width
exceeds the bus width, more than one bus access is needed to read and
write that register over that bus.
he byte_addressing argument affects how the address is
incremented in these consecutive accesses. For example, if
n_bytes=4 and byte_addressing=0, then an access to a
register that is 64-bits wide and at offset 0 will result in two bus
accesses at addresses 0 and 1. With byte_addressing=1, that
same access will result in two bus accesses at addresses 0 and 4.
The default for byte_addressing is
1
The first map to be created within a register
block is assigned to the default_map member of the
register block
Register Adapter
uvm_reg_adapter
Methods
Description
reg2bus
Overload to convert generic register access items to target bus
agent sequence items
bus2reg
Overload to convert target bus sequence items to register model
items
Properties (Of type bit)
Description
supports_byte_enable
Set to 1 if the target bus and the target bus agent supports byte
enables, else set to 0
provides_responses
Set to 1 if the target agent driver sends separate response
sequence_items that require response handling
The provides_responses bit should be set if the
agent driver returns a separate response item (i.e.
put(response), or item_done(response)) from
its request item
Prediction
the update, or prediction, of the register model content can occur
using one of three models
Auto Prediction
This mode of operation is the simplest to implement, but suffers from
the drawback that it can only keep the register model up to date with
the transfers that it initiates. If any other sequences
directly access the target sequencer to update register content, or if
there are register accesses from other DUT interfaces, then the register
model will not be updated.
// Gets the auto-predict mode setting for this map. functionbit uvm_reg_map::get_auto_predict(); return m_auto_predict; endfunction
// Function: set_auto_predict
//
// Sets the auto-predict mode for his map.
//
// When on is TRUE,
// the register model will automatically update its
mirror (what it thinks should be in the DUT)
immediately after any bus read or write operation via this map.
Before a uvm_reg::write
// or uvm_reg::read operation returns, the register's
uvm_reg::predict method is called to update
the mirrored value in the register.
//
// When on is FALSE, bus reads and writes via
this map do not
// automatically update the mirror. For real-time updates to the
mirror
// in this mode, you connect a uvm_reg_predictor
instance to the bus
// monitor. The predictor takes observed bus transactions from
the
// bus monitor, looks up the associated uvm_reg register
given
// the address, then calls that register's
uvm_reg::predict method.
// While more complex, this mode will capture all register
read/write
// activity, including that not directly descendant from calls to
// uvm_reg::write and uvm_reg::read.
//
// By default, auto-prediction is turned off.
//
The register model content is updated based on the register accesses
it initiates
Explicit Prediction
(Recommended Approach)
Explicit prediction is the default mode of
prediction
The register model content is updated via the predictor
component based on all observed bus transactions, ensuring that
register accesses made without the register model are mirrored
correctly. The predictor looks up the accessed register by address then
calls its predict() method
if (m_is_busy && kind == UVM_PREDICT_DIRECT) begin `uvm_warning("RegModel", {"Trying to predict value of register '", get_full_name(),"' while it is being accessed"}) rw.status = UVM_NOT_OK; return; end foreach (m_fields[i]) begin rw.value[0] = (reg_value >> m_fields[i].get_lsb_pos()) & ((1 << m_fields[i].get_n_bits())-1); m_fields[i].do_predict(rw, kind, be>>(m_fields[i].get_lsb_pos()/8)); end
UVM_PREDICT_DIRECT: begin if (m_parent.is_busy()) begin `uvm_warning("RegModel", {"Trying to predict value of field '", get_name(),"' while register '",m_parent.get_full_name(), "' is being accessed"}) rw.status = UVM_NOT_OK; end end endcase
// update the mirror with predicted value m_mirrored = field_val; m_desired = field_val; this.value = field_val;
endfunction: do_predict
uvm_access_e
1 2 3 4 5 6 7 8 9 10 11 12 13
// Enum: uvm_access_e // // Type of operation begin performed // // UVM_READ - Read operation // UVM_WRITE - Write operation // typedefenum { UVM_READ, UVM_WRITE, UVM_BURST_READ, UVM_BURST_WRITE } uvm_access_e;
uvm_predict_e
1 2 3 4 5 6 7 8 9 10 11 12 13
// Enum: uvm_predict_e // // How the mirror is to be updated // // UVM_PREDICT_DIRECT - Predicted value is as-is // UVM_PREDICT_READ - Predict based on the specified value having been read // UVM_PREDICT_WRITE - Predict based on the specified value having been written // typedefenum { UVM_PREDICT_DIRECT, UVM_PREDICT_READ, UVM_PREDICT_WRITE } uvm_predict_e;
uvm_path_e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Enum: uvm_path_e // // Path used for register operation // // UVM_FRONTDOOR - Use the front door // UVM_BACKDOOR - Use the back door // UVM_PREDICT - Operation derived from observations by a bus monitor via // the <uvm_reg_predictor> class. // UVM_DEFAULT_PATH - Operation specified by the context // typedefenum { UVM_FRONTDOOR, UVM_BACKDOOR, UVM_PREDICT, UVM_DEFAULT_PATH } uvm_path_e;
whether a register field can be read or written depends on both the
field's configured access policy and the register's rights in the map
being used to access the field
If a back-door access path is used, the effect of writing the
register through a physical access is mimicked. For example,
read-only bits in the registers will
not be written.
The mirrored value will be updated using the
uvm_reg::predict() method.
If a back-door access path is used, the effect of reading the
register through a physical access is mimicked. For example,
clear-on-read bits in the registers will be set to
zero.
The mirrored value will be updated using the
uvm_reg::predict() method.
Sample the value in the DUT register corresponding to this
abstraction class instance using a back-door access.
The register value is sampled, not modified.
Uses the HDL path for the design abstraction specified by
kind.
The mirrored value will be updated using the
uvm_reg::predict() method.
Read the register and optionally compared the readback
value with the current mirrored value if
check is UVM_CHECK.
The mirrored value will be updated using the
uvm_reg::predict() method based on the readback
value.
The mirroring can be performed using the physical interfaces
(frontdoor) or uvm_reg::peek() (backdoor).
If the register contains write-only fields,
their content is mirrored and optionally checked only if a
UVM_BACKDOOR access path is used to read the
register.
Write this register if the DUT register is out-of-date with the
desired/mirrored value in the abstraction class, as determined by the
uvm_reg::needs_update() method.
The update can be performed using the using the physical interfaces
(frontdoor) or uvm_reg::poke() (backdoor) access.
functionbit uvm_reg::needs_update(); needs_update = 0; foreach (m_fields[i]) begin if (m_fields[i].needs_update()) begin return1; end end endfunction: needs_update
// Concatenate the write-to-update values from each field // Fields are stored in LSB or MSB order upd = 0; foreach (m_fields[i]) upd |= m_fields[i].XupdateX() << m_fields[i].get_lsb_pos();
Update the mirrored and desired value for this
register.
Predict the mirror (and desired) value of the fields in the register
based on the specified observed value on a specified
address map, or based on a calculated value.
if (rw.status ==UVM_IS_OK ) rw.status = UVM_IS_OK;
if (m_is_busy && kind == UVM_PREDICT_DIRECT) begin `uvm_warning("RegModel", {"Trying to predict value of register '", get_full_name(),"' while it is being accessed"}) rw.status = UVM_NOT_OK; return; end
foreach (m_fields[i]) begin rw.value[0] = (reg_value >> m_fields[i].get_lsb_pos()) & ((1 << m_fields[i].get_n_bits())-1); m_fields[i].do_predict(rw, kind, be>>(m_fields[i].get_lsb_pos()/8)); end
UVM_PREDICT_DIRECT: begin if (m_parent.is_busy()) begin `uvm_warning("RegModel", {"Trying to predict value of field '", get_name(),"' while register '",m_parent.get_full_name(), "' is being accessed"}) rw.status = UVM_NOT_OK; end end endcase
// update the mirror with predicted value m_mirrored = field_val; m_desired = field_val; this.value = field_val;
Resetting a register model sets the mirror to the reset value
specified in the model
uvm_reg::reset
1 2 3 4 5 6 7 8 9 10
functionvoid uvm_reg::reset(string kind = "HARD"); foreach (m_fields[i]) m_fields[i].reset(kind); // Put back a key in the semaphore if it is checked out // in case a thread was killed during an operation void'(m_atomic.try_get(1)); m_atomic.put(1); m_process = null; Xset_busyX(0); endfunction: reset
m_mirrored = m_reset[kind]; m_desired = m_mirrored; value = m_mirrored;
if (kind == "HARD") m_written = 0;
endfunction: reset
uvm_reg_field::randomize
uvm_reg_field::pre_randomize()
Update the only publicly known property value with the
current desired value so it can be used as a state
variable should the rand_mode of the field be turned
off.
value is m_desired if
rand_mode is off.
1 2 3
functionvoid uvm_reg_field::pre_randomize(); value = m_desired; endfunction: pre_randomize
// Enum: uvm_predict_e // // How the mirror is to be updated // // UVM_PREDICT_DIRECT - Predicted value is as-is // UVM_PREDICT_READ - Predict based on the specified value having been read // UVM_PREDICT_WRITE - Predict based on the specified value having been written // typedefenum { UVM_PREDICT_DIRECT, UVM_PREDICT_READ, UVM_PREDICT_WRITE } uvm_predict_e;
UVM REG RALF & IP-XACT
UVM Register Abstraction Layer Generator User Guide, S-2021.09-SP1,
December 2021
class ral_reg_CTRL extends uvm_reg; class ral_reg_STAT extends uvm_reg; class ral_reg_HTIM extends uvm_reg; class ral_reg_VTIM extends uvm_reg; class ral_reg_C1CR extends uvm_reg; class ral_mem_CLUT1 extends uvm_mem; class ral_block_vga_lcd extends uvm_reg_block;
ralgen command
1
ralgen -uvm -t dut_regmodel0 vga_lcd_env.ralf
BYTE or HALFWORD access
User is verifying 32 bit registers and the design also allows the
BYTE (8 bits) and HALFWORD (16 bits) accesses.
this is achieved by setting the bit_addressing=0
field in the uvm_reg_block::create_map function.
Using create_map in uvm_reg_block, you can
change the type of addressing scheme you want to use; namely BYTE or
HALFWORD.
this.maps[map] = 1; if (maps.num() == 1) default_map = map;
return map; endfunction
uvm_reg_map::add_reg
The register is located at the specified address offset from
this maps configured base address.
The number of consecutive physical addresses occupied by the register
depends on the width of the register and the number of bytes in the
physical interface corresponding to this address map.
If unmapped is TRUE, the register does not occupy any
physical addresses and the base address is ignored. Unmapped registers
require a user-defined frontdoor to be specified.
functionvoid uvm_reg_map::add_reg(uvm_reg rg, uvm_reg_addr_t offset, string rights = "RW", bit unmapped=0, uvm_reg_frontdoor frontdoor=null);
if (m_regs_info.exists(rg)) begin `uvm_error("RegModel", {"Register '",rg.get_name(), "' has already been added to map '",get_name(),"'"}) return; end
if (rg.get_parent() != get_parent()) begin `uvm_error("RegModel", {"Register '",rg.get_full_name(),"' may not be added to address map '", get_full_name(),"' : they are not in the same block"}) return; end rg.add_map(this);
begin uvm_reg_map_info info = new; info.offset = offset; info.rights = rights; info.unmapped = unmapped; info.frontdoor = frontdoor; m_regs_info[rg] = info; end endfunction
a register has an addressOffset that describes the
location of the register expressed in addressUnitBits as
offset to the starting address of the containing
addressBlock or the containing
registerFile
addressUnitBits
The addressUnitBits element describes the number of bits
of an address increment between two consecutive addressable
units in the addressSpace. If
addressUnitBits is not described, then its value
defaults to 8, indicating a
byte-addressableaddressSpace
block
memory
Global Handles in UVM
Mechanism in UVM-1.1
1 2 3 4 5
functionvoid test_base::start_of_simulation_phase(uvm_phase phase); super.start_of_simulation_phase(phase); uvm_top.print_topology(); // Will not compile in UVM-1.2 factory.print(); // Will not compile in UVM-1.2 endfunction
Global handles uvm_top and factory in uvm_pkg have been removed in
UVM-1.2 and later
Mechanism in UVM-1.1 and
UVM-1.2
Call the get() method of the class to retrieve the
singleton handle.
1 2 3 4 5
functionvoid test_base::start_of_simulation_phase(uvm_phase phase); super.start_of_simulation_phase(phase); uvm_root::get().print_topology(); // Works in UVM-1.1 & UVM-1.2 uvm_factory::get().print(); // Works in UVM-1.1 & UVM-1.2 endfunction
uvm_coreservice_t is the uvm-1.2 mechanism for
accessing all the central UVM services such as
uvm_root,uvm_factory,
uvm_report_server, etc.
1 2 3 4 5 6 7
// Using the uvm_coreservice_t: uvm_coreservice_t cs; uvm_factory f; uvm_root top; cs = uvm_coreservice_t::get(); f = cs.get_factory(); top = cs.get_root();
As you can see , the uvm_config_db::set is after the
factory type_id::create, but receive_value
is still 100, I believe that build_phase
of upper hierarchy finish first before create lower components.
But the recommendation in literature and many web is place
uvm_config_db::set before create
// YAPP Interface to the DUT yapp_if in0(clock, reset); // reset will now be generated by the Clock and Reset UVC // input: clock; output: reset, run_clock, clock_period clock_and_reset_if clk_rst_if(.clock(clock), .reset(reset), .run_clock(run_clock), .clock_period(clock_period)); hbus_if hif(.clock(clock), .reset(reset));
imp and port connectors are sufficient for
modeling most connections, but there is third connector,
export, which is used exclusively in hierarchical
connections.
Normally hierarchical routing is not required. A port on
an UVC monitor can be connected directly to an imp in a
scoreboard by specifying full hierarchical pathname (e.g.,
env.agent.monitor.analysis_port). The structure of an UVC is fixed and
so the user knows to look in the monitor component for the analysis
ports.
However the internal hierarchy of a module UVC is more arbitrary, and
it may be convenient to route all the module UVC connectors to the top
level to allow use without knowledge of the internal structure.
On the initiator side
ports are routed up the hierarchy via other port instances
On the target side
only the component which defines the communication method is allowed
to have an imp instance. So we need a third object to route
connections up the target side hierarchy - export
The hierarchical route must be connected at each level in the
direction of control flow:
1
initiator.connect(target)
Connection rules are as follows:
port initiators can be connected to port,
export or imp targets
export initiators can be connected to
export or imp targets
imp cannot be connection initiator. imp is
target only and is always the last connection object on a route
uvm_analysis_port
1 2 3
class uvm_analysis_port # ( type T = int ) extends uvm_port_base # (uvm_tlm_if_base #(T,T))
uvm_analysis_port
1 2 3
class uvm_analysis_port # ( type T = int ) extends uvm_port_base # (uvm_tlm_if_base #(T,T))
uvm_analysis_imp
1 2 3 4
class uvm_analysis_imp #( type T = int, type IMP = int ) extends uvm_port_base #(uvm_tlm_if_base #(T,T))
QA
What are the three distinct functions of a scoreboard?
Reference model, expected data storage and comparison
To how many consumer components can an analysis port be
connected?
Any number, including zero
What are the names of the two declarations made available by the
following macro:
1
`uvm_analysis_imp_decl(_possible)
Subclass uvm_analysis_imp_possible
Method write_possible
Why should a scoreboard clone the data received by a
write method?
The write method only sends a reference, therefore if
the sending component uses the same reference for every data item,
overwriting of data in the storage function of the scoreboard is
possible without cloning.
Debug topology and
Check config usage in UVM
Topology of the test
1 2 3
functionvoid test_base::start_of_simulation_phase(uvm_phase phase); uvm_root::get().print_topology(); // defaults to table printer endfunction
The default printer policy is
uvm_default_table_printer
There are three default printer policies that the uvm_pkg
provides:
Check all configuration settings in a components configuration table
to determine if the setting has been used, overridden or not used. When
recurse is 1 (default), configuration for this and all child
components are recursively checked. This function is automatically
called in the check phase, but can be manually called at any time.
To get all configuration information prior to the run phase, do
something like this in your top object:
Control-oriented coverage uses SystemVerilog Assertion (SVA) syntax
and the cover directive. It is used to cover sequences of signal values
over time. Assertions cannot be declared or "covered" in a class
declaration, so their use in an UVM verification environment is
restricted to interface only
Covergroup for
data-oriented coverage
CAN be declared as a class member and created in class
constructor
Data-oriented coverage uses the covergroup construct.
Covergroups can be declared and created in classes, therefore they are
essential for explicit coverage in a UVM verification environment.
Interface Monitor Coverage
covergroup new constructor is called
directly off the declaration of the covergroup, it does
not have a separate instance name
1
collected_pkts_cq = new();
The covergroup instance must be created in the class
constructor, not a UVM build_phase or other phase
method
Routing - packets flow from input ports to all legal outputs
latency - all packets received within speicied delay
The module UVC monitor receives data from the interface monitors via
analysis ports, so the implementation of the analysis write function is
a convenient place to put coverage code
run_test search testcase in
UVM
It depends on the simulator:
QuestaSim, Xcelium: You have to import pkg or
`include file in top testbench
VCS: VCS automatically search testcase in other Compilation
Units
UVM_WARNING @ 0: reporter [BDTYP] Cannot create a component of type 'my_test' because it is not registered with the factory. UVM_FATAL @ 0: reporter [INVTST] Requested test from command line +UVM_TESTNAME=my_test not found. UVM_INFO /home/EDA/Cadence/XCELIUM2109/tools/methodology/UVM/CDNS-1.2/sv/src/base/uvm_report_catcher.svh(705) @ 0: reporter [UVM/REPORT/CATCHER]
QuestaSim log:
# UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(277) @ 0: reporter [Questa UVM] QUESTA_UVM-1.2.3# UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(278) @ 0: reporter [Questa UVM] questa_uvm::init(+struct)# UVM_WARNING @ 0: reporter [BDTYP] Cannot create a component of type 'my_test' because it is not registered with the factory.# UVM_FATAL @ 0: reporter [INVTST] Requested test from command line +UVM_TESTNAME=my_test not found.# UVM_INFO verilog_src/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
solution:
uncomment //import sim_pkg::*; in tb_top.sv
Efficient Sequence
Objection in UVM
Objections are handled in pre/post body decalared in
a base sequence class
This is efficient for all sequence execution options:
Default sequences use body objections
Test sequences use test objections
Subsequences use objections of the root sequence which calls
them
A default sequence is a root sequence, so the pre/post
body methods are executed and objection are raised/dropped
there
Test sequence:
A test sequence is a root sequence, so the pre/post
body methods are executed. However, for a test sequence,
starting_phase is null and so the objection is
not handled in the sequence. The test must raise and drop objections
Subsequence:
Not a root sequence, so pre/post body methods are
not executed. The root sequence which ultimately called the subsequence
handles the objections, using one of the two options above.
If a sequence is call via a `uvm_do
variant, the it is defined as a subsequence and its
pre/post_body() methods are not executed.
objection change in UVM1.2
Raising or dropping objections directly from
starting_phase is deprecated