//
// -------------------------------------------------------------
// Copyright 2004-2009 Synopsys, Inc.
// Copyright 2010-2011 Mentor Graphics Corporation
// Copyright 2010-2011 Cadence Design Systems, Inc.
// All Rights Reserved Worldwide
//
// Licensed under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in
// writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See
// the License for the specific language governing
// permissions and limitations under the License.
// -------------------------------------------------------------
//
typedef class [docs]uvm_reg_cbs;
typedef class [docs]uvm_reg_frontdoor;
//-----------------------------------------------------------------
// CLASS: uvm_reg
// Register abstraction base class
//
// A register represents a set of fields that are accessible
// as a single entity.
//
// A register may be mapped to one or more address maps,
// each with different access rights and policy.
//-----------------------------------------------------------------
virtual class [docs]uvm_reg extends uvm_object;
local bit m_locked;
local uvm_reg_block m_parent;
local uvm_reg_file m_regfile_parent;
local int unsigned m_n_bits;
local int unsigned m_n_used_bits;
protected bit m_maps[uvm_reg_map];
protected uvm_reg_field m_fields[$]; // Fields in LSB to MSB order
local int m_has_cover;
local int m_cover_on;
local semaphore m_atomic;
local process m_process;
local string m_fname;
local int m_lineno;
local bit m_read_in_progress;
local bit m_write_in_progress;
protected bit m_update_in_progress;
/*local*/ bit m_is_busy;
/*local*/ bit m_is_locked_by_field;
local uvm_reg_backdoor m_backdoor;
local static int unsigned m_max_size;
local uvm_object_string_pool
#(uvm_queue #(uvm_hdl_path_concat)) m_hdl_paths_pool;
//----------------------
// Group: Initialization
//----------------------
// Function: new
//
// Create a new instance and type-specific configuration
//
// Creates an instance of a register abstraction class with the specified
// name.
//
// ~n_bits~ specifies the total number of bits in the register.
// Not all bits need to be implemented.
// This value is usually a multiple of 8.
//
// ~has_coverage~ specifies which functional coverage models are present in
// the extension of the register abstraction class.
// Multiple functional coverage models may be specified by adding their
// symbolic names, as defined by the <uvm_coverage_model_e> type.
//
extern function [docs]new (string name="",
int unsigned n_bits,
int has_coverage);
// Function: configure
//
// Instance-specific configuration
//
// Specify the parent block of this register.
// May also set a parent register file for this register,
//
// If the register is implemented in a single HDL variable,
// its name is specified as the ~hdl_path~.
// Otherwise, if the register is implemented as a concatenation
// of variables (usually one per field), then the HDL path
// must be specified using the <add_hdl_path()> or
// <add_hdl_path_slice> method.
//
extern function void [docs]configure (uvm_reg_block blk_parent,
uvm_reg_file regfile_parent = null,
string hdl_path = "");
// Function: set_offset
//
// Modify the offset of the register
//
// The offset of a register within an address map is set using the
// <uvm_reg_map::add_reg()> method.
// This method is used to modify that offset dynamically.
//
// Modifying the offset of a register will make the register model
// diverge from the specification that was used to create it.
//
extern virtual function void [docs]set_offset (uvm_reg_map map,
uvm_reg_addr_t offset,
bit unmapped = 0);
/*local*/ extern virtual function void [docs]set_parent (uvm_reg_block blk_parent,
uvm_reg_file regfile_parent);
/*local*/ extern virtual function void [docs]add_field (uvm_reg_field field);
/*local*/ extern virtual function void [docs]add_map (uvm_reg_map map);
/*local*/ extern function void [docs]Xlock_modelX;
//---------------------
// Group: Introspection
//---------------------
// Function: get_name
//
// Get the simple name
//
// Return the simple object name of this register.
//
// Function: get_full_name
//
// Get the hierarchical name
//
// Return the hierarchal name of this register.
// The base of the hierarchical name is the root block.
//
extern virtual function string [docs]get_full_name();
// Function: get_parent
//
// Get the parent block
//
extern virtual function uvm_reg_block [docs]get_parent ();
extern virtual function uvm_reg_block [docs]get_block ();
// Function: get_regfile
//
// Get the parent register file
//
// Returns ~null~ if this register is instantiated in a block.
//
extern virtual function uvm_reg_file [docs]get_regfile ();
// Function: get_n_maps
//
// Returns the number of address maps this register is mapped in
//
extern virtual function int [docs]get_n_maps ();
// Function: is_in_map
//
// Returns 1 if this register is in the specified address ~map~
//
extern function bit [docs]is_in_map (uvm_reg_map map);
// Function: get_maps
//
// Returns all of the address ~maps~ where this register is mapped
//
extern virtual function void [docs]get_maps (ref uvm_reg_map maps[$]);
/*local*/ extern virtual function uvm_reg_map [docs]get_local_map (uvm_reg_map map,
string caller = "");
/*local*/ extern virtual function uvm_reg_map [docs]get_default_map (string caller = "");
// Function: get_rights
//
// Returns the accessibility ("RW, "RO", or "WO") of this register in the given ~map~.
//
// If no address map is specified and the register is mapped in only one
// address map, that address map is used. If the register is mapped
// in more than one address map, the default address map of the
// parent block is used.
//
// Whether a register field can be read or written depends on both the field's
// configured access policy (refer to <uvm_reg_field::configure>) and the register's
// accessibility rights in the map being used to access the field.
//
// If an address map is specified and
// the register is not mapped in the specified
// address map, an error message is issued
// and "RW" is returned.
//
extern virtual function string [docs]get_rights (uvm_reg_map map = null);
// Function: get_n_bits
//
// Returns the width, in bits, of this register.
//
extern virtual function int unsigned [docs]get_n_bits ();
// Function: get_n_bytes
//
// Returns the width, in bytes, of this register. Rounds up to
// next whole byte if register is not a multiple of 8.
//
extern virtual function int unsigned [docs]get_n_bytes();
// Function: get_max_size
//
// Returns the maximum width, in bits, of all registers.
//
extern static function int unsigned [docs]get_max_size();
// Function: get_fields
//
// Return the fields in this register
//
// Fills the specified array with the abstraction class
// for all of the fields contained in this register.
// Fields are ordered from least-significant position to most-significant
// position within the register.
//
extern virtual function void [docs]get_fields (ref uvm_reg_field fields[$]);
// Function: get_field_by_name
//
// Return the named field in this register
//
// Finds a field with the specified name in this register
// and returns its abstraction class.
// If no fields are found, returns ~null~.
//
extern virtual function uvm_reg_field [docs]get_field_by_name(string name);
/*local*/ extern function string [docs]Xget_fields_accessX(uvm_reg_map map);
// Function: get_offset
//
// Returns the offset of this register
//
// Returns the offset of this register in an address ~map~.
//
// If no address map is specified and the register is mapped in only one
// address map, that address map is used. If the register is mapped
// in more than one address map, the default address map of the
// parent block is used.
//
// If an address map is specified and
// the register is not mapped in the specified
// address map, an error message is issued.
//
extern virtual function uvm_reg_addr_t [docs]get_offset (uvm_reg_map map = null);
// Function: get_address
//
// Returns the base external physical address of this register
//
// Returns the base external physical address of this register
// if accessed through the specified address ~map~.
//
// If no address map is specified and the register is mapped in only one
// address map, that address map is used. If the register is mapped
// in more than one address map, the default address map of the
// parent block is used.
//
// If an address map is specified and
// the register is not mapped in the specified
// address map, an error message is issued.
//
extern virtual function uvm_reg_addr_t [docs]get_address (uvm_reg_map map = null);
// Function: get_addresses
//
// Identifies the external physical address(es) of this register
//
// Computes all of the external physical addresses that must be accessed
// to completely read or write this register. The addressed are specified in
// little endian order.
// Returns the number of bytes transferred on each access.
//
// If no address map is specified and the register is mapped in only one
// address map, that address map is used. If the register is mapped
// in more than one address map, the default address map of the
// parent block is used.
//
// If an address map is specified and
// the register is not mapped in the specified
// address map, an error message is issued.
//
extern virtual function int [docs]get_addresses (uvm_reg_map map = null,
ref uvm_reg_addr_t addr[]);
//--------------
// Group: Access
//--------------
// Function: set
//
// Set the desired value for this register
//
// Sets the desired value of the fields in the register
// to the specified value. Does not actually
// set the value of the register in the design,
// only the desired value in its corresponding
// abstraction class in the RegModel model.
// Use the <uvm_reg::update()> method to update the
// actual register with the mirrored value or
// the <uvm_reg::write()> method to set
// the actual register and its mirrored value.
//
// Unless this method is used, the desired value is equal to
// the mirrored value.
//
// Refer <uvm_reg_field::set()> for more details on the effect
// of setting mirror values on fields with different
// access policies.
//
// To modify the mirrored field values to a specific value,
// and thus use the mirrored as a scoreboard for the register values
// in the DUT, use the <uvm_reg::predict()> method.
//
extern virtual function void [docs]set (uvm_reg_data_t value,
string fname = "",
int lineno = 0);
// Function: get
//
// Return the desired value of the fields in the register.
//
// Does not actually read the value
// of the register in the design, only the desired value
// in the abstraction class. Unless set to a different value
// using the <uvm_reg::set()>, the desired value
// and the mirrored value are identical.
//
// Use the <uvm_reg::read()> or <uvm_reg::peek()>
// method to get the actual register value.
//
// If the register contains write-only fields, the desired/mirrored
// value for those fields are the value last written and assumed
// to reside in the bits implementing these fields.
// Although a physical read operation would something different
// for these fields,
// the returned value is the actual content.
//
extern virtual function uvm_reg_data_t [docs]get(string fname = "",
int lineno = 0);
// Function: get_mirrored_value
//
// Return the mirrored value of the fields in the register.
//
// Does not actually read the value
// of the register in the design
//
// If the register contains write-only fields, the desired/mirrored
// value for those fields are the value last written and assumed
// to reside in the bits implementing these fields.
// Although a physical read operation would something different
// for these fields, the returned value is the actual content.
//
extern virtual function uvm_reg_data_t [docs]get_mirrored_value(string fname = "",
int lineno = 0);
// Function: needs_update
//
// Returns 1 if any of the fields need updating
//
// See <uvm_reg_field::needs_update()> for details.
// Use the <uvm_reg::update()> to actually update the DUT register.
//
extern virtual function bit [docs]needs_update();
// Function: reset
//
// Reset the desired/mirrored value for this register.
//
// Sets the desired and mirror value of the fields in this register
// to the reset value for the specified reset ~kind~.
// See <uvm_reg_field.reset()> for more details.
//
// Also resets the semaphore that prevents concurrent access
// to the register.
// This semaphore must be explicitly reset if a thread accessing
// this register array was killed in before the access
// was completed
//
extern virtual function void [docs]reset(string kind = "HARD");
// Function: get_reset
//
// Get the specified reset value for this register
//
// Return the reset value for this register
// for the specified reset ~kind~.
//
extern virtual function uvm_reg_data_t
[docs]get_reset(string kind = "HARD");
// Function: has_reset
//
// Check if any field in the register has a reset value specified
// for the specified reset ~kind~.
// If ~delete~ is TRUE, removes the reset value, if any.
//
extern virtual function bit [docs]has_reset(string kind = "HARD",
bit delete = 0);
// Function: set_reset
//
// Specify or modify the reset value for this register
//
// Specify or modify the reset value for all the fields in the register
// corresponding to the cause specified by ~kind~.
//
extern virtual function void
[docs]set_reset(uvm_reg_data_t value,
string kind = "HARD");
// Task: write
//
// Write the specified value in this register
//
// Write ~value~ in the DUT register that corresponds to this
// abstraction class instance using the specified access
// ~path~.
// If the register is mapped in more than one address map,
// an address ~map~ must be
// specified if a physical access is used (front-door access).
// 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.
//
extern virtual task [docs]write(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// Task: read
//
// Read the current value from this register
//
// Read and return ~value~ from the DUT register that corresponds to this
// abstraction class instance using the specified access
// ~path~.
// If the register is mapped in more than one address map,
// an address ~map~ must be
// specified if a physical access is used (front-door access).
// 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.
//
extern virtual task [docs]read(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// Task: poke
//
// Deposit the specified value in this register
//
// Deposit the value in the DUT register corresponding to this
// abstraction class instance, as-is, using a back-door access.
//
// Uses the HDL path for the design abstraction specified by ~kind~.
//
// The mirrored value will be updated using the <uvm_reg::predict()>
// method.
//
extern virtual task [docs]poke(output uvm_status_e status,
input uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// Task: peek
//
// Read the current value from this register
//
// 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.
//
extern virtual task [docs]peek(output uvm_status_e status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// Task: update
//
// Updates the content of the register in the design to match the
// desired value
//
// This method performs the reverse
// operation of <uvm_reg::mirror()>.
// 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.
// If the register is mapped in multiple address maps and physical access
// is used (front-door), an address ~map~ must be specified.
//
extern virtual task [docs]update(output uvm_status_e status,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// Task: mirror
//
// Read the register and update/check its mirror value
//
// 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 ~check~ is specified as UVM_CHECK,
// an error message is issued if the current mirrored value
// does not match the readback value. Any field whose check has been
// disabled with <uvm_reg_field::set_compare()> will not be considered
// in the comparison.
//
// If the register is mapped in multiple address maps and physical
// access is used (front-door access), an address ~map~ must be specified.
// 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.
//
extern virtual task [docs]mirror(output uvm_status_e status,
input uvm_check_e check = UVM_NO_CHECK,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// Function: predict
//
// 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.
// See <uvm_reg_field::predict()> for more details.
//
// Returns TRUE if the prediction was successful for each field in the
// register.
//
extern virtual function bit [docs]predict (uvm_reg_data_t value,
uvm_reg_byte_en_t be = -1,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_path_e path = UVM_FRONTDOOR,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
// Function: is_busy
//
// Returns 1 if register is currently being read or written.
//
extern function bit [docs]is_busy();
/*local*/ extern function void [docs]Xset_busyX(bit busy);
/*local*/ extern task [docs]XreadX (output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_path_e path,
input uvm_reg_map map,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
/*local*/ extern task [docs]XatomicX(bit on);
/*local*/ extern virtual function bit [docs]Xcheck_accessX
(input uvm_reg_item rw,
output uvm_reg_map_info map_info,
input string caller);
/*local*/ extern function bit [docs]Xis_locked_by_fieldX();
extern virtual function bit [docs]do_check(uvm_reg_data_t expected,
uvm_reg_data_t actual,
uvm_reg_map map);
extern virtual task [docs]do_write(uvm_reg_item rw);
extern virtual task [docs]do_read(uvm_reg_item rw);
extern virtual function void [docs]do_predict
(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
//-----------------
// Group: Frontdoor
//-----------------
// Function: set_frontdoor
//
// Set a user-defined frontdoor for this register
//
// By default, registers are mapped linearly into the address space
// of the address maps that instantiate them.
// If registers are accessed using a different mechanism,
// a user-defined access
// mechanism must be defined and associated with
// the corresponding register abstraction class
//
// If the register is mapped in multiple address maps, an address ~map~
// must be specified.
//
extern function void [docs]set_frontdoor(uvm_reg_frontdoor ftdr,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
// Function: get_frontdoor
//
// Returns the user-defined frontdoor for this register
//
// If ~null~, no user-defined frontdoor has been defined.
// A user-defined frontdoor is defined
// by using the <uvm_reg::set_frontdoor()> method.
//
// If the register is mapped in multiple address maps, an address ~map~
// must be specified.
//
extern function uvm_reg_frontdoor [docs]get_frontdoor(uvm_reg_map map = null);
//----------------
// Group: Backdoor
//----------------
// Function: set_backdoor
//
// Set a user-defined backdoor for this register
//
// By default, registers are accessed via the built-in string-based
// DPI routines if an HDL path has been specified using the
// <uvm_reg::configure()> or <uvm_reg::add_hdl_path()> method.
//
// If this default mechanism is not suitable (e.g. because
// the register is not implemented in pure SystemVerilog)
// a user-defined access
// mechanism must be defined and associated with
// the corresponding register abstraction class
//
// A user-defined backdoor is required if active update of the
// mirror of this register abstraction class, based on observed
// changes of the corresponding DUT register, is used.
//
extern function void [docs]set_backdoor(uvm_reg_backdoor bkdr,
string fname = "",
int lineno = 0);
// Function: get_backdoor
//
// Returns the user-defined backdoor for this register
//
// If ~null~, no user-defined backdoor has been defined.
// A user-defined backdoor is defined
// by using the <uvm_reg::set_backdoor()> method.
//
// If ~inherited~ is TRUE, returns the backdoor of the parent block
// if none have been specified for this register.
//
extern function uvm_reg_backdoor [docs]get_backdoor(bit inherited = 1);
// Function: clear_hdl_path
//
// Delete HDL paths
//
// Remove any previously specified HDL path to the register instance
// for the specified design abstraction.
//
extern function void [docs]clear_hdl_path (string kind = "RTL");
// Function: add_hdl_path
//
// Add an HDL path
//
// Add the specified HDL path to the register instance for the specified
// design abstraction. This method may be called more than once for the
// same design abstraction if the register is physically duplicated
// in the design abstraction
//
// For example, the following register
//
//| 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
//| Bits: 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//| +-+---+-------------+---+-------+
//| |A|xxx| B |xxx| C |
//| +-+---+-------------+---+-------+
//
// would be specified using the following literal value:
//
//| add_hdl_path('{ '{"A_reg", 15, 1},
//| '{"B_reg", 6, 7},
//| '{'C_reg", 0, 4} } );
//
// If the register is implemented using a single HDL variable,
// The array should specify a single slice with its ~offset~ and ~size~
// specified as -1. For example:
//
//| r1.add_hdl_path('{ '{"r1", -1, -1} });
//
extern function void [docs]add_hdl_path (uvm_hdl_path_slice slices[],
string kind = "RTL");
// Function: add_hdl_path_slice
//
// Append the specified HDL slice to the HDL path of the register instance
// for the specified design abstraction.
// If ~first~ is TRUE, starts the specification of a duplicate
// HDL implementation of the register.
//
extern function void [docs]add_hdl_path_slice(string name,
int offset,
int size,
bit first = 0,
string kind = "RTL");
// Function: has_hdl_path
//
// Check if a HDL path is specified
//
// Returns TRUE if the register instance has a HDL path defined for the
// specified design abstraction. If no design abstraction is specified,
// uses the default design abstraction specified for the parent block.
//
extern function bit [docs]has_hdl_path (string kind = "");
// Function: get_hdl_path
//
// Get the incremental HDL path(s)
//
// Returns the HDL path(s) defined for the specified design abstraction
// in the register instance.
// Returns only the component of the HDL paths that corresponds to
// the register, not a full hierarchical path
//
// If no design abstraction is specified, the default design abstraction
// for the parent block is used.
//
extern function void [docs]get_hdl_path (ref uvm_hdl_path_concat paths[$],
input string kind = "");
// Function: get_hdl_path_kinds
//
// Get design abstractions for which HDL paths have been defined
//
extern function void [docs]get_hdl_path_kinds (ref string kinds[$]);
// Function: get_full_hdl_path
//
// Get the full hierarchical HDL path(s)
//
// Returns the full hierarchical HDL path(s) defined for the specified
// design abstraction in the register instance.
// There may be more than one path returned even
// if only one path was defined for the register instance, if any of the
// parent components have more than one path defined for the same design
// abstraction
//
// If no design abstraction is specified, the default design abstraction
// for each ancestor block is used to get each incremental path.
//
extern function void [docs]get_full_hdl_path (ref uvm_hdl_path_concat paths[$],
input string kind = "",
input string separator = ".");
// Function: backdoor_read
//
// User-define backdoor read access
//
// Override the default string-based DPI backdoor access read
// for this register type.
// By default calls <uvm_reg::backdoor_read_func()>.
//
extern virtual task [docs]backdoor_read(uvm_reg_item rw);
// Function: backdoor_write
//
// User-defined backdoor read access
//
// Override the default string-based DPI backdoor access write
// for this register type.
//
extern virtual task [docs]backdoor_write(uvm_reg_item rw);
// Function: backdoor_read_func
//
// User-defined backdoor read access
//
// Override the default string-based DPI backdoor access read
// for this register type.
//
extern virtual function uvm_status_e [docs]backdoor_read_func(uvm_reg_item rw);
// Function: backdoor_watch
//
// User-defined DUT register change monitor
//
// Watch the DUT register corresponding to this abstraction class
// instance for any change in value and return when a value-change occurs.
// This may be implemented a string-based DPI access if the simulation
// tool provide a value-change callback facility. Such a facility does
// not exists in the standard SystemVerilog DPI and thus no
// default implementation for this method can be provided.
//
virtual task [docs]backdoor_watch(); endtask
//----------------
// Group: Coverage
//----------------
// Function: include_coverage
//
// Specify which coverage model that must be included in
// various block, register or memory abstraction class instances.
//
// The coverage models are specified by OR'ing or adding the
// <uvm_coverage_model_e> coverage model identifiers corresponding to the
// coverage model to be included.
//
// The scope specifies a hierarchical name or pattern identifying
// a block, memory or register abstraction class instances.
// Any block, memory or register whose full hierarchical name
// matches the specified scope will have the specified functional
// coverage models included in them.
//
// The scope can be specified as a POSIX regular expression
// or simple pattern.
// See <uvm_resource_base::Scope Interface> for more details.
//
//| uvm_reg::include_coverage("*", UVM_CVR_ALL);
//
// The specification of which coverage model to include in
// which abstraction class is stored in a <uvm_reg_cvr_t> resource in the
// <uvm_resource_db> resource database,
// in the "uvm_reg::" scope namespace.
//
extern static function void [docs]include_coverage(string scope,
uvm_reg_cvr_t models,
uvm_object accessor = null);
// Function: build_coverage
//
// Check if all of the specified coverage models must be built.
//
// Check which of the specified coverage model must be built
// in this instance of the register abstraction class,
// as specified by calls to <uvm_reg::include_coverage()>.
//
// Models are specified by adding the symbolic value of individual
// coverage model as defined in <uvm_coverage_model_e>.
// Returns the sum of all coverage models to be built in the
// register model.
//
extern protected function uvm_reg_cvr_t build_coverage(uvm_reg_cvr_t models);
// Function: add_coverage
//
// Specify that additional coverage models are available.
//
// Add the specified coverage model to the coverage models
// available in this class.
// Models are specified by adding the symbolic value of individual
// coverage model as defined in <uvm_coverage_model_e>.
//
// This method shall be called only in the constructor of
// subsequently derived classes.
//
extern virtual protected function void add_coverage(uvm_reg_cvr_t models);
// Function: has_coverage
//
// Check if register has coverage model(s)
//
// Returns TRUE if the register abstraction class contains a coverage model
// for all of the models specified.
// Models are specified by adding the symbolic value of individual
// coverage model as defined in <uvm_coverage_model_e>.
//
extern virtual function bit [docs]has_coverage(uvm_reg_cvr_t models);
// Function: set_coverage
//
// Turns on coverage measurement.
//
// Turns the collection of functional coverage measurements on or off
// for this register.
// The functional coverage measurement is turned on for every
// coverage model specified using <uvm_coverage_model_e> symbolic
// identifiers.
// Multiple functional coverage models can be specified by adding
// the functional coverage model identifiers.
// All other functional coverage models are turned off.
// Returns the sum of all functional
// coverage models whose measurements were previously on.
//
// This method can only control the measurement of functional
// coverage models that are present in the register abstraction classes,
// then enabled during construction.
// See the <uvm_reg::has_coverage()> method to identify
// the available functional coverage models.
//
extern virtual function uvm_reg_cvr_t [docs]set_coverage(uvm_reg_cvr_t is_on);
// Function: get_coverage
//
// Check if coverage measurement is on.
//
// Returns TRUE if measurement for all of the specified functional
// coverage models are currently on.
// Multiple functional coverage models can be specified by adding the
// functional coverage model identifiers.
//
// See <uvm_reg::set_coverage()> for more details.
//
extern virtual function bit [docs]get_coverage(uvm_reg_cvr_t is_on);
// Function: sample
//
// Functional coverage measurement method
//
// This method is invoked by the register abstraction class
// whenever it is read or written with the specified ~data~
// via the specified address ~map~.
// It is invoked after the read or write operation has completed
// but before the mirror has been updated.
//
// Empty by default, this method may be extended by the
// abstraction class generator to perform the required sampling
// in any provided functional coverage model.
//
protected virtual function void sample(uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
endfunction
// Function: sample_values
//
// Functional coverage measurement method for field values
//
// This method is invoked by the user
// or by the <uvm_reg_block::sample_values()> method of the parent block
// to trigger the sampling
// of the current field values in the
// register-level functional coverage model.
//
// This method may be extended by the
// abstraction class generator to perform the required sampling
// in any provided field-value functional coverage model.
//
virtual function void [docs]sample_values();
endfunction
/*local*/ function void [docs]XsampleX(uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
sample(data, byte_en, is_read, map);
endfunction
//-----------------
// Group: Callbacks
//-----------------
`uvm_register_cb(uvm_reg, uvm_reg_cbs)
// Task: pre_write
//
// Called before register write.
//
// If the specified data value, access ~path~ or address ~map~ are modified,
// the updated data value, access path or address map will be used
// to perform the register operation.
// If the ~status~ is modified to anything other than <UVM_IS_OK>,
// the operation is aborted.
//
// The registered callback methods are invoked after the invocation
// of this method.
// All register callbacks are executed before the corresponding
// field callbacks
//
virtual task [docs]pre_write(uvm_reg_item rw); endtask
// Task: post_write
//
// Called after register write.
//
// If the specified ~status~ is modified,
// the updated status will be
// returned by the register operation.
//
// The registered callback methods are invoked before the invocation
// of this method.
// All register callbacks are executed before the corresponding
// field callbacks
//
virtual task [docs]post_write(uvm_reg_item rw); endtask
// Task: pre_read
//
// Called before register read.
//
// If the specified access ~path~ or address ~map~ are modified,
// the updated access path or address map will be used to perform
// the register operation.
// If the ~status~ is modified to anything other than <UVM_IS_OK>,
// the operation is aborted.
//
// The registered callback methods are invoked after the invocation
// of this method.
// All register callbacks are executed before the corresponding
// field callbacks
//
virtual task [docs]pre_read(uvm_reg_item rw); endtask
// Task: post_read
//
// Called after register read.
//
// If the specified readback data or ~status~ is modified,
// the updated readback data or status will be
// returned by the register operation.
//
// The registered callback methods are invoked before the invocation
// of this method.
// All register callbacks are executed before the corresponding
// field callbacks
//
virtual task [docs]post_read(uvm_reg_item rw); endtask
extern virtual function void [docs]do_print (uvm_printer printer);
extern virtual function string [docs]convert2string();
extern virtual function uvm_object [docs]clone ();
extern virtual function void [docs]do_copy (uvm_object rhs);
extern virtual function bit [docs]do_compare (uvm_object rhs,
uvm_comparer comparer);
extern virtual function void [docs]do_pack (uvm_packer packer);
extern virtual function void [docs]do_unpack (uvm_packer packer);
endclass: uvm_reg
//------------------------------------------------------------------------------
// IMPLEMENTATION
//------------------------------------------------------------------------------
// new
function uvm_reg::new(string name="", int unsigned n_bits, int has_coverage);
super.new(name);
if (n_bits == 0) begin
`uvm_error("RegModel", $sformatf("Register \"%s\" cannot have 0 bits", get_name()));
n_bits = 1;
end
m_n_bits = n_bits;
m_has_cover = has_coverage;
m_atomic = new(1);
m_n_used_bits = 0;
m_locked = 0;
m_is_busy = 0;
m_is_locked_by_field = 1'b0;
m_hdl_paths_pool = new("hdl_paths");
if (n_bits > m_max_size)
m_max_size = n_bits;
endfunction: new
// configure
function void uvm_reg::configure (uvm_reg_block blk_parent,
uvm_reg_file regfile_parent=null,
string hdl_path = "");
if (blk_parent == null) begin
`uvm_error("UVM/REG/CFG/NOBLK", {"uvm_reg::configure() called without a parent block for instance \"", get_name(), "\" of register type \"", get_type_name(), "\"."})
return;
end
m_parent = blk_parent;
m_parent.add_reg(this);
m_regfile_parent = regfile_parent;
if (hdl_path != "")
add_hdl_path_slice(hdl_path, -1, -1);
endfunction: configure
// add_field
function void uvm_reg::add_field(uvm_reg_field field);
int offset;
int idx;
if (m_locked) begin
`uvm_error("RegModel", "Cannot add field to locked register model");
return;
end
if (field == null) `uvm_fatal("RegModel", "Attempting to register NULL field");
// Store fields in LSB to MSB order
offset = field.get_lsb_pos();
idx = -1;
foreach (m_fields[i]) begin
if (offset < m_fields[i].get_lsb_pos()) begin
int j = i;
m_fields.insert(j, field);
idx = i;
break;
end
end
if (idx < 0) begin
m_fields.push_back(field);
idx = m_fields.size()-1;
end
m_n_used_bits += field.get_n_bits();
// Check if there are too many fields in the register
if (m_n_used_bits > m_n_bits) begin
`uvm_error("RegModel",
$sformatf("Fields use more bits (%0d) than available in register \"%s\" (%0d)",
m_n_used_bits, get_name(), m_n_bits));
end
// Check if there are overlapping fields
if (idx > 0) begin
if (m_fields[idx-1].get_lsb_pos() +
m_fields[idx-1].get_n_bits() > offset) begin
`uvm_error("RegModel", $sformatf("Field %s overlaps field %s in register \"%s\"",
m_fields[idx-1].get_name(),
field.get_name(), get_name()));
end
end
if (idx < m_fields.size()-1) begin
if (offset + field.get_n_bits() >
m_fields[idx+1].get_lsb_pos()) begin
`uvm_error("RegModel", $sformatf("Field %s overlaps field %s in register \"%s\"",
field.get_name(),
m_fields[idx+1].get_name(),
get_name()));
end
end
endfunction: add_field
// Xlock_modelX
function void uvm_reg::Xlock_modelX();
if (m_locked)
return;
m_locked = 1;
endfunction
//----------------------
// Group- User Frontdoor
//----------------------
// set_frontdoor
function void uvm_reg::set_frontdoor(uvm_reg_frontdoor ftdr,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
uvm_reg_map_info map_info;
ftdr.fname = m_fname;
ftdr.lineno = m_lineno;
map = get_local_map(map, "set_frontdoor()");
if (map == null)
return;
map_info = map.get_reg_map_info(this);
if (map_info == null)
map.add_reg(this, -1, "RW", 1, ftdr);
else begin
map_info.frontdoor = ftdr;
end
endfunction: set_frontdoor
// get_frontdoor
function uvm_reg_frontdoor uvm_reg::get_frontdoor(uvm_reg_map map = null);
uvm_reg_map_info map_info;
map = get_local_map(map, "get_frontdoor()");
if (map == null)
return null;
map_info = map.get_reg_map_info(this);
return map_info.frontdoor;
endfunction: get_frontdoor
// set_backdoor
function void uvm_reg::set_backdoor(uvm_reg_backdoor bkdr,
string fname = "",
int lineno = 0);
bkdr.fname = fname;
bkdr.lineno = lineno;
if (m_backdoor != null &&
m_backdoor.has_update_threads()) begin
`uvm_warning("RegModel", "Previous register backdoor still has update threads running. Backdoors with active mirroring should only be set before simulation starts.");
end
m_backdoor = bkdr;
endfunction: set_backdoor
// get_backdoor
function uvm_reg_backdoor uvm_reg::get_backdoor(bit inherited = 1);
if (m_backdoor == null && inherited) begin
uvm_reg_block blk = get_parent();
uvm_reg_backdoor bkdr;
while (blk != null) begin
bkdr = blk.get_backdoor();
if (bkdr != null) begin
m_backdoor = bkdr;
break;
end
blk = blk.get_parent();
end
end
return m_backdoor;
endfunction: get_backdoor
// clear_hdl_path
function void uvm_reg::clear_hdl_path(string kind = "RTL");
if (kind == "ALL") begin
m_hdl_paths_pool = new("hdl_paths");
return;
end
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
if (!m_hdl_paths_pool.exists(kind)) begin
`uvm_warning("RegModel",{"Unknown HDL Abstraction '",kind,"'"})
return;
end
m_hdl_paths_pool.delete(kind);
endfunction
// add_hdl_path
function void uvm_reg::add_hdl_path(uvm_hdl_path_slice slices[],
string kind = "RTL");
uvm_queue #(uvm_hdl_path_concat) paths = m_hdl_paths_pool.get(kind);
uvm_hdl_path_concat concat = new();
concat.set(slices);
paths.push_back(concat);
endfunction
// add_hdl_path_slice
function void uvm_reg::add_hdl_path_slice(string name,
int offset,
int size,
bit first = 0,
string kind = "RTL");
uvm_queue #(uvm_hdl_path_concat) paths = m_hdl_paths_pool.get(kind);
uvm_hdl_path_concat concat;
if (first || paths.size() == 0) begin
concat = new();
paths.push_back(concat);
end
else
concat = paths.get(paths.size()-1);
concat.add_path(name, offset, size);
endfunction
// has_hdl_path
function bit uvm_reg::has_hdl_path(string kind = "");
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
return m_hdl_paths_pool.exists(kind);
endfunction
// get_hdl_path_kinds
function void uvm_reg::get_hdl_path_kinds (ref string kinds[$]);
string kind;
kinds.delete();
if (!m_hdl_paths_pool.first(kind))
return;
do
kinds.push_back(kind);
while (m_hdl_paths_pool.next(kind));
endfunction
// get_hdl_path
function void uvm_reg::get_hdl_path(ref uvm_hdl_path_concat paths[$],
input string kind = "");
uvm_queue #(uvm_hdl_path_concat) hdl_paths;
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
if (!has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"Register does not have hdl path defined for abstraction '",kind,"'"})
return;
end
hdl_paths = m_hdl_paths_pool.get(kind);
for (int i=0; i<hdl_paths.size();i++) begin
paths.push_back(hdl_paths.get(i));
end
endfunction
// get_full_hdl_path
function void uvm_reg::get_full_hdl_path(ref uvm_hdl_path_concat paths[$],
input string kind = "",
input string separator = ".");
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
if (!has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"Register ",get_full_name()," does not have hdl path defined for abstraction '",kind,"'"})
return;
end
begin
uvm_queue #(uvm_hdl_path_concat) hdl_paths = m_hdl_paths_pool.get(kind);
string parent_paths[$];
if (m_regfile_parent != null)
m_regfile_parent.get_full_hdl_path(parent_paths, kind, separator);
else
m_parent.get_full_hdl_path(parent_paths, kind, separator);
for (int i=0; i<hdl_paths.size();i++) begin
uvm_hdl_path_concat hdl_concat = hdl_paths.get(i);
foreach (parent_paths[j]) begin
uvm_hdl_path_concat t = new;
foreach (hdl_concat.slices[k]) begin
if (hdl_concat.slices[k].path == "")
t.add_path(parent_paths[j]);
else
t.add_path({ parent_paths[j], separator, hdl_concat.slices[k].path },
hdl_concat.slices[k].offset,
hdl_concat.slices[k].size);
end
paths.push_back(t);
end
end
end
endfunction
// set_offset
function void uvm_reg::set_offset (uvm_reg_map map,
uvm_reg_addr_t offset,
bit unmapped = 0);
uvm_reg_map orig_map = map;
if (m_maps.num() > 1 && map == null) begin
`uvm_error("RegModel",{"set_offset requires a non-null map when register '",
get_full_name(),"' belongs to more than one map."})
return;
end
map = get_local_map(map,"set_offset()");
if (map == null)
return;
map.m_set_reg_offset(this, offset, unmapped);
endfunction
// set_parent
function void uvm_reg::set_parent(uvm_reg_block blk_parent,
uvm_reg_file regfile_parent);
if (m_parent != null) begin
// ToDo: remove register from previous parent
end
m_parent = blk_parent;
m_regfile_parent = regfile_parent;
endfunction
// get_parent
function uvm_reg_block uvm_reg::get_parent();
return get_block();
endfunction
// get_regfile
function uvm_reg_file uvm_reg::get_regfile();
return m_regfile_parent;
endfunction
// get_full_name
function string uvm_reg::get_full_name();
if (m_regfile_parent != null)
return {m_regfile_parent.get_full_name(), ".", get_name()};
if (m_parent != null)
return {m_parent.get_full_name(), ".", get_name()};
return get_name();
endfunction: get_full_name
// add_map
function void uvm_reg::add_map(uvm_reg_map map);
m_maps[map] = 1;
endfunction
// get_maps
function void uvm_reg::get_maps(ref uvm_reg_map maps[$]);
foreach (m_maps[map])
maps.push_back(map);
endfunction
// get_n_maps
function int uvm_reg::get_n_maps();
return m_maps.num();
endfunction
// is_in_map
function bit uvm_reg::is_in_map(uvm_reg_map map);
if (m_maps.exists(map))
return 1;
foreach (m_maps[l]) begin
uvm_reg_map local_map = l;
uvm_reg_map parent_map = local_map.get_parent_map();
while (parent_map != null) begin
if (parent_map == map)
return 1;
parent_map = parent_map.get_parent_map();
end
end
return 0;
endfunction
// get_local_map
function uvm_reg_map uvm_reg::get_local_map(uvm_reg_map map, string caller="");
if (map == null)
return get_default_map();
if (m_maps.exists(map))
return map;
foreach (m_maps[l]) begin
uvm_reg_map local_map=l;
uvm_reg_map parent_map = local_map.get_parent_map();
while (parent_map != null) begin
if (parent_map == map)
return local_map;
parent_map = parent_map.get_parent_map();
end
end
`uvm_warning("RegModel",
{"Register '",get_full_name(),"' is not contained within map '",map.get_full_name(),"'",
(caller == "" ? "": {" (called from ",caller,")"}) })
return null;
endfunction
// get_default_map
function uvm_reg_map uvm_reg::get_default_map(string caller="");
// if reg is not associated with any map, return ~null~
if (m_maps.num() == 0) begin
`uvm_warning("RegModel",
{"Register '",get_full_name(),"' is not registered with any map",
(caller == "" ? "": {" (called from ",caller,")"})})
return null;
end
// if only one map, choose that
if (m_maps.num() == 1) begin
uvm_reg_map map;
void'(m_maps.first(map));
return map;
end
// try to choose one based on default_map in parent blocks.
foreach (m_maps[l]) begin
uvm_reg_map map = l;
uvm_reg_block blk = map.get_parent();
uvm_reg_map default_map = blk.get_default_map();
if (default_map != null) begin
uvm_reg_map local_map = get_local_map(default_map,"get_default_map()");
if (local_map != null)
return local_map;
end
end
// if that fails, choose the first in this reg's maps
begin
uvm_reg_map map;
void'(m_maps.first(map));
return map;
end
endfunction
// get_rights
function string uvm_reg::get_rights(uvm_reg_map map = null);
uvm_reg_map_info info;
map = get_local_map(map,"get_rights()");
if (map == null)
return "RW";
info = map.get_reg_map_info(this);
return info.rights;
endfunction
// get_block
function uvm_reg_block uvm_reg::get_block();
get_block = m_parent;
endfunction
// get_offset
function uvm_reg_addr_t uvm_reg::get_offset(uvm_reg_map map = null);
uvm_reg_map_info map_info;
uvm_reg_map orig_map = map;
map = get_local_map(map,"get_offset()");
if (map == null)
return -1;
map_info = map.get_reg_map_info(this);
if (map_info.unmapped) begin
`uvm_warning("RegModel", {"Register '",get_name(),
"' is unmapped in map '",
((orig_map == null) ? map.get_full_name() : orig_map.get_full_name()),"'"})
return -1;
end
return map_info.offset;
endfunction
// get_addresses
function int uvm_reg::get_addresses(uvm_reg_map map=null, ref uvm_reg_addr_t addr[]);
uvm_reg_map_info map_info;
uvm_reg_map system_map;
uvm_reg_map orig_map = map;
map = get_local_map(map,"get_addresses()");
if (map == null)
return -1;
map_info = map.get_reg_map_info(this);
if (map_info.unmapped) begin
`uvm_warning("RegModel", {"Register '",get_name(),
"' is unmapped in map '",
((orig_map == null) ? map.get_full_name() : orig_map.get_full_name()),"'"})
return -1;
end
addr = map_info.addr;
system_map = map.get_root_map();
return map.get_n_bytes();
endfunction
// get_address
function uvm_reg_addr_t uvm_reg::get_address(uvm_reg_map map = null);
uvm_reg_addr_t addr[];
void'(get_addresses(map,addr));
return addr[0];
endfunction
// get_n_bits
function int unsigned uvm_reg::get_n_bits();
return m_n_bits;
endfunction
// get_n_bytes
function int unsigned uvm_reg::get_n_bytes();
return ((m_n_bits-1) / 8) + 1;
endfunction
// get_max_size
function int unsigned uvm_reg::get_max_size();
return m_max_size;
endfunction: get_max_size
// get_fields
function void uvm_reg::get_fields(ref uvm_reg_field fields[$]);
foreach(m_fields[i])
fields.push_back(m_fields[i]);
endfunction
// get_field_by_name
function uvm_reg_field uvm_reg::get_field_by_name(string name);
foreach (m_fields[i])
if (m_fields[i].get_name() == name)
return m_fields[i];
`uvm_warning("RegModel", {"Unable to locate field '",name,
"' in register '",get_name(),"'"})
return null;
endfunction
// Xget_field_accessX
//
// Returns "WO" if all of the fields in the registers are write-only
// Returns "RO" if all of the fields in the registers are read-only
// Returns "RW" otherwise.
function string uvm_reg::Xget_fields_accessX(uvm_reg_map map);
bit is_R;
bit is_W;
foreach(m_fields[i]) begin
case (m_fields[i].get_access(map))
"RO",
"RC",
"RS":
is_R = 1;
"WO",
"WOC",
"WOS",
"WO1":
is_W = 1;
default:
return "RW";
endcase
if (is_R && is_W) return "RW";
end
case ({is_R, is_W})
2'b01: return "WO";
2'b10: return "RO";
endcase
return "RW";
endfunction
//---------
// COVERAGE
//---------
// include_coverage
function void uvm_reg::include_coverage(string scope,
uvm_reg_cvr_t models,
uvm_object accessor = null);
uvm_reg_cvr_rsrc_db::set({"uvm_reg::", scope},
"include_coverage",
models, accessor);
endfunction
// build_coverage
function uvm_reg_cvr_t uvm_reg::build_coverage(uvm_reg_cvr_t models);
build_coverage = UVM_NO_COVERAGE;
void'(uvm_reg_cvr_rsrc_db::read_by_name({"uvm_reg::", get_full_name()},
"include_coverage",
build_coverage, this));
return build_coverage & models;
endfunction: build_coverage
// add_coverage
function void uvm_reg::add_coverage(uvm_reg_cvr_t models);
m_has_cover |= models;
endfunction: add_coverage
// has_coverage
function bit uvm_reg::has_coverage(uvm_reg_cvr_t models);
return ((m_has_cover & models) == models);
endfunction: has_coverage
// set_coverage
function uvm_reg_cvr_t uvm_reg::set_coverage(uvm_reg_cvr_t is_on);
if (is_on == uvm_reg_cvr_t'(UVM_NO_COVERAGE)) begin
m_cover_on = is_on;
return m_cover_on;
end
m_cover_on = m_has_cover & is_on;
return m_cover_on;
endfunction: set_coverage
// get_coverage
function bit uvm_reg::get_coverage(uvm_reg_cvr_t is_on);
if (has_coverage(is_on) == 0)
return 0;
return ((m_cover_on & is_on) == is_on);
endfunction: get_coverage
//---------
// ACCESS
//---------
// set
function void uvm_reg::set(uvm_reg_data_t value,
string fname = "",
int lineno = 0);
// Split the value into the individual fields
m_fname = fname;
m_lineno = lineno;
foreach (m_fields[i])
m_fields[i].set((value >> m_fields[i].get_lsb_pos()) &
((1 << m_fields[i].get_n_bits()) - 1));
endfunction: set
// predict
function bit uvm_reg::predict (uvm_reg_data_t value,
uvm_reg_byte_en_t be = -1,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_path_e path = UVM_FRONTDOOR,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
uvm_reg_item rw = new;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.fname = fname;
rw.lineno = lineno;
do_predict(rw, kind, be);
predict = (rw.status == UVM_NOT_OK) ? 0 : 1;
endfunction: predict
// do_predict
function void uvm_reg::do_predict(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
uvm_reg_data_t reg_value = rw.value[0];
m_fname = rw.fname;
m_lineno = rw.lineno;
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
rw.value[0] = reg_value;
endfunction: do_predict
// get
function uvm_reg_data_t uvm_reg::get(string fname = "",
int lineno = 0);
// Concatenate the value of the individual fields
// to form the register value
m_fname = fname;
m_lineno = lineno;
get = 0;
foreach (m_fields[i])
get |= m_fields[i].get() << m_fields[i].get_lsb_pos();
endfunction: get
// get_mirrored_value
function uvm_reg_data_t uvm_reg::get_mirrored_value(string fname = "",
int lineno = 0);
// Concatenate the value of the individual fields
// to form the register value
m_fname = fname;
m_lineno = lineno;
get_mirrored_value = 0;
foreach (m_fields[i])
get_mirrored_value |= m_fields[i].get_mirrored_value() << m_fields[i].get_lsb_pos();
endfunction: get_mirrored_value
// reset
function void 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
// get_reset
function uvm_reg_data_t uvm_reg::get_reset(string kind = "HARD");
// Concatenate the value of the individual fields
// to form the register value
get_reset = 0;
foreach (m_fields[i])
get_reset |= m_fields[i].get_reset(kind) << m_fields[i].get_lsb_pos();
endfunction: get_reset
// has_reset
function bit uvm_reg::has_reset(string kind = "HARD",
bit delete = 0);
has_reset = 0;
foreach (m_fields[i]) begin
has_reset |= m_fields[i].has_reset(kind, delete);
if (!delete && has_reset)
return 1;
end
endfunction: has_reset
// set_reset
function void uvm_reg::set_reset(uvm_reg_data_t value,
string kind = "HARD");
foreach (m_fields[i]) begin
m_fields[i].set_reset(value >> m_fields[i].get_lsb_pos(), kind);
end
endfunction: set_reset
//-----------
// BUS ACCESS
//-----------
// needs_update
function bit uvm_reg::needs_update();
needs_update = 0;
foreach (m_fields[i]) begin
if (m_fields[i].needs_update()) begin
return 1;
end
end
endfunction: needs_update
// update
task uvm_reg::update(output uvm_status_e status,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_data_t upd;
status = UVM_IS_OK;
if (!needs_update()) return;
// 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();
write(status, upd, path, map, parent, prior, extension, fname, lineno);
endtask: update
// write
task uvm_reg::write(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// create an abstract transaction for this operation
uvm_reg_item rw;
XatomicX(1);
set(value);
rw = uvm_reg_item::type_id::create("write_item",,get_full_name());
rw.element = this;
rw.element_kind = UVM_REG;
rw.kind = UVM_WRITE;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.parent = parent;
rw.prior = prior;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
do_write(rw);
status = rw.status;
XatomicX(0);
endtask
// do_write
task uvm_reg::do_write (uvm_reg_item rw);
uvm_reg_cb_iter cbs = new(this);
uvm_reg_map_info map_info;
uvm_reg_data_t value;
m_fname = rw.fname;
m_lineno = rw.lineno;
if (!Xcheck_accessX(rw,map_info,"write()"))
return;
XatomicX(1);
m_write_in_progress = 1'b1;
rw.value[0] &= ((1 << m_n_bits)-1);
value = rw.value[0];
rw.status = UVM_IS_OK;
// PRE-WRITE CBS - FIELDS
begin : pre_write_callbacks
uvm_reg_data_t msk;
int lsb;
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
lsb = f.get_lsb_pos();
msk = ((1<<f.get_n_bits())-1) << lsb;
rw.value[0] = (value & msk) >> lsb;
f.pre_write(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next()) begin
rw.element = f;
rw.element_kind = UVM_FIELD;
cb.pre_write(rw);
end
value = (value & ~msk) | (rw.value[0] << lsb);
end
end
rw.element = this;
rw.element_kind = UVM_REG;
rw.value[0] = value;
// PRE-WRITE CBS - REG
pre_write(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.pre_write(rw);
if (rw.status != UVM_IS_OK) begin
m_write_in_progress = 1'b0;
XatomicX(0);
return;
end
// EXECUTE WRITE...
case (rw.path)
// ...VIA USER BACKDOOR
UVM_BACKDOOR: begin
uvm_reg_data_t final_val;
uvm_reg_backdoor bkdr = get_backdoor();
value = rw.value[0];
// Mimick the final value after a physical read
rw.kind = UVM_READ;
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
if (rw.status == UVM_NOT_OK) begin
m_write_in_progress = 1'b0;
return;
end
begin
foreach (m_fields[i]) begin
uvm_reg_data_t field_val;
int lsb = m_fields[i].get_lsb_pos();
int sz = m_fields[i].get_n_bits();
field_val = m_fields[i].XpredictX((rw.value[0] >> lsb) & ((1<<sz)-1),
(value >> lsb) & ((1<<sz)-1),
rw.local_map);
final_val |= field_val << lsb;
end
end
rw.kind = UVM_WRITE;
rw.value[0] = final_val;
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
do_predict(rw, UVM_PREDICT_WRITE);
end
UVM_FRONTDOOR: begin
uvm_reg_map system_map = rw.local_map.get_root_map();
m_is_busy = 1;
// ...VIA USER FRONTDOOR
if (map_info.frontdoor != null) begin
uvm_reg_frontdoor fd = map_info.frontdoor;
fd.rw_info = rw;
if (fd.sequencer == null)
fd.sequencer = system_map.get_sequencer();
fd.start(fd.sequencer, rw.parent);
end
// ...VIA BUILT-IN FRONTDOOR
else begin : built_in_frontdoor
rw.local_map.do_write(rw);
end
m_is_busy = 0;
if (system_map.get_auto_predict()) begin
uvm_status_e status;
if (rw.status != UVM_NOT_OK) begin
sample(value, -1, 0, rw.map);
m_parent.XsampleX(map_info.offset, 0, rw.map);
end
status = rw.status; // do_predict will override rw.status, so we save it here
do_predict(rw, UVM_PREDICT_WRITE);
rw.status = status;
end
end
endcase
value = rw.value[0];
// POST-WRITE CBS - REG
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_write(rw);
post_write(rw);
// POST-WRITE CBS - FIELDS
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
rw.element = f;
rw.element_kind = UVM_FIELD;
rw.value[0] = (value >> f.get_lsb_pos()) & ((1<<f.get_n_bits())-1);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_write(rw);
f.post_write(rw);
end
rw.value[0] = value;
rw.element = this;
rw.element_kind = UVM_REG;
// REPORT
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "RegModel")) begin
string path_s,value_s;
if (rw.path == UVM_FRONTDOOR)
path_s = (map_info.frontdoor != null) ? "user frontdoor" :
{"map ",rw.map.get_full_name()};
else
path_s = (get_backdoor() != null) ? "user backdoor" : "DPI backdoor";
value_s = $sformatf("=0x%0h",rw.value[0]);
uvm_report_info("RegModel", {"Wrote register via ",path_s,": ",
get_full_name(),value_s}, UVM_HIGH);
end
m_write_in_progress = 1'b0;
XatomicX(0);
endtask: do_write
// read
task uvm_reg::read(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
XatomicX(1);
XreadX(status, value, path, map, parent, prior, extension, fname, lineno);
XatomicX(0);
endtask: read
// XreadX
task uvm_reg::XreadX(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_path_e path,
input uvm_reg_map map,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// create an abstract transaction for this operation
uvm_reg_item rw;
rw = uvm_reg_item::type_id::create("read_item",,get_full_name());
rw.element = this;
rw.element_kind = UVM_REG;
rw.kind = UVM_READ;
rw.value[0] = 0;
rw.path = path;
rw.map = map;
rw.parent = parent;
rw.prior = prior;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
do_read(rw);
status = rw.status;
value = rw.value[0];
endtask: XreadX
// do_read
task uvm_reg::do_read(uvm_reg_item rw);
uvm_reg_cb_iter cbs = new(this);
uvm_reg_map_info map_info;
uvm_reg_data_t value;
uvm_reg_data_t exp;
m_fname = rw.fname;
m_lineno = rw.lineno;
if (!Xcheck_accessX(rw,map_info,"read()"))
return;
m_read_in_progress = 1'b1;
rw.status = UVM_IS_OK;
// PRE-READ CBS - FIELDS
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
rw.element = f;
rw.element_kind = UVM_FIELD;
m_fields[i].pre_read(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.pre_read(rw);
end
rw.element = this;
rw.element_kind = UVM_REG;
// PRE-READ CBS - REG
pre_read(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.pre_read(rw);
if (rw.status != UVM_IS_OK) begin
m_read_in_progress = 1'b0;
return;
end
// EXECUTE READ...
case (rw.path)
// ...VIA USER BACKDOOR
UVM_BACKDOOR: begin
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_map map = uvm_reg_map::backdoor();
if (map.get_check_on_read()) exp = get();
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
value = rw.value[0];
// Need to clear RC fields, set RS fields and mask WO fields
if (rw.status != UVM_NOT_OK) begin
uvm_reg_data_t wo_mask;
foreach (m_fields[i]) begin
string acc = m_fields[i].get_access(uvm_reg_map::backdoor());
if (acc == "RC" ||
acc == "WRC" ||
acc == "WSRC" ||
acc == "W1SRC" ||
acc == "W0SRC") begin
value &= ~(((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos());
end
else if (acc == "RS" ||
acc == "WRS" ||
acc == "WCRS" ||
acc == "W1CRS" ||
acc == "W0CRS") begin
value |= (((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos());
end
else if (acc == "WO" ||
acc == "WOC" ||
acc == "WOS" ||
acc == "WO1") begin
wo_mask |= ((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos();
end
end
if (value != rw.value[0]) begin
uvm_reg_data_t saved;
saved = rw.value[0];
rw.value[0] = value;
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
rw.value[0] = saved;
end
rw.value[0] &= ~wo_mask;
if (map.get_check_on_read() &&
rw.status != UVM_NOT_OK) begin
void'(do_check(exp, rw.value[0], map));
end
do_predict(rw, UVM_PREDICT_READ);
end
end
UVM_FRONTDOOR: begin
uvm_reg_map system_map = rw.local_map.get_root_map();
m_is_busy = 1;
if (rw.local_map.get_check_on_read()) exp = get();
// ...VIA USER FRONTDOOR
if (map_info.frontdoor != null) begin
uvm_reg_frontdoor fd = map_info.frontdoor;
fd.rw_info = rw;
if (fd.sequencer == null)
fd.sequencer = system_map.get_sequencer();
fd.start(fd.sequencer, rw.parent);
end
// ...VIA BUILT-IN FRONTDOOR
else begin
rw.local_map.do_read(rw);
end
m_is_busy = 0;
if (system_map.get_auto_predict()) begin
uvm_status_e status;
if (rw.local_map.get_check_on_read() &&
rw.status != UVM_NOT_OK) begin
void'(do_check(exp, rw.value[0], system_map));
end
if (rw.status != UVM_NOT_OK) begin
sample(rw.value[0], -1, 1, rw.map);
m_parent.XsampleX(map_info.offset, 1, rw.map);
end
status = rw.status; // do_predict will override rw.status, so we save it here
do_predict(rw, UVM_PREDICT_READ);
rw.status = status;
end
end
endcase
value = rw.value[0]; // preserve
// POST-READ CBS - REG
for (uvm_reg_cbs cb = cbs.first(); cb != null; cb = cbs.next())
cb.post_read(rw);
post_read(rw);
// POST-READ CBS - FIELDS
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
rw.element = f;
rw.element_kind = UVM_FIELD;
rw.value[0] = (value >> f.get_lsb_pos()) & ((1<<f.get_n_bits())-1);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_read(rw);
f.post_read(rw);
end
rw.value[0] = value; // restore
rw.element = this;
rw.element_kind = UVM_REG;
// REPORT
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "RegModel")) begin
string path_s,value_s;
if (rw.path == UVM_FRONTDOOR)
path_s = (map_info.frontdoor != null) ? "user frontdoor" :
{"map ",rw.map.get_full_name()};
else
path_s = (get_backdoor() != null) ? "user backdoor" : "DPI backdoor";
value_s = $sformatf("=%0h",rw.value[0]);
uvm_report_info("RegModel", {"Read register via ",path_s,": ",
get_full_name(),value_s}, UVM_HIGH);
end
m_read_in_progress = 1'b0;
endtask: do_read
// Xcheck_accessX
function bit uvm_reg::Xcheck_accessX (input uvm_reg_item rw,
output uvm_reg_map_info map_info,
input string caller);
if (rw.path == UVM_DEFAULT_PATH)
rw.path = m_parent.get_default_path();
if (rw.path == UVM_BACKDOOR) begin
if (get_backdoor() == null && !has_hdl_path()) begin
`uvm_warning("RegModel",
{"No backdoor access available for register '",get_full_name(),
"' . Using frontdoor instead."})
rw.path = UVM_FRONTDOOR;
end
else
rw.map = uvm_reg_map::backdoor();
end
if (rw.path != UVM_BACKDOOR) begin
rw.local_map = get_local_map(rw.map,caller);
if (rw.local_map == null) begin
`uvm_error(get_type_name(),
{"No transactor available to physically access register on map '",
rw.map.get_full_name(),"'"})
rw.status = UVM_NOT_OK;
return 0;
end
map_info = rw.local_map.get_reg_map_info(this);
if (map_info.frontdoor == null && map_info.unmapped) begin
`uvm_error("RegModel", {"Register '",get_full_name(),
"' unmapped in map '",
(rw.map==null)? rw.local_map.get_full_name():rw.map.get_full_name(),
"' and does not have a user-defined frontdoor"})
rw.status = UVM_NOT_OK;
return 0;
end
if (rw.map == null)
rw.map = rw.local_map;
end
return 1;
endfunction
// is_busy
function bit uvm_reg::is_busy();
return m_is_busy;
endfunction
// Xset_busyX
function void uvm_reg::Xset_busyX(bit busy);
m_is_busy = busy;
endfunction
// Xis_loacked_by_fieldX
function bit uvm_reg::Xis_locked_by_fieldX();
return m_is_locked_by_field;
endfunction
// backdoor_write
task uvm_reg::backdoor_write(uvm_reg_item rw);
uvm_hdl_path_concat paths[$];
bit ok=1;
get_full_hdl_path(paths,rw.bd_kind);
foreach (paths[i]) begin
uvm_hdl_path_concat hdl_concat = paths[i];
foreach (hdl_concat.slices[j]) begin
`uvm_info("RegMem", {"backdoor_write to ",
hdl_concat.slices[j].path},UVM_DEBUG)
if (hdl_concat.slices[j].offset < 0) begin
ok &= uvm_hdl_deposit(hdl_concat.slices[j].path,rw.value[0]);
continue;
end
begin
uvm_reg_data_t slice;
slice = rw.value[0] >> hdl_concat.slices[j].offset;
slice &= (1 << hdl_concat.slices[j].size)-1;
ok &= uvm_hdl_deposit(hdl_concat.slices[j].path, slice);
end
end
end
rw.status = (ok ? UVM_IS_OK : UVM_NOT_OK);
endtask
// backdoor_read
task uvm_reg::backdoor_read (uvm_reg_item rw);
rw.status = backdoor_read_func(rw);
endtask
// backdoor_read_func
function uvm_status_e uvm_reg::backdoor_read_func(uvm_reg_item rw);
uvm_hdl_path_concat paths[$];
uvm_reg_data_t val;
bit ok=1;
get_full_hdl_path(paths,rw.bd_kind);
foreach (paths[i]) begin
uvm_hdl_path_concat hdl_concat = paths[i];
val = 0;
foreach (hdl_concat.slices[j]) begin
`uvm_info("RegMem", {"backdoor_read from %s ",
hdl_concat.slices[j].path},UVM_DEBUG)
if (hdl_concat.slices[j].offset < 0) begin
ok &= uvm_hdl_read(hdl_concat.slices[j].path,val);
continue;
end
begin
uvm_reg_data_t slice;
int k = hdl_concat.slices[j].offset;
ok &= uvm_hdl_read(hdl_concat.slices[j].path, slice);
repeat (hdl_concat.slices[j].size) begin
val[k++] = slice[0];
slice >>= 1;
end
end
end
val &= (1 << m_n_bits)-1;
if (i == 0)
rw.value[0] = val;
if (val != rw.value[0]) begin
`uvm_error("RegModel", $sformatf("Backdoor read of register %s with multiple HDL copies: values are not the same: %0h at path '%s', and %0h at path '%s'. Returning first value.",
get_full_name(),
rw.value[0], uvm_hdl_concat2string(paths[0]),
val, uvm_hdl_concat2string(paths[i])));
return UVM_NOT_OK;
end
`uvm_info("RegMem",
$sformatf("returned backdoor value 0x%0x",rw.value[0]),UVM_DEBUG);
end
rw.status = (ok) ? UVM_IS_OK : UVM_NOT_OK;
return rw.status;
endfunction
// poke
task uvm_reg::poke(output uvm_status_e status,
input uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_item rw;
m_fname = fname;
m_lineno = lineno;
if (bkdr == null && !has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"No backdoor access available to poke register '",get_full_name(),"'"})
status = UVM_NOT_OK;
return;
end
if (!m_is_locked_by_field)
XatomicX(1);
// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("reg_poke_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_REG;
rw.kind = UVM_WRITE;
rw.bd_kind = kind;
rw.value[0] = value & ((1 << m_n_bits)-1);
rw.parent = parent;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
status = rw.status;
`uvm_info("RegModel", $sformatf("Poked register \"%s\": 'h%h",
get_full_name(), value),UVM_HIGH);
do_predict(rw, UVM_PREDICT_WRITE);
if (!m_is_locked_by_field)
XatomicX(0);
endtask: poke
// peek
task uvm_reg::peek(output uvm_status_e status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_item rw;
m_fname = fname;
m_lineno = lineno;
if (bkdr == null && !has_hdl_path(kind)) begin
`uvm_error("RegModel",
$sformatf("No backdoor access available to peek register \"%s\"",
get_full_name()));
status = UVM_NOT_OK;
return;
end
if(!m_is_locked_by_field)
XatomicX(1);
// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("mem_peek_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_REG;
rw.kind = UVM_READ;
rw.bd_kind = kind;
rw.parent = parent;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
status = rw.status;
value = rw.value[0];
`uvm_info("RegModel", $sformatf("Peeked register \"%s\": 'h%h",
get_full_name(), value),UVM_HIGH);
do_predict(rw, UVM_PREDICT_READ);
if (!m_is_locked_by_field)
XatomicX(0);
endtask: peek
// do_check
function bit uvm_reg::do_check(input uvm_reg_data_t expected,
input uvm_reg_data_t actual,
uvm_reg_map map);
uvm_reg_data_t dc = 0;
foreach(m_fields[i]) begin
string acc = m_fields[i].get_access(map);
acc = acc.substr(0, 1);
if (m_fields[i].get_compare() == UVM_NO_CHECK ||
acc == "WO") begin
dc |= ((1 << m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos();
end
end
if ((actual|dc) === (expected|dc)) return 1;
`uvm_error("RegModel", $sformatf("Register \"%s\" value read from DUT (0x%h) does not match mirrored value (0x%h)",
get_full_name(), actual, (expected ^ ('x & dc))));
foreach(m_fields[i]) begin
string acc = m_fields[i].get_access(map);
acc = acc.substr(0, 1);
if (!(m_fields[i].get_compare() == UVM_NO_CHECK ||
acc == "WO")) begin
uvm_reg_data_t mask = ((1 << m_fields[i].get_n_bits())-1);
uvm_reg_data_t val = actual >> m_fields[i].get_lsb_pos() & mask;
uvm_reg_data_t exp = expected >> m_fields[i].get_lsb_pos() & mask;
if (val !== exp) begin
`uvm_info("RegModel",
$sformatf("Field %s (%s[%0d:%0d]) mismatch read=%0d'h%0h mirrored=%0d'h%0h ",
m_fields[i].get_name(), get_full_name(),
m_fields[i].get_lsb_pos() + m_fields[i].get_n_bits() - 1,
m_fields[i].get_lsb_pos(),
m_fields[i].get_n_bits(), val,
m_fields[i].get_n_bits(), exp),
UVM_NONE)
end
end
end
return 0;
endfunction
// mirror
task uvm_reg::mirror(output uvm_status_e status,
input uvm_check_e check = UVM_NO_CHECK,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_data_t v;
uvm_reg_data_t exp;
uvm_reg_backdoor bkdr = get_backdoor();
XatomicX(1);
m_fname = fname;
m_lineno = lineno;
if (path == UVM_DEFAULT_PATH)
path = m_parent.get_default_path();
if (path == UVM_BACKDOOR && (bkdr != null || has_hdl_path()))
map = uvm_reg_map::backdoor();
else
map = get_local_map(map, "read()");
if (map == null)
return;
// Remember what we think the value is before it gets updated
if (check == UVM_CHECK)
exp = get_mirrored_value();
XreadX(status, v, path, map, parent, prior, extension, fname, lineno);
if (status == UVM_NOT_OK) begin
XatomicX(0);
return;
end
if (check == UVM_CHECK) void'(do_check(exp, v, map));
XatomicX(0);
endtask: mirror
// XatomicX
task uvm_reg::XatomicX(bit on);
process m_reg_process;
m_reg_process=process::self();
if (on) begin
if (m_reg_process == m_process)
return;
m_atomic.get(1);
m_process = m_reg_process;
end
else begin
// Maybe a key was put back in by a spurious call to reset()
void'(m_atomic.try_get(1));
m_atomic.put(1);
m_process = null;
end
endtask: XatomicX
//-------------
// STANDARD OPS
//-------------
// convert2string
function string uvm_reg::convert2string();
string res_str;
string t_str;
bit with_debug_info;
string prefix;
$sformat(convert2string, "Register %s -- %0d bytes, mirror value:'h%h",
get_full_name(), get_n_bytes(),get());
if (m_maps.num()==0)
convert2string = {convert2string, " (unmapped)\n"};
else
convert2string = {convert2string, "\n"};
foreach (m_maps[map]) begin
uvm_reg_map parent_map = map;
int unsigned offset;
while (parent_map != null) begin
uvm_reg_map this_map = parent_map;
parent_map = this_map.get_parent_map();
offset = parent_map == null ? this_map.get_base_addr(UVM_NO_HIER) :
parent_map.get_submap_offset(this_map);
prefix = {prefix, " "};
begin
uvm_endianness_e e = this_map.get_endian();
$sformat(convert2string,
"%sMapped in '%s' -- %d bytes, %s, offset 'h%0h\n",
prefix, this_map.get_full_name(), this_map.get_n_bytes(),
e.name(), offset);
end
end
end
prefix = " ";
foreach(m_fields[i]) begin
$sformat(convert2string, "%s\n%s", convert2string,
m_fields[i].convert2string());
end
if (m_read_in_progress == 1'b1) begin
if (m_fname != "" && m_lineno != 0)
$sformat(res_str, "%s:%0d ",m_fname, m_lineno);
convert2string = {convert2string, "\n", res_str,
"currently executing read method"};
end
if ( m_write_in_progress == 1'b1) begin
if (m_fname != "" && m_lineno != 0)
$sformat(res_str, "%s:%0d ",m_fname, m_lineno);
convert2string = {convert2string, "\n", res_str,
"currently executing write method"};
end
endfunction: convert2string
// do_print
function void uvm_reg::do_print (uvm_printer printer);
uvm_reg_field f[$];
super.do_print(printer);
get_fields(f);
foreach(f[i]) printer.print_generic(f[i].get_name(),f[i].get_type_name(),-2,f[i].convert2string());
endfunction
// clone
function uvm_object uvm_reg::clone();
`uvm_fatal("RegModel","RegModel registers cannot be cloned")
return null;
endfunction
// do_copy
function void uvm_reg::do_copy(uvm_object rhs);
`uvm_fatal("RegModel","RegModel registers cannot be copied")
endfunction
// do_compare
function bit uvm_reg::do_compare (uvm_object rhs,
uvm_comparer comparer);
`uvm_warning("RegModel","RegModel registers cannot be compared")
return 0;
endfunction
// do_pack
function void uvm_reg::do_pack (uvm_packer packer);
`uvm_warning("RegModel","RegModel registers cannot be packed")
endfunction
// do_unpack
function void uvm_reg::do_unpack (uvm_packer packer);
`uvm_warning("RegModel","RegModel registers cannot be unpacked")
endfunction