//
// -------------------------------------------------------------
// 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.
// -------------------------------------------------------------
//------------------------------------------------------------------------------
// CLASS: uvm_mem
//------------------------------------------------------------------------------
// Memory abstraction base class
//
// A memory is a collection of contiguous locations.
// A memory may be accessible via more than one address map.
//
// Unlike registers, memories are not mirrored because of the potentially
// large data space: tests that walk the entire memory space would negate
// any benefit from sparse memory modelling techniques.
// Rather than relying on a mirror, it is recommended that
// backdoor access be used instead.
//
//------------------------------------------------------------------------------
class [docs]uvm_mem extends uvm_object;
typedef enum {UNKNOWNS, ZEROES, ONES, ADDRESS, VALUE, INCR, DECR} [docs]init_e;
local bit m_locked;
local bit m_read_in_progress;
local bit m_write_in_progress;
local string m_access;
local longint unsigned m_size;
local uvm_reg_block m_parent;
local bit m_maps[uvm_reg_map];
local int unsigned m_n_bits;
local uvm_reg_backdoor m_backdoor;
local bit m_is_powered_down;
local int m_has_cover;
local int m_cover_on;
local string m_fname;
local int m_lineno;
local bit m_vregs[uvm_vreg];
local uvm_object_string_pool
#(uvm_queue #(uvm_hdl_path_concat)) m_hdl_paths_pool;
local static int unsigned m_max_size;
//----------------------
// Group: Initialization
//----------------------
// Function: new
//
// Create a new instance and type-specific configuration
//
// Creates an instance of a memory abstraction class with the specified
// name.
//
// ~size~ specifies the total number of memory locations.
// ~n_bits~ specifies the total number of bits in each memory location.
// ~access~ specifies the access policy of this memory and may be
// one of "RW for RAMs and "RO" for ROMs.
//
// ~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,
longint unsigned size,
int unsigned n_bits,
string access = "RW",
int has_coverage = UVM_NO_COVERAGE);
// Function: configure
//
// Instance-specific configuration
//
// Specify the parent block of this memory.
//
// If this memory is implemented in a single HDL variable,
// its name is specified as the ~hdl_path~.
// Otherwise, if the memory is implemented as a concatenation
// of variables (usually one per bank), 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 parent,
string hdl_path = "");
// Function: set_offset
//
// Modify the offset of the memory
//
// The offset of a memory within an address map is set using the
// <uvm_reg_map::add_mem()> method.
// This method is used to modify that offset dynamically.
//
// Note: Modifying the offset of a memory will make the abstract 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 parent);
/*local*/ extern function void [docs]add_map(uvm_reg_map map);
/*local*/ extern function void [docs]Xlock_modelX();
/*local*/ extern function void [docs]Xadd_vregX(uvm_vreg vreg);
/*local*/ extern function void [docs]Xdelete_vregX(uvm_vreg vreg);
// variable: mam
//
// Memory allocation manager
//
// Memory allocation manager for the memory corresponding to this
// abstraction class instance.
// Can be used to allocate regions of consecutive addresses of
// specific sizes, such as DMA buffers,
// or to locate virtual register array.
//
uvm_mem_mam mam;
//---------------------
// Group: Introspection
//---------------------
// Function: get_name
//
// Get the simple name
//
// Return the simple object name of this memory.
//
// Function: get_full_name
//
// Get the hierarchical name
//
// Return the hierarchal name of this memory.
// 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_n_maps
//
// Returns the number of address maps this memory is mapped in
//
extern virtual function int [docs]get_n_maps ();
// Function: is_in_map
//
// Return TRUE if this memory 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 memory is mapped
//
extern virtual function void [docs]get_maps (ref uvm_reg_map maps[$]);
/*local*/ extern function uvm_reg_map [docs]get_local_map (uvm_reg_map map,
string caller = "");
/*local*/ extern function uvm_reg_map [docs]get_default_map (string caller = "");
// Function: get_rights
//
// Returns the access rights of this memory.
//
// Returns "RW", "RO" or "WO".
// The access rights of a memory is always "RW",
// unless it is a shared memory
// with access restriction in a particular address map.
//
// If no address map is specified and the memory is mapped in only one
// address map, that address map is used. If the memory 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 memory 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_access
//
// Returns the access policy of the memory when written and read
// via an address map.
//
// If the memory is mapped in more than one address map,
// an address ~map~ must be specified.
// If access restrictions are present when accessing a memory
// through the specified address map, the access mode returned
// takes the access restrictions into account.
// For example, a read-write memory accessed
// through a domain with read-only restrictions would return "RO".
//
extern virtual function string [docs]get_access(uvm_reg_map map = null);
// Function: get_size
//
// Returns the number of unique memory locations in this memory.
//
extern function longint unsigned [docs]get_size();
// Function: get_n_bytes
//
// Return the width, in number of bytes, of each memory location
//
extern function int unsigned [docs]get_n_bytes();
// Function: get_n_bits
//
// Returns the width, in number of bits, of each memory location
//
extern function int unsigned [docs]get_n_bits();
// Function: get_max_size
//
// Returns the maximum width, in number of bits, of all memories
//
extern static function int unsigned [docs]get_max_size();
// Function: get_virtual_registers
//
// Return the virtual registers in this memory
//
// Fills the specified array with the abstraction class
// for all of the virtual registers implemented in this memory.
// The order in which the virtual registers are located in the array
// is not specified.
//
extern virtual function void [docs]get_virtual_registers(ref uvm_vreg regs[$]);
// Function: get_virtual_fields
//
// Return the virtual fields in the memory
//
// Fills the specified dynamic array with the abstraction class
// for all of the virtual fields implemented in this memory.
// The order in which the virtual fields are located in the array is
// not specified.
//
extern virtual function void [docs]get_virtual_fields(ref uvm_vreg_field fields[$]);
// Function: get_vreg_by_name
//
// Find the named virtual register
//
// Finds a virtual register with the specified name
// implemented in this memory and returns
// its abstraction class instance.
// If no virtual register with the specified name is found, returns ~null~.
//
extern virtual function uvm_vreg [docs]get_vreg_by_name(string name);
// Function: get_vfield_by_name
//
// Find the named virtual field
//
// Finds a virtual field with the specified name
// implemented in this memory and returns
// its abstraction class instance.
// If no virtual field with the specified name is found, returns ~null~.
//
extern virtual function uvm_vreg_field [docs]get_vfield_by_name(string name);
// Function: get_vreg_by_offset
//
// Find the virtual register implemented at the specified offset
//
// Finds the virtual register implemented in this memory
// at the specified ~offset~ in the specified address ~map~
// and returns its abstraction class instance.
// If no virtual register at the offset is found, returns ~null~.
//
extern virtual function uvm_vreg [docs]get_vreg_by_offset(uvm_reg_addr_t offset,
uvm_reg_map map = null);
// Function: get_offset
//
// Returns the base offset of a memory location
//
// Returns the base offset of the specified location in this memory
// in an address ~map~.
//
// If no address map is specified and the memory is mapped in only one
// address map, that address map is used. If the memory 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 memory 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_addr_t offset = 0,
uvm_reg_map map = null);
// Function: get_address
//
// Returns the base external physical address of a memory location
//
// Returns the base external physical address of the specified location
// in this memory if accessed through the specified address ~map~.
//
// If no address map is specified and the memory is mapped in only one
// address map, that address map is used. If the memory 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 memory 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_addr_t offset = 0,
uvm_reg_map map = null);
// Function: get_addresses
//
// Identifies the external physical address(es) of a memory location
//
// Computes all of the external physical addresses that must be accessed
// to completely read or write the specified location in this memory.
// 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 memory is mapped in only one
// address map, that address map is used. If the memory 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 memory is not mapped in the specified
// address map, an error message is issued.
//
extern virtual function int [docs]get_addresses(uvm_reg_addr_t offset = 0,
uvm_reg_map map=null,
ref uvm_reg_addr_t addr[]);
//------------------
// Group: HDL Access
//------------------
// Task: write
//
// Write the specified value in a memory location
//
// Write ~value~ in the memory location that corresponds to this
// abstraction class instance at the specified ~offset~
// using the specified access ~path~.
// If the memory 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, a read-only memory will not be written.
//
extern virtual task [docs]write(output uvm_status_e status,
input uvm_reg_addr_t offset,
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 a memory location
//
// Read and return ~value~ from the memory location that corresponds to this
// abstraction class instance at the specified ~offset~
// 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).
//
extern virtual task [docs]read(output uvm_status_e status,
input uvm_reg_addr_t offset,
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: burst_write
//
// Write the specified values in memory locations
//
// Burst-write the specified ~values~ in the memory locations
// beginning at the specified ~offset~.
// If the memory is mapped in more than one address map,
// an address ~map~ must be specified if not using the backdoor.
// If a back-door access path is used, the effect of writing
// the register through a physical access is mimicked. For
// example, a read-only memory will not be written.
//
extern virtual task [docs]burst_write(output uvm_status_e status,
input uvm_reg_addr_t offset,
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: burst_read
//
// Read values from memory locations
//
// Burst-read into ~values~ the data the memory locations
// beginning at the specified ~offset~.
// If the memory is mapped in more than one address map,
// an address ~map~ must be specified if not using the backdoor.
// If a back-door access path is used, the effect of writing
// the register through a physical access is mimicked. For
// example, a read-only memory will not be written.
//
extern virtual task [docs]burst_read(output uvm_status_e status,
input uvm_reg_addr_t offset,
ref 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 a memory location
//
// Deposit the value in the DUT memory location corresponding to this
// abstraction class instance at the specified ~offset~, as-is,
// using a back-door access.
//
// Uses the HDL path for the design abstraction specified by ~kind~.
//
extern virtual task [docs]poke(output uvm_status_e status,
input uvm_reg_addr_t offset,
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 a memory location
//
// Sample the value in the DUT memory location corresponding to this
// abstraction class instance at the specified ~offset~
// using a back-door access.
// The memory location value is sampled, not modified.
//
// Uses the HDL path for the design abstraction specified by ~kind~.
//
extern virtual task [docs]peek(output uvm_status_e status,
input uvm_reg_addr_t offset,
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);
extern protected function bit Xcheck_accessX (input uvm_reg_item rw,
output uvm_reg_map_info map_info,
input string caller);
extern virtual task [docs]do_write (uvm_reg_item rw);
extern virtual task [docs]do_read (uvm_reg_item rw);
//-----------------
// Group: Frontdoor
//-----------------
// Function: set_frontdoor
//
// Set a user-defined frontdoor for this memory
//
// By default, memories are mapped linearly into the address space
// of the address maps that instantiate them.
// If memories are accessed using a different mechanism,
// a user-defined access
// mechanism must be defined and associated with
// the corresponding memory abstraction class
//
// If the memory 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 memory
//
// If ~null~, no user-defined frontdoor has been defined.
// A user-defined frontdoor is defined
// by using the <uvm_mem::set_frontdoor()> method.
//
// If the memory 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 memory
//
// By default, memories are accessed via the built-in string-based
// DPI routines if an HDL path has been specified using the
// <uvm_mem::configure()> or <uvm_mem::add_hdl_path()> method.
// If this default mechanism is not suitable (e.g. because
// the memory is not implemented in pure SystemVerilog)
// a user-defined access
// mechanism must be defined and associated with
// the corresponding memory abstraction class
//
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 memory
//
// If ~null~, no user-defined backdoor has been defined.
// A user-defined backdoor is defined
// by using the <uvm_reg::set_backdoor()> method.
//
// If ~inherit~ is TRUE, returns the backdoor of the parent block
// if none have been specified for this memory.
//
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 memory 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 memory instance for the specified
// design abstraction. This method may be called more than once for the
// same design abstraction if the memory is physically duplicated
// in the design abstraction
//
extern function void [docs]add_hdl_path (uvm_hdl_path_slice slices[],
string kind = "RTL");
// Function: add_hdl_path_slice
//
// Add the specified HDL slice to the HDL path for the specified
// design abstraction.
// If ~first~ is TRUE, starts the specification of a duplicate
// HDL implementation of the memory.
//
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 memory 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 memory instance.
// Returns only the component of the HDL paths that corresponds to
// the memory, 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_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 memory instance.
// There may be more than one path returned even
// if only one path was defined for the memory 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: 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: backdoor_read
//
// User-define backdoor read access
//
// Override the default string-based DPI backdoor access read
// for this memory type.
// By default calls <uvm_mem::backdoor_read_func()>.
//
extern virtual protected task 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 memory 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 memory type.
//
extern virtual function uvm_status_e [docs]backdoor_read_func(uvm_reg_item rw);
//-----------------
// Group: Callbacks
//-----------------
`uvm_register_cb(uvm_mem, uvm_reg_cbs)
// Task: pre_write
//
// Called before memory write.
//
// If the ~offset~, ~value~, access ~path~,
// or address ~map~ are modified, the updated offset, data value,
// access path or address map will be used to perform the memory 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.
//
virtual task [docs]pre_write(uvm_reg_item rw); endtask
// Task: post_write
//
// Called after memory write.
//
// If the ~status~ is modified, the updated status will be
// returned by the memory operation.
//
// The registered callback methods are invoked before the invocation
// of this method.
//
virtual task [docs]post_write(uvm_reg_item rw); endtask
// Task: pre_read
//
// Called before memory read.
//
// If the ~offset~, access ~path~ or address ~map~ are modified,
// the updated offset, access path or address map will be used to perform
// the memory 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.
//
virtual task [docs]pre_read(uvm_reg_item rw); endtask
// Task: post_read
//
// Called after memory read.
//
// If the readback data or ~status~ is modified,
// the updated readback //data or status will be
// returned by the memory operation.
//
// The registered callback methods are invoked before the invocation
// of this method.
//
virtual task [docs]post_read(uvm_reg_item rw); endtask
//----------------
// Group: Coverage
//----------------
// Function: build_coverage
//
// Check if all of the specified coverage model must be built.
//
// Check which of the specified coverage model must be built
// in this instance of the memory 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
// memory 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 memory has coverage model(s)
//
// Returns TRUE if the memory 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 memory.
// 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 memory abstraction classes,
// then enabled during construction.
// See the <uvm_mem::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_mem::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 memory abstraction class
// whenever an address within one of its address map
// is successfully read or written.
// The specified offset is the offset within the memory,
// not an absolute address.
//
// 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_addr_t offset,
bit is_read,
uvm_reg_map map);
endfunction
/*local*/ function void [docs]XsampleX(uvm_reg_addr_t addr,
bit is_read,
uvm_reg_map map);
sample(addr, is_read, map);
endfunction
// Core ovm_object operations
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_mem
//------------------------------------------------------------------------------
// IMPLEMENTATION
//------------------------------------------------------------------------------
// new
function uvm_mem::new (string name,
longint unsigned size,
int unsigned n_bits,
string access = "RW",
int has_coverage = UVM_NO_COVERAGE);
super.new(name);
m_locked = 0;
if (n_bits == 0) begin
`uvm_error("RegModel", {"Memory '",get_full_name(),"' cannot have 0 bits"})
n_bits = 1;
end
m_size = size;
m_n_bits = n_bits;
m_backdoor = null;
m_access = access.toupper();
m_has_cover = has_coverage;
m_hdl_paths_pool = new("hdl_paths");
if (n_bits > m_max_size)
m_max_size = n_bits;
endfunction: new
// configure
function void uvm_mem::configure(uvm_reg_block parent,
string hdl_path="");
if (parent == null)
`uvm_fatal("REG/NULL_PARENT","configure: parent argument is null")
m_parent = parent;
if (m_access != "RW" && m_access != "RO") begin
`uvm_error("RegModel", {"Memory '",get_full_name(),"' can only be RW or RO"})
m_access = "RW";
end
begin
uvm_mem_mam_cfg cfg = new;
cfg.n_bytes = ((m_n_bits-1) / 8) + 1;
cfg.start_offset = 0;
cfg.end_offset = m_size-1;
cfg.mode = uvm_mem_mam::GREEDY;
cfg.locality = uvm_mem_mam::BROAD;
mam = new(get_full_name(), cfg, this);
end
m_parent.add_mem(this);
if (hdl_path != "") add_hdl_path_slice(hdl_path, -1, -1);
endfunction: configure
// set_offset
function void uvm_mem::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 memory '",
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_mem_offset(this, offset, unmapped);
endfunction
// add_map
function void uvm_mem::add_map(uvm_reg_map map);
m_maps[map] = 1;
endfunction
// Xlock_modelX
function void uvm_mem::Xlock_modelX();
m_locked = 1;
endfunction: Xlock_modelX
// get_full_name
function string uvm_mem::get_full_name();
if (m_parent == null)
return get_name();
return {m_parent.get_full_name(), ".", get_name()};
endfunction: get_full_name
// get_block
function uvm_reg_block uvm_mem::get_block();
return m_parent;
endfunction: get_block
// get_n_maps
function int uvm_mem::get_n_maps();
return m_maps.num();
endfunction: get_n_maps
// get_maps
function void uvm_mem::get_maps(ref uvm_reg_map maps[$]);
foreach (m_maps[map])
maps.push_back(map);
endfunction
// is_in_map
function bit uvm_mem::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_mem::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",
{"Memory '",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_mem::get_default_map(string caller="");
// if mem is not associated with any may, return ~null~
if (m_maps.num() == 0) begin
`uvm_warning("RegModel",
{"Memory '",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
void'(m_maps.first(get_default_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);
if (local_map != null)
return local_map;
end
end
// if that fails, choose the first in this mem's maps
void'(m_maps.first(get_default_map));
endfunction
// get_access
function string uvm_mem::get_access(uvm_reg_map map = null);
get_access = m_access;
if (get_n_maps() == 1) return get_access;
map = get_local_map(map, "get_access()");
if (map == null) return get_access;
// Is the memory restricted in this map?
case (get_rights(map))
"RW":
// No restrictions
return get_access;
"RO":
case (get_access)
"RW", "RO": get_access = "RO";
"WO": `uvm_error("RegModel", {"WO memory '",get_full_name(),
"' restricted to RO in map '",map.get_full_name(),"'"})
default: `uvm_error("RegModel", {"Memory '",get_full_name(),
"' has invalid access mode, '",get_access,"'"})
endcase
"WO":
case (get_access)
"RW", "WO": get_access = "WO";
"RO": `uvm_error("RegModel", {"RO memory '",get_full_name(),
"' restricted to WO in map '",map.get_full_name(),"'"})
default: `uvm_error("RegModel", {"Memory '",get_full_name(),
"' has invalid access mode, '",get_access,"'"})
endcase
default: `uvm_error("RegModel", {"Shared memory '",get_full_name(),
"' is not shared in map '",map.get_full_name(),"'"})
endcase
endfunction: get_access
// get_rights
function string uvm_mem::get_rights(uvm_reg_map map = null);
uvm_reg_map_info info;
// No right restrictions if not shared
if (m_maps.num() <= 1) begin
return "RW";
end
map = get_local_map(map,"get_rights()");
if (map == null)
return "RW";
info = map.get_mem_map_info(this);
return info.rights;
endfunction: get_rights
// get_offset
function uvm_reg_addr_t uvm_mem::get_offset(uvm_reg_addr_t offset = 0,
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_mem_map_info(this);
if (map_info.unmapped) begin
`uvm_warning("RegModel", {"Memory '",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_offset
// get_virtual_registers
function void uvm_mem::get_virtual_registers(ref uvm_vreg regs[$]);
foreach (m_vregs[vreg])
regs.push_back(vreg);
endfunction
// get_virtual_fields
function void uvm_mem::get_virtual_fields(ref uvm_vreg_field fields[$]);
foreach (m_vregs[l])
begin
uvm_vreg vreg = l;
vreg.get_fields(fields);
end
endfunction: get_virtual_fields
// get_vfield_by_name
function uvm_vreg_field uvm_mem::get_vfield_by_name(string name);
// Return first occurrence of vfield matching name
uvm_vreg_field vfields[$];
get_virtual_fields(vfields);
foreach (vfields[i])
if (vfields[i].get_name() == name)
return vfields[i];
`uvm_warning("RegModel", {"Unable to find virtual field '",name,
"' in memory '",get_full_name(),"'"})
return null;
endfunction: get_vfield_by_name
// get_vreg_by_name
function uvm_vreg uvm_mem::get_vreg_by_name(string name);
foreach (m_vregs[l])
begin
uvm_vreg vreg = l;
if (vreg.get_name() == name)
return vreg;
end
`uvm_warning("RegModel", {"Unable to find virtual register '",name,
"' in memory '",get_full_name(),"'"})
return null;
endfunction: get_vreg_by_name
// get_vreg_by_offset
function uvm_vreg uvm_mem::get_vreg_by_offset(uvm_reg_addr_t offset,
uvm_reg_map map = null);
`uvm_error("RegModel", "uvm_mem::get_vreg_by_offset() not yet implemented")
return null;
endfunction: get_vreg_by_offset
// get_addresses
function int uvm_mem::get_addresses(uvm_reg_addr_t offset = 0,
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 0;
map_info = map.get_mem_map_info(this);
if (map_info.unmapped) begin
`uvm_warning("RegModel", {"Memory '",get_name(),
"' is unmapped in map '",
((orig_map == null) ? map.get_full_name() : orig_map.get_full_name()),"'"})
return 0;
end
addr = map_info.addr;
foreach (addr[i])
addr[i] = addr[i] + map_info.mem_range.stride * offset;
return map.get_n_bytes();
endfunction
// get_address
function uvm_reg_addr_t uvm_mem::get_address(uvm_reg_addr_t offset = 0,
uvm_reg_map map = null);
uvm_reg_addr_t addr[];
void'(get_addresses(offset, map, addr));
return addr[0];
endfunction
// get_size
function longint unsigned uvm_mem::get_size();
return m_size;
endfunction: get_size
// get_n_bits
function int unsigned uvm_mem::get_n_bits();
return m_n_bits;
endfunction: get_n_bits
// get_max_size
function int unsigned uvm_mem::get_max_size();
return m_max_size;
endfunction: get_max_size
// get_n_bytes
function int unsigned uvm_mem::get_n_bytes();
return (m_n_bits - 1) / 8 + 1;
endfunction: get_n_bytes
//---------
// COVERAGE
//---------
function uvm_reg_cvr_t uvm_mem::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_mem::add_coverage(uvm_reg_cvr_t models);
m_has_cover |= models;
endfunction: add_coverage
// has_coverage
function bit uvm_mem::has_coverage(uvm_reg_cvr_t models);
return ((m_has_cover & models) == models);
endfunction: has_coverage
// set_coverage
function uvm_reg_cvr_t uvm_mem::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_mem::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
//-----------
// HDL ACCESS
//-----------
// write
//------
task uvm_mem::write(output uvm_status_e status,
input uvm_reg_addr_t offset,
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 = uvm_reg_item::type_id::create("mem_write",,get_full_name());
rw.element = this;
rw.element_kind = UVM_MEM;
rw.kind = UVM_WRITE;
rw.offset = offset;
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;
endtask: write
// read
task uvm_mem::read(output uvm_status_e status,
input uvm_reg_addr_t offset,
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);
uvm_reg_item rw;
rw = uvm_reg_item::type_id::create("mem_read",,get_full_name());
rw.element = this;
rw.element_kind = UVM_MEM;
rw.kind = UVM_READ;
rw.value[0] = 0;
rw.offset = offset;
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: read
// burst_write
task uvm_mem::burst_write(output uvm_status_e status,
input uvm_reg_addr_t offset,
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);
uvm_reg_item rw;
rw = uvm_reg_item::type_id::create("mem_burst_write",,get_full_name());
rw.element = this;
rw.element_kind = UVM_MEM;
rw.kind = UVM_BURST_WRITE;
rw.offset = offset;
rw.value = 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;
endtask: burst_write
// burst_read
task uvm_mem::burst_read(output uvm_status_e status,
input uvm_reg_addr_t offset,
ref 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);
uvm_reg_item rw;
rw = uvm_reg_item::type_id::create("mem_burst_read",,get_full_name());
rw.element = this;
rw.element_kind = UVM_MEM;
rw.kind = UVM_BURST_READ;
rw.offset = offset;
rw.value = value;
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;
endtask: burst_read
// do_write
task uvm_mem::do_write(uvm_reg_item rw);
uvm_mem_cb_iter cbs = new(this);
uvm_reg_map_info map_info;
m_fname = rw.fname;
m_lineno = rw.lineno;
if (!Xcheck_accessX(rw, map_info, "burst_write()"))
return;
m_write_in_progress = 1'b1;
rw.status = UVM_IS_OK;
// PRE-WRITE CBS
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;
return;
end
rw.status = UVM_NOT_OK;
// FRONTDOOR
if (rw.path == UVM_FRONTDOOR) begin
uvm_reg_map system_map = rw.local_map.get_root_map();
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
else begin
rw.local_map.do_write(rw);
end
if (rw.status != UVM_NOT_OK)
for (uvm_reg_addr_t idx = rw.offset;
idx <= rw.offset + rw.value.size();
idx++) begin
XsampleX(map_info.mem_range.stride * idx, 0, rw.map);
m_parent.XsampleX(map_info.offset +
(map_info.mem_range.stride * idx),
0, rw.map);
end
end
// BACKDOOR
else begin
// Mimick front door access, i.e. do not write read-only memories
if (get_access(rw.map) == "RW") begin
uvm_reg_backdoor bkdr = get_backdoor();
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
end
else
rw.status = UVM_IS_OK;
end
// POST-WRITE CBS
post_write(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_write(rw);
// REPORT
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "RegModel")) begin
string path_s,value_s,pre_s,range_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";
if (rw.value.size() > 1) begin
value_s = "='{";
pre_s = "Burst ";
foreach (rw.value[i])
value_s = {value_s,$sformatf("%0h,",rw.value[i])};
value_s[value_s.len()-1]="}";
range_s = $sformatf("[%0d:%0d]",rw.offset,rw.offset+rw.value.size());
end
else begin
value_s = $sformatf("=%0h",rw.value[0]);
range_s = $sformatf("[%0d]",rw.offset);
end
uvm_report_info("RegModel", {pre_s,"Wrote memory via ",path_s,": ",
get_full_name(),range_s,value_s}, UVM_HIGH);
end
m_write_in_progress = 1'b0;
endtask: do_write
// do_read
task uvm_mem::do_read(uvm_reg_item rw);
uvm_mem_cb_iter cbs = new(this);
uvm_reg_map_info map_info;
m_fname = rw.fname;
m_lineno = rw.lineno;
if (!Xcheck_accessX(rw, map_info, "burst_read()"))
return;
m_read_in_progress = 1'b1;
rw.status = UVM_IS_OK;
// PRE-READ CBS
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
rw.status = UVM_NOT_OK;
// FRONTDOOR
if (rw.path == UVM_FRONTDOOR) begin
uvm_reg_map system_map = rw.local_map.get_root_map();
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
else begin
rw.local_map.do_read(rw);
end
if (rw.status != UVM_NOT_OK)
for (uvm_reg_addr_t idx = rw.offset;
idx <= rw.offset + rw.value.size();
idx++) begin
XsampleX(map_info.mem_range.stride * idx, 1, rw.map);
m_parent.XsampleX(map_info.offset +
(map_info.mem_range.stride * idx),
1, rw.map);
end
end
// BACKDOOR
else begin
uvm_reg_backdoor bkdr = get_backdoor();
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
end
// POST-READ CBS
post_read(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_read(rw);
// REPORT
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "RegModel")) begin
string path_s,value_s,pre_s,range_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";
if (rw.value.size() > 1) begin
value_s = "='{";
pre_s = "Burst ";
foreach (rw.value[i])
value_s = {value_s,$sformatf("%0h,",rw.value[i])};
value_s[value_s.len()-1]="}";
range_s = $sformatf("[%0d:%0d]",rw.offset,(rw.offset+rw.value.size()));
end
else begin
value_s = $sformatf("=%0h",rw.value[0]);
range_s = $sformatf("[%0d]",rw.offset);
end
uvm_report_info("RegModel", {pre_s,"Read memory via ",path_s,": ",
get_full_name(),range_s,value_s}, UVM_HIGH);
end
m_read_in_progress = 1'b0;
endtask: do_read
// Xcheck_accessX
function bit uvm_mem::Xcheck_accessX(input uvm_reg_item rw,
output uvm_reg_map_info map_info,
input string caller);
if (rw.offset >= m_size) begin
`uvm_error(get_type_name(),
$sformatf("Offset 'h%0h exceeds size of memory, 'h%0h",
rw.offset, m_size))
rw.status = UVM_NOT_OK;
return 0;
end
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 memory '",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 memory from map '",
rw.map.get_full_name(),"'"})
rw.status = UVM_NOT_OK;
return 0;
end
map_info = rw.local_map.get_mem_map_info(this);
if (map_info.frontdoor == null) begin
if (map_info.unmapped) begin
`uvm_error("RegModel", {"Memory '",get_full_name(),
"' unmapped in map '", rw.map.get_full_name(),
"' and does not have a user-defined frontdoor"})
rw.status = UVM_NOT_OK;
return 0;
end
if ((rw.value.size() > 1)) begin
if (get_n_bits() > rw.local_map.get_n_bytes()*8) begin
`uvm_error("RegModel",
$sformatf("Cannot burst a %0d-bit memory through a narrower data path (%0d bytes)",
get_n_bits(), rw.local_map.get_n_bytes()*8));
rw.status = UVM_NOT_OK;
return 0;
end
if (rw.offset + rw.value.size() > m_size) begin
`uvm_error("RegModel",
$sformatf("Burst of size 'd%0d starting at offset 'd%0d exceeds size of memory, 'd%0d",
rw.value.size(), rw.offset, m_size))
return 0;
end
end
end
if (rw.map == null)
rw.map = rw.local_map;
end
return 1;
endfunction
//-------
// ACCESS
//-------
// poke
task uvm_mem::poke(output uvm_status_e status,
input uvm_reg_addr_t offset,
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_item rw;
uvm_reg_backdoor bkdr = get_backdoor();
m_fname = fname;
m_lineno = lineno;
if (bkdr == null && !has_hdl_path(kind)) begin
`uvm_error("RegModel", {"No backdoor access available in memory '",
get_full_name(),"'"})
status = UVM_NOT_OK;
return;
end
// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("mem_poke_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_MEM;
rw.kind = UVM_WRITE;
rw.offset = offset;
rw.value[0] = value & ((1 << m_n_bits)-1);
rw.bd_kind = kind;
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 memory '%s[%0d]' with value 'h%h",
get_full_name(), offset, value),UVM_HIGH);
endtask: poke
// peek
task uvm_mem::peek(output uvm_status_e status,
input uvm_reg_addr_t offset,
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", {"No backdoor access available in memory '",
get_full_name(),"'"})
status = UVM_NOT_OK;
return;
end
// 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_MEM;
rw.kind = UVM_READ;
rw.offset = offset;
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 memory '%s[%0d]' has value 'h%h",
get_full_name(), offset, value),UVM_HIGH);
endtask: peek
//-----------------
// Group- Frontdoor
//-----------------
// set_frontdoor
function void uvm_mem::set_frontdoor(uvm_reg_frontdoor ftdr,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
uvm_reg_map_info map_info;
m_fname = fname;
m_lineno = lineno;
map = get_local_map(map, "set_frontdoor()");
if (map == null) begin
`uvm_error("RegModel", {"Memory '",get_full_name(),
"' not found in map '", map.get_full_name(),"'"})
return;
end
map_info = map.get_mem_map_info(this);
map_info.frontdoor = ftdr;
endfunction: set_frontdoor
// get_frontdoor
function uvm_reg_frontdoor uvm_mem::get_frontdoor(uvm_reg_map map = null);
uvm_reg_map_info map_info;
map = get_local_map(map, "set_frontdoor()");
if (map == null) begin
`uvm_error("RegModel", {"Memory '",get_full_name(),
"' not found in map '", map.get_full_name(),"'"})
return null;
end
map_info = map.get_mem_map_info(this);
return map_info.frontdoor;
endfunction: get_frontdoor
//----------------
// Group- Backdoor
//----------------
// set_backdoor
function void uvm_mem::set_backdoor(uvm_reg_backdoor bkdr,
string fname = "",
int lineno = 0);
m_fname = fname;
m_lineno = lineno;
m_backdoor = bkdr;
endfunction: set_backdoor
// get_backdoor
function uvm_reg_backdoor uvm_mem::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
// backdoor_read_func
function uvm_status_e uvm_mem::backdoor_read_func(uvm_reg_item rw);
uvm_hdl_path_concat paths[$];
uvm_hdl_data_t val;
bit ok=1;
get_full_hdl_path(paths,rw.bd_kind);
foreach (rw.value[mem_idx]) begin
string idx;
idx.itoa(rw.offset + mem_idx);
foreach (paths[i]) begin
uvm_hdl_path_concat hdl_concat = paths[i];
val = 0;
foreach (hdl_concat.slices[j]) begin
string hdl_path = {hdl_concat.slices[j].path, "[", idx, "]"};
`uvm_info("RegModel", {"backdoor_read from ",hdl_path},UVM_DEBUG)
if (hdl_concat.slices[j].offset < 0) begin
ok &= uvm_hdl_read(hdl_path, val);
continue;
end
begin
uvm_reg_data_t slice;
int k = hdl_concat.slices[j].offset;
ok &= uvm_hdl_read(hdl_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[mem_idx] = val;
if (val != rw.value[mem_idx]) 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[mem_idx], uvm_hdl_concat2string(paths[0]),
val, uvm_hdl_concat2string(paths[i])));
return UVM_NOT_OK;
end
end
end
rw.status = (ok) ? UVM_IS_OK : UVM_NOT_OK;
return rw.status;
endfunction
// backdoor_read
task uvm_mem::backdoor_read(uvm_reg_item rw);
rw.status = backdoor_read_func(rw);
endtask
// backdoor_write
task uvm_mem::backdoor_write(uvm_reg_item rw);
uvm_hdl_path_concat paths[$];
bit ok=1;
get_full_hdl_path(paths,rw.bd_kind);
foreach (rw.value[mem_idx]) begin
string idx;
idx.itoa(rw.offset + mem_idx);
foreach (paths[i]) begin
uvm_hdl_path_concat hdl_concat = paths[i];
foreach (hdl_concat.slices[j]) begin
`uvm_info("RegModel", $sformatf("backdoor_write to %s ",hdl_concat.slices[j].path),UVM_DEBUG);
if (hdl_concat.slices[j].offset < 0) begin
ok &= uvm_hdl_deposit({hdl_concat.slices[j].path,"[", idx, "]"},rw.value[mem_idx]);
continue;
end
begin
uvm_reg_data_t slice;
slice = rw.value[mem_idx] >> hdl_concat.slices[j].offset;
slice &= (1 << hdl_concat.slices[j].size)-1;
ok &= uvm_hdl_deposit({hdl_concat.slices[j].path, "[", idx, "]"}, slice);
end
end
end
end
rw.status = (ok ? UVM_IS_OK : UVM_NOT_OK);
endtask
// clear_hdl_path
function void uvm_mem::clear_hdl_path(string kind = "RTL");
if (kind == "ALL") begin
m_hdl_paths_pool = new("hdl_paths");
return;
end
if (kind == "")
kind = m_parent.get_default_hdl_path();
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_mem::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_mem::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_mem::has_hdl_path(string kind = "");
if (kind == "")
kind = m_parent.get_default_hdl_path();
return m_hdl_paths_pool.exists(kind);
endfunction
// get_hdl_path
function void uvm_mem::get_hdl_path(ref uvm_hdl_path_concat paths[$],
input string kind = "");
uvm_queue #(uvm_hdl_path_concat) hdl_paths;
if (kind == "")
kind = m_parent.get_default_hdl_path();
if (!has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"Memory 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
uvm_hdl_path_concat t = hdl_paths.get(i);
paths.push_back(t);
end
endfunction
// get_hdl_path_kinds
function void uvm_mem::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_full_hdl_path
function void uvm_mem::get_full_hdl_path(ref uvm_hdl_path_concat paths[$],
input string kind = "",
input string separator = ".");
if (kind == "")
kind = m_parent.get_default_hdl_path();
if (!has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"Memory 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[$];
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_parent
function void uvm_mem::set_parent(uvm_reg_block parent);
m_parent = parent;
endfunction
// get_parent
function uvm_reg_block uvm_mem::get_parent();
return get_block();
endfunction
// convert2string
function string uvm_mem::convert2string();
string res_str;
string prefix;
$sformat(convert2string, "%sMemory %s -- %0dx%0d bits", prefix,
get_full_name(), get_size(), get_n_bits());
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;
uvm_endianness_e endian_name;
parent_map = this_map.get_parent_map();
endian_name=this_map.get_endian();
offset = parent_map == null ? this_map.get_base_addr(UVM_NO_HIER) :
parent_map.get_submap_offset(this_map);
prefix = {prefix, " "};
$sformat(convert2string, "%sMapped in '%s' -- buswidth %0d bytes, %s, offset 'h%0h, size 'h%0h, %s\n", prefix,
this_map.get_full_name(), this_map.get_n_bytes(), endian_name.name(), offset,get_size(),get_access(this_map));
end
end
prefix = " ";
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, " ", 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, " ", res_str,
"currently executing write method"};
end
endfunction
// do_print
function void uvm_mem::do_print (uvm_printer printer);
super.do_print(printer);
//printer.print_generic(" ", " ", -1, convert2string());
printer.print_field_int("n_bits",get_n_bits(),32, UVM_UNSIGNED);
printer.print_field_int("size",get_size(),32, UVM_UNSIGNED);
endfunction
// clone
function uvm_object uvm_mem::clone();
`uvm_fatal("RegModel","RegModel memories cannot be cloned")
return null;
endfunction
// do_copy
function void uvm_mem::do_copy(uvm_object rhs);
`uvm_fatal("RegModel","RegModel memories cannot be copied")
endfunction
// do_compare
function bit uvm_mem::do_compare (uvm_object rhs,
uvm_comparer comparer);
`uvm_warning("RegModel","RegModel memories cannot be compared")
return 0;
endfunction
// do_pack
function void uvm_mem::do_pack (uvm_packer packer);
`uvm_warning("RegModel","RegModel memories cannot be packed")
endfunction
// do_unpack
function void uvm_mem::do_unpack (uvm_packer packer);
`uvm_warning("RegModel","RegModel memories cannot be unpacked")
endfunction
// Xadd_vregX
function void uvm_mem::Xadd_vregX(uvm_vreg vreg);
m_vregs[vreg] = 1;
endfunction
// Xdelete_vregX
function void uvm_mem::Xdelete_vregX(uvm_vreg vreg);
if (m_vregs.exists(vreg))
m_vregs.delete(vreg);
endfunction