//
// -------------------------------------------------------------
// Copyright 2004-2009 Synopsys, Inc.
// Copyright 2010 Mentor Graphics Corporation
// 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.
// -------------------------------------------------------------
//
//------------------------------------------------------------------------------
// Title: Virtual Registers
//------------------------------------------------------------------------------
//
// A virtual register is a collection of fields,
// overlaid on top of a memory, usually in an array.
// The semantics and layout of virtual registers comes from
// an agreement between the software and the hardware,
// not any physical structures in the DUT.
//
//------------------------------------------------------------------------------
typedef class [docs]uvm_mem_region;
typedef class [docs]uvm_mem_mam;
typedef class [docs]uvm_vreg_cbs;
//------------------------------------------------------------------------------
// Class: uvm_vreg
//
// Virtual register abstraction base class
//
// A virtual register represents a set of fields that are
// logically implemented in consecutive memory locations.
//
// All virtual register accesses eventually turn into memory accesses.
//
// A virtual register array may be implemented on top of
// any memory abstraction class and possibly dynamically
// resized and/or relocated.
//
//------------------------------------------------------------------------------
class [docs]uvm_vreg extends uvm_object;
`uvm_register_cb(uvm_vreg, uvm_vreg_cbs)
local bit locked;
local uvm_reg_block parent;
local int unsigned n_bits;
local int unsigned n_used_bits;
local uvm_vreg_field fields[$]; // Fields in LSB to MSB order
local uvm_mem mem; // Where is it implemented?
local uvm_reg_addr_t offset; // Start of vreg[0]
local int unsigned incr; // From start to start of next
local longint unsigned size; //number of vregs
local bit is_static;
local uvm_mem_region region; // Not NULL if implemented via MAM
local semaphore atomic; // Field RMW operations must be atomic
local string fname;
local int lineno;
local bit read_in_progress;
local bit write_in_progress;
//
// Group: Initialization
//
//
// FUNCTION: new
// Create a new instance and type-specific configuration
//
// Creates an instance of a virtual register abstraction class
// with the specified name.
//
// ~n_bits~ specifies the total number of bits in a virtual register.
// Not all bits need to be mapped to a virtual field.
// This value is usually a multiple of 8.
//
extern function [docs]new(string name,
int unsigned n_bits);
//
// Function: configure
// Instance-specific configuration
//
// Specify the ~parent~ block of this virtual register array.
// If one of the other parameters are specified, the virtual register
// is assumed to be dynamic and can be later (re-)implemented using
// the <uvm_vreg::implement()> method.
//
// If ~mem~ is specified, then the virtual register array is assumed
// to be statically implemented in the memory corresponding to the specified
// memory abstraction class and ~size~, ~offset~ and ~incr~
// must also be specified.
// Static virtual register arrays cannot be re-implemented.
//
extern function void [docs]configure(uvm_reg_block parent,
uvm_mem mem = null,
longint unsigned size = 0,
uvm_reg_addr_t offset = 0,
int unsigned incr = 0);
//
// FUNCTION: implement
// Dynamically implement, resize or relocate a virtual register array
//
// Implement an array of virtual registers of the specified
// ~size~, in the specified memory and ~offset~.
// If an offset increment is specified, each
// virtual register is implemented at the specified offset increment
// from the previous one.
// If an offset increment of 0 is specified,
// virtual registers are packed as closely as possible
// in the memory.
//
// If no memory is specified, the virtual register array is
// in the same memory, at the same base offset using the same
// offset increment as originally implemented.
// Only the number of virtual registers in the virtual register array
// is modified.
//
// The initial value of the newly-implemented or
// relocated set of virtual registers is whatever values
// are currently stored in the memory now implementing them.
//
// Returns TRUE if the memory
// can implement the number of virtual registers
// at the specified base offset and offset increment.
// Returns FALSE otherwise.
//
// The memory region used to implement a virtual register array
// is reserved in the memory allocation manager associated with
// the memory to prevent it from being allocated for another purpose.
//
extern virtual function bit [docs]implement(longint unsigned n,
uvm_mem mem = null,
uvm_reg_addr_t offset = 0,
int unsigned incr = 0);
//
// FUNCTION: allocate
// Randomly implement, resize or relocate a virtual register array
//
// Implement a virtual register array of the specified
// size in a randomly allocated region of the appropriate size
// in the address space managed by the specified memory allocation manager.
// If a memory allocation policy is specified, it is passed to the
// uvm_mem_mam::request_region() method.
//
// The initial value of the newly-implemented
// or relocated set of virtual registers is whatever values are
// currently stored in the
// memory region now implementing them.
//
// Returns a reference to a <uvm_mem_region> memory region descriptor
// if the memory allocation manager was able to allocate a region
// that can implement the virtual register array with the specified allocation policy.
// Returns ~null~ otherwise.
//
// A region implementing a virtual register array
// must not be released using the <uvm_mem_mam::release_region()> method.
// It must be released using the <uvm_vreg::release_region()> method.
//
extern virtual function uvm_mem_region [docs]allocate(longint unsigned n,
uvm_mem_mam mam,
uvm_mem_mam_policy alloc = null);
//
// FUNCTION: get_region
// Get the region where the virtual register array is implemented
//
// Returns a reference to the <uvm_mem_region> memory region descriptor
// that implements the virtual register array.
//
// Returns ~null~ if the virtual registers array
// is not currently implemented.
// A region implementing a virtual register array
// must not be released using the <uvm_mem_mam::release_region()> method.
// It must be released using the <uvm_vreg::release_region()> method.
//
extern virtual function uvm_mem_region [docs]get_region();
//
// FUNCTION: release_region
// Dynamically un-implement a virtual register array
//
// Release the memory region used to implement a virtual register array
// and return it to the pool of available memory
// that can be allocated by the memory's default allocation manager.
// The virtual register array is subsequently considered as unimplemented
// and can no longer be accessed.
//
// Statically-implemented virtual registers cannot be released.
//
extern virtual function void [docs]release_region();
/*local*/ extern virtual function void [docs]set_parent(uvm_reg_block parent);
/*local*/ extern function void [docs]Xlock_modelX();
/*local*/ extern function void [docs]add_field(uvm_vreg_field field);
/*local*/ extern task [docs]XatomicX(bit on);
//
// 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_memory
// Get the memory where the virtual register array is implemented
//
extern virtual function uvm_mem [docs]get_memory();
//
// Function: get_n_maps
// Returns the number of address maps this virtual register array is mapped in
//
extern virtual function int [docs]get_n_maps ();
//
// Function: is_in_map
// Return TRUE if this virtual register array 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 virtual register array is mapped
//
extern virtual function void [docs]get_maps (ref uvm_reg_map maps[$]);
//
// FUNCTION: get_rights
// Returns the access rights of this virtual register array
//
// Returns "RW", "RO" or "WO".
// The access rights of a virtual register array is always "RW",
// unless it is implemented in 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 virtual register array
// when written and read via an address map.
//
// If the memory implementing the virtual register array
// 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 an address map with read-only restrictions would return "RO".
//
extern virtual function string [docs]get_access(uvm_reg_map map = null);
//
// FUNCTION: get_size
// Returns the size of the virtual register array.
//
extern virtual function int unsigned [docs]get_size();
//
// FUNCTION: get_n_bytes
// Returns the width, in bytes, of a virtual register.
//
// The width of a virtual register is always a multiple of the width
// of the memory locations used to implement it.
// For example, a virtual register containing two 1-byte fields
// implemented in a memory with 4-bytes memory locations is 4-byte wide.
//
extern virtual function int unsigned [docs]get_n_bytes();
//
// FUNCTION: get_n_memlocs
// Returns the number of memory locations used
// by a single virtual register.
//
extern virtual function int unsigned [docs]get_n_memlocs();
//
// FUNCTION: get_incr
// Returns the number of memory locations
// between two individual virtual registers in the same array.
//
extern virtual function int unsigned [docs]get_incr();
//
// FUNCTION: get_fields
// Return the virtual fields in this virtual register
//
// Fills the specified array with the abstraction class
// for all of the virtual fields contained in this virtual register.
// Fields are ordered from least-significant position to most-significant
// position within the register.
//
extern virtual function void [docs]get_fields(ref uvm_vreg_field fields[$]);
//
// FUNCTION: get_field_by_name
// Return the named virtual field in this virtual register
//
// Finds a virtual field with the specified name in this virtual register
// and returns its abstraction class.
// If no fields are found, returns ~null~.
//
extern virtual function uvm_vreg_field [docs]get_field_by_name(string name);
//
// FUNCTION: get_offset_in_memory
// Returns the offset of a virtual register
//
// Returns the base offset of the specified virtual register,
// in the overall address space of the memory
// that implements the virtual register array.
//
extern virtual function uvm_reg_addr_t [docs]get_offset_in_memory(longint unsigned idx);
//
// FUNCTION: get_address
// Returns the base external physical address of a virtual register
//
// Returns the base external physical address of the specified
// virtual register if accessed through the specified address ~map~.
//
// If no address map is specified and the memory implementing
// the virtual register array 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(longint unsigned idx,
uvm_reg_map map = null);
//
// Group: HDL Access
//
//
// TASK: write
// Write the specified value in a virtual register
//
// Write ~value~ in the DUT memory location(s) that implements
// the virtual register array that corresponds to this
// abstraction class instance using the specified access
// ~path~.
//
// If the memory implementing the virtual register array
// is mapped in more than one address map,
// an address ~map~ must be
// specified if a physical access is used (front-door access).
//
// The operation is eventually mapped into set of
// memory-write operations at the location where the virtual register
// specified by ~idx~ in the virtual register array is implemented.
//
extern virtual task [docs]write(input longint unsigned idx,
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 uvm_object extension = null,
input string fname = "",
input int lineno = 0);
//
// TASK: read
// Read the current value from a virtual register
//
// Read from the DUT memory location(s) that implements
// the virtual register array that corresponds to this
// abstraction class instance using the specified access
// ~path~ and return the readback ~value~.
//
// If the memory implementing the virtual register array
// is mapped in more than one address map,
// an address ~map~ must be
// specified if a physical access is used (front-door access).
//
// The operation is eventually mapped into set of
// memory-read operations at the location where the virtual register
// specified by ~idx~ in the virtual register array is implemented.
//
extern virtual task [docs]read(input longint unsigned idx,
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 uvm_object extension = null,
input string fname = "",
input int lineno = 0);
//
// TASK: poke
// Deposit the specified value in a virtual register
//
// Deposit ~value~ in the DUT memory location(s) that implements
// the virtual register array that corresponds to this
// abstraction class instance using the memory backdoor access.
//
// The operation is eventually mapped into set of
// memory-poke operations at the location where the virtual register
// specified by ~idx~ in the virtual register array is implemented.
//
extern virtual task [docs]poke(input longint unsigned idx,
output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
//
// TASK: peek
// Sample the current value in a virtual register
//
// Sample the DUT memory location(s) that implements
// the virtual register array that corresponds to this
// abstraction class instance using the memory backdoor access,
// and return the sampled ~value~.
//
// The operation is eventually mapped into set of
// memory-peek operations at the location where the virtual register
// specified by ~idx~ in the virtual register array is implemented.
//
extern virtual task [docs]peek(input longint unsigned idx,
output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
//
// Function: reset
// Reset the access semaphore
//
// Reset the semaphore that prevents concurrent access
// to the virtual register.
// This semaphore must be explicitly reset if a thread accessing
// this virtual register array was killed in before the access
// was completed
//
extern function void [docs]reset(string kind = "HARD");
//
// Group: Callbacks
//
//
// TASK: pre_write
// Called before virtual 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 virtual register operation.
//
// The registered callback methods are invoked after the invocation
// of this method.
// All register callbacks are executed after the corresponding
// field callbacks
// The pre-write virtual register and field callbacks are executed
// before the corresponding pre-write memory callbacks
//
virtual task [docs]pre_write(longint unsigned idx,
ref uvm_reg_data_t wdat,
ref uvm_path_e path,
ref uvm_reg_map map);
endtask: pre_write
//
// TASK: post_write
// Called after virtual register write.
//
// If the specified ~status~ is modified,
// the updated status will be
// returned by the virtual register operation.
//
// The registered callback methods are invoked before the invocation
// of this method.
// All register callbacks are executed before the corresponding
// field callbacks
// The post-write virtual register and field callbacks are executed
// after the corresponding post-write memory callbacks
//
virtual task [docs]post_write(longint unsigned idx,
uvm_reg_data_t wdat,
uvm_path_e path,
uvm_reg_map map,
ref uvm_status_e status);
endtask: post_write
//
// TASK: pre_read
// Called before virtual 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.
//
// The registered callback methods are invoked after the invocation
// of this method.
// All register callbacks are executed after the corresponding
// field callbacks
// The pre-read virtual register and field callbacks are executed
// before the corresponding pre-read memory callbacks
//
virtual task [docs]pre_read(longint unsigned idx,
ref uvm_path_e path,
ref uvm_reg_map map);
endtask: pre_read
//
// TASK: post_read
// Called after virtual 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
// The post-read virtual register and field callbacks are executed
// after the corresponding post-read memory callbacks
//
virtual task [docs]post_read(longint unsigned idx,
ref uvm_reg_data_t rdat,
input uvm_path_e path,
input uvm_reg_map map,
ref uvm_status_e status);
endtask: post_read
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_vreg
//------------------------------------------------------------------------------
// Class: uvm_vreg_cbs
//
// Pre/post read/write callback facade class
//
//------------------------------------------------------------------------------
class [docs]uvm_vreg_cbs extends uvm_callback;
string fname;
int lineno;
function [docs]new(string name = "uvm_reg_cbs");
super.new(name);
endfunction
//
// Task: pre_write
// Callback called before a write operation.
//
// The registered callback methods are invoked after the invocation
// of the <uvm_vreg::pre_write()> method.
// All virtual register callbacks are executed after the corresponding
// virtual field callbacks
// The pre-write virtual register and field callbacks are executed
// before the corresponding pre-write memory callbacks
//
// The written value ~wdat~, access ~path~ and address ~map~,
// if modified, modifies the actual value, access path or address map
// used in the virtual register operation.
//
virtual task [docs]pre_write(uvm_vreg rg,
longint unsigned idx,
ref uvm_reg_data_t wdat,
ref uvm_path_e path,
ref uvm_reg_map map);
endtask: pre_write
//
// TASK: post_write
// Called after register write.
//
// The registered callback methods are invoked before the invocation
// of the <uvm_reg::post_write()> method.
// All register callbacks are executed before the corresponding
// virtual field callbacks
// The post-write virtual register and field callbacks are executed
// after the corresponding post-write memory callbacks
//
// The ~status~ of the operation,
// if modified, modifies the actual returned status.
//
virtual task [docs]post_write(uvm_vreg rg,
longint unsigned idx,
uvm_reg_data_t wdat,
uvm_path_e path,
uvm_reg_map map,
ref uvm_status_e status);
endtask: post_write
//
// TASK: pre_read
// Called before register read.
//
// The registered callback methods are invoked after the invocation
// of the <uvm_reg::pre_read()> method.
// All register callbacks are executed after the corresponding
// virtual field callbacks
// The pre-read virtual register and field callbacks are executed
// before the corresponding pre-read memory callbacks
//
// The access ~path~ and address ~map~,
// if modified, modifies the actual access path or address map
// used in the register operation.
//
virtual task [docs]pre_read(uvm_vreg rg,
longint unsigned idx,
ref uvm_path_e path,
ref uvm_reg_map map);
endtask: pre_read
//
// TASK: post_read
// Called after register read.
//
// The registered callback methods are invoked before the invocation
// of the <uvm_reg::post_read()> method.
// All register callbacks are executed before the corresponding
// virtual field callbacks
// The post-read virtual register and field callbacks are executed
// after the corresponding post-read memory callbacks
//
// The readback value ~rdat~ and the ~status~ of the operation,
// if modified, modifies the actual returned readback value and status.
//
virtual task [docs]post_read(uvm_vreg rg,
longint unsigned idx,
ref uvm_reg_data_t rdat,
input uvm_path_e path,
input uvm_reg_map map,
ref uvm_status_e status);
endtask: post_read
endclass: uvm_vreg_cbs
//
// Type: uvm_vreg_cb
// Convenience callback type declaration
//
// Use this declaration to register virtual register callbacks rather than
// the more verbose parameterized class
//
typedef uvm_callbacks#(uvm_vreg, uvm_vreg_cbs) [docs]uvm_vreg_cb;
//
// Type: uvm_vreg_cb_iter
// Convenience callback iterator type declaration
//
// Use this declaration to iterate over registered virtual register callbacks
// rather than the more verbose parameterized class
//
typedef uvm_callback_iter#(uvm_vreg, uvm_vreg_cbs) [docs]uvm_vreg_cb_iter;
//------------------------------------------------------------------------------
// IMPLEMENTATION
//------------------------------------------------------------------------------
function uvm_vreg::new(string name,
int unsigned n_bits);
super.new(name);
if (n_bits == 0) begin
`uvm_error("RegModel", $sformatf("Virtual register \"%s\" cannot have 0 bits", this.get_full_name()));
n_bits = 1;
end
if (n_bits > `UVM_REG_DATA_WIDTH) begin
`uvm_error("RegModel", $sformatf("Virtual register \"%s\" cannot have more than %0d bits (%0d)", this.get_full_name(), `UVM_REG_DATA_WIDTH, n_bits));
n_bits = `UVM_REG_DATA_WIDTH;
end
this.n_bits = n_bits;
this.locked = 0;
endfunction: new
function void uvm_vreg::configure(uvm_reg_block parent,
uvm_mem mem = null,
longint unsigned size = 0,
uvm_reg_addr_t offset = 0,
int unsigned incr = 0);
this.parent = parent;
this.n_used_bits = 0;
if (mem != null) begin
void'(this.implement(size, mem, offset, incr));
this.is_static = 1;
end
else begin
this.mem = null;
this.is_static = 0;
end
this.parent.add_vreg(this);
this.atomic = new(1);
endfunction: configure
function void uvm_vreg::Xlock_modelX();
if (this.locked) return;
this.locked = 1;
endfunction: Xlock_modelX
function void uvm_vreg::add_field(uvm_vreg_field field);
int offset;
int idx;
if (this.locked) begin
`uvm_error("RegModel", "Cannot add virtual field to locked virtual register model");
return;
end
if (field == null) `uvm_fatal("RegModel", "Attempting to register NULL virtual field");
// Store fields in LSB to MSB order
offset = field.get_lsb_pos_in_register();
idx = -1;
foreach (this.fields[i]) begin
if (offset < this.fields[i].get_lsb_pos_in_register()) begin
int j = i;
this.fields.insert(j, field);
idx = i;
break;
end
end
if (idx < 0) begin
this.fields.push_back(field);
idx = this.fields.size()-1;
end
this.n_used_bits += field.get_n_bits();
// Check if there are too many fields in the register
if (this.n_used_bits > this.n_bits) begin
`uvm_error("RegModel", $sformatf("Virtual fields use more bits (%0d) than available in virtual register \"%s\" (%0d)",
this.n_used_bits, this.get_full_name(), this.n_bits));
end
// Check if there are overlapping fields
if (idx > 0) begin
if (this.fields[idx-1].get_lsb_pos_in_register() +
this.fields[idx-1].get_n_bits() > offset) begin
`uvm_error("RegModel", $sformatf("Field %s overlaps field %s in virtual register \"%s\"",
this.fields[idx-1].get_name(),
field.get_name(),
this.get_full_name()));
end
end
if (idx < this.fields.size()-1) begin
if (offset + field.get_n_bits() >
this.fields[idx+1].get_lsb_pos_in_register()) begin
`uvm_error("RegModel", $sformatf("Field %s overlaps field %s in virtual register \"%s\"",
field.get_name(),
this.fields[idx+1].get_name(),
this.get_full_name()));
end
end
endfunction: add_field
task uvm_vreg::XatomicX(bit on);
if (on) this.atomic.get(1);
else begin
// Maybe a key was put back in by a spurious call to reset()
void'(this.atomic.try_get(1));
this.atomic.put(1);
end
endtask: XatomicX
function void uvm_vreg::reset(string kind = "HARD");
// Put back a key in the semaphore if it is checked out
// in case a thread was killed during an operation
void'(this.atomic.try_get(1));
this.atomic.put(1);
endfunction: reset
function string uvm_vreg::get_full_name();
uvm_reg_block blk;
get_full_name = this.get_name();
// Do not include top-level name in full name
blk = this.get_block();
if (blk == null) return get_full_name;
if (blk.get_parent() == null) return get_full_name;
get_full_name = {this.parent.get_full_name(), ".", get_full_name};
endfunction: get_full_name
function void uvm_vreg::set_parent(uvm_reg_block parent);
this.parent = parent;
endfunction: set_parent
function uvm_reg_block uvm_vreg::get_parent();
get_parent = this.parent;
endfunction: get_parent
function uvm_reg_block uvm_vreg::get_block();
get_block = this.parent;
endfunction: get_block
function bit uvm_vreg::implement(longint unsigned n,
uvm_mem mem = null,
uvm_reg_addr_t offset = 0,
int unsigned incr = 0);
uvm_mem_region region;
if(n < 1)
begin
`uvm_error("RegModel", $sformatf("Attempting to implement virtual register \"%s\" with a subscript less than one doesn't make sense",this.get_full_name()));
return 0;
end
if (mem == null) begin
`uvm_error("RegModel", $sformatf("Attempting to implement virtual register \"%s\" using a NULL uvm_mem reference", this.get_full_name()));
return 0;
end
if (this.is_static) begin
`uvm_error("RegModel", $sformatf("Virtual register \"%s\" is static and cannot be dynamically implemented", this.get_full_name()));
return 0;
end
if (mem.get_block() != this.parent) begin
`uvm_error("RegModel", $sformatf("Attempting to implement virtual register \"%s\" on memory \"%s\" in a different block",
this.get_full_name(),
mem.get_full_name()));
return 0;
end
begin
int min_incr = (this.get_n_bytes()-1) / mem.get_n_bytes() + 1;
if (incr == 0) incr = min_incr;
if (min_incr > incr) begin
`uvm_error("RegModel", $sformatf("Virtual register \"%s\" increment is too small (%0d): Each virtual register requires at least %0d locations in memory \"%s\".",
this.get_full_name(), incr,
min_incr, mem.get_full_name()));
return 0;
end
end
// Is the memory big enough for ya?
if (offset + (n * incr) > mem.get_size()) begin
`uvm_error("RegModel", $sformatf("Given Offset for Virtual register \"%s[%0d]\" is too big for memory %s@'h%0h", this.get_full_name(), n, mem.get_full_name(), offset));
return 0;
end
region = mem.mam.reserve_region(offset,n*incr*mem.get_n_bytes());
if (region == null) begin
`uvm_error("RegModel", $sformatf("Could not allocate a memory region for virtual register \"%s\"", this.get_full_name()));
return 0;
end
if (this.mem != null) begin
`uvm_info("RegModel", $sformatf("Virtual register \"%s\" is being moved re-implemented from %s@'h%0h to %s@'h%0h",
this.get_full_name(),
this.mem.get_full_name(),
this.offset,
mem.get_full_name(), offset),UVM_MEDIUM);
this.release_region();
end
this.region = region;
this.mem = mem;
this.size = n;
this.offset = offset;
this.incr = incr;
this.mem.Xadd_vregX(this);
return 1;
endfunction: implement
function uvm_mem_region uvm_vreg::allocate(longint unsigned n,
uvm_mem_mam mam,
uvm_mem_mam_policy alloc=null);
uvm_mem mem;
if(n < 1)
begin
`uvm_error("RegModel", $sformatf("Attempting to implement virtual register \"%s\" with a subscript less than one doesn't make sense",this.get_full_name()));
return null;
end
if (mam == null) begin
`uvm_error("RegModel", $sformatf("Attempting to implement virtual register \"%s\" using a NULL uvm_mem_mam reference", this.get_full_name()));
return null;
end
if (this.is_static) begin
`uvm_error("RegModel", $sformatf("Virtual register \"%s\" is static and cannot be dynamically allocated", this.get_full_name()));
return null;
end
mem = mam.get_memory();
if (mem.get_block() != this.parent) begin
`uvm_error("RegModel", $sformatf("Attempting to allocate virtual register \"%s\" on memory \"%s\" in a different block",
this.get_full_name(),
mem.get_full_name()));
return null;
end
begin
int min_incr = (this.get_n_bytes()-1) / mem.get_n_bytes() + 1;
if (incr == 0) incr = min_incr;
if (min_incr < incr) begin
`uvm_error("RegModel", $sformatf("Virtual register \"%s\" increment is too small (%0d): Each virtual register requires at least %0d locations in memory \"%s\".",
this.get_full_name(), incr,
min_incr, mem.get_full_name()));
return null;
end
end
// Need memory at least of size num_vregs*sizeof(vreg) in bytes.
allocate = mam.request_region(n*incr*mem.get_n_bytes(), alloc);
if (allocate == null) begin
`uvm_error("RegModel", $sformatf("Could not allocate a memory region for virtual register \"%s\"", this.get_full_name()));
return null;
end
if (this.mem != null) begin
`uvm_info("RegModel", $sformatf("Virtual register \"%s\" is being moved from %s@'h%0h to %s@'h%0h",
this.get_full_name(),
this.mem.get_full_name(),
this.offset,
mem.get_full_name(),
allocate.get_start_offset()),UVM_MEDIUM);
this.release_region();
end
this.region = allocate;
this.mem = mam.get_memory();
this.offset = allocate.get_start_offset();
this.size = n;
this.incr = incr;
this.mem.Xadd_vregX(this);
endfunction: allocate
function uvm_mem_region uvm_vreg::get_region();
return this.region;
endfunction: get_region
function void uvm_vreg::release_region();
if (this.is_static) begin
`uvm_error("RegModel", $sformatf("Virtual register \"%s\" is static and cannot be dynamically released", this.get_full_name()));
return;
end
if (this.mem != null)
this.mem.Xdelete_vregX(this);
if (this.region != null) begin
this.region.release_region();
end
this.region = null;
this.mem = null;
this.size = 0;
this.offset = 0;
this.reset();
endfunction: release_region
function uvm_mem uvm_vreg::get_memory();
return this.mem;
endfunction: get_memory
function uvm_reg_addr_t uvm_vreg::get_offset_in_memory(longint unsigned idx);
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_offset_in_memory() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return 0;
end
return this.offset + idx * this.incr;
endfunction
function uvm_reg_addr_t uvm_vreg::get_address(longint unsigned idx,
uvm_reg_map map = null);
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot get address of of unimplemented virtual register \"%s\".", this.get_full_name()));
return 0;
end
return this.mem.get_address(this.get_offset_in_memory(idx), map);
endfunction: get_address
function int unsigned uvm_vreg::get_size();
if (this.size == 0) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_size() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return 0;
end
return this.size;
endfunction: get_size
function int unsigned uvm_vreg::get_n_bytes();
return ((this.n_bits-1) / 8) + 1;
endfunction: get_n_bytes
function int unsigned uvm_vreg::get_n_memlocs();
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_n_memlocs() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return 0;
end
return (this.get_n_bytes()-1) / this.mem.get_n_bytes() + 1;
endfunction: get_n_memlocs
function int unsigned uvm_vreg::get_incr();
if (this.incr == 0) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_incr() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return 0;
end
return this.incr;
endfunction: get_incr
function int uvm_vreg::get_n_maps();
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_n_maps() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return 0;
end
return this.mem.get_n_maps();
endfunction: get_n_maps
function void uvm_vreg::get_maps(ref uvm_reg_map maps[$]);
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_maps() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return;
end
this.mem.get_maps(maps);
endfunction: get_maps
function bit uvm_vreg::is_in_map(uvm_reg_map map);
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::is_in_map() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return 0;
end
return this.mem.is_in_map(map);
endfunction
function string uvm_vreg::get_access(uvm_reg_map map = null);
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_rights() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return "RW";
end
return this.mem.get_access(map);
endfunction: get_access
function string uvm_vreg::get_rights(uvm_reg_map map = null);
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot call uvm_vreg::get_rights() on unimplemented virtual register \"%s\"",
this.get_full_name()));
return "RW";
end
return this.mem.get_rights(map);
endfunction: get_rights
function void uvm_vreg::get_fields(ref uvm_vreg_field fields[$]);
foreach(this.fields[i])
fields.push_back(this.fields[i]);
endfunction: get_fields
function uvm_vreg_field uvm_vreg::get_field_by_name(string name);
foreach (this.fields[i]) begin
if (this.fields[i].get_name() == name) begin
return this.fields[i];
end
end
`uvm_warning("RegModel", $sformatf("Unable to locate field \"%s\" in virtual register \"%s\".",
name, this.get_full_name()));
get_field_by_name = null;
endfunction: get_field_by_name
task uvm_vreg::write(input longint unsigned idx,
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 uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_vreg_cb_iter cbs = new(this);
uvm_reg_addr_t addr;
uvm_reg_data_t tmp;
uvm_reg_data_t msk;
int lsb;
this.write_in_progress = 1'b1;
this.fname = fname;
this.lineno = lineno;
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot write to unimplemented virtual register \"%s\".", this.get_full_name()));
status = UVM_NOT_OK;
return;
end
if (path == UVM_DEFAULT_PATH)
path = this.parent.get_default_path();
foreach (fields[i]) begin
uvm_vreg_field_cb_iter cbs = new(fields[i]);
uvm_vreg_field f = fields[i];
lsb = f.get_lsb_pos_in_register();
msk = ((1<<f.get_n_bits())-1) << lsb;
tmp = (value & msk) >> lsb;
f.pre_write(idx, tmp, path, map);
for (uvm_vreg_field_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.pre_write(f, idx, tmp, path, map);
end
value = (value & ~msk) | (tmp << lsb);
end
this.pre_write(idx, value, path, map);
for (uvm_vreg_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.pre_write(this, idx, value, path, map);
end
addr = this.offset + (idx * this.incr);
lsb = 0;
status = UVM_IS_OK;
for (int i = 0; i < this.get_n_memlocs(); i++) begin
uvm_status_e s;
msk = ((1<<(this.mem.get_n_bytes()*8))-1) << lsb;
tmp = (value & msk) >> lsb;
this.mem.write(s, addr + i, tmp, path, map , parent, , extension, fname, lineno);
if (s != UVM_IS_OK && s != UVM_HAS_X) status = s;
lsb += this.mem.get_n_bytes() * 8;
end
for (uvm_vreg_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.post_write(this, idx, value, path, map, status);
end
this.post_write(idx, value, path, map, status);
foreach (fields[i]) begin
uvm_vreg_field_cb_iter cbs = new(fields[i]);
uvm_vreg_field f = fields[i];
lsb = f.get_lsb_pos_in_register();
msk = ((1<<f.get_n_bits())-1) << lsb;
tmp = (value & msk) >> lsb;
for (uvm_vreg_field_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.post_write(f, idx, tmp, path, map, status);
end
f.post_write(idx, tmp, path, map, status);
value = (value & ~msk) | (tmp << lsb);
end
`uvm_info("RegModel", $sformatf("Wrote virtual register \"%s\"[%0d] via %s with: 'h%h",
this.get_full_name(), idx,
(path == UVM_FRONTDOOR) ? "frontdoor" : "backdoor",
value),UVM_MEDIUM);
this.write_in_progress = 1'b0;
this.fname = "";
this.lineno = 0;
endtask: write
task uvm_vreg::read(input longint unsigned idx,
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 uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_vreg_cb_iter cbs = new(this);
uvm_reg_addr_t addr;
uvm_reg_data_t tmp;
uvm_reg_data_t msk;
int lsb;
this.read_in_progress = 1'b1;
this.fname = fname;
this.lineno = lineno;
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot read from unimplemented virtual register \"%s\".", this.get_full_name()));
status = UVM_NOT_OK;
return;
end
if (path == UVM_DEFAULT_PATH)
path = this.parent.get_default_path();
foreach (fields[i]) begin
uvm_vreg_field_cb_iter cbs = new(fields[i]);
uvm_vreg_field f = fields[i];
f.pre_read(idx, path, map);
for (uvm_vreg_field_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.pre_read(f, idx, path, map);
end
end
this.pre_read(idx, path, map);
for (uvm_vreg_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.pre_read(this, idx, path, map);
end
addr = this.offset + (idx * this.incr);
lsb = 0;
value = 0;
status = UVM_IS_OK;
for (int i = 0; i < this.get_n_memlocs(); i++) begin
uvm_status_e s;
this.mem.read(s, addr + i, tmp, path, map, parent, , extension, fname, lineno);
if (s != UVM_IS_OK && s != UVM_HAS_X) status = s;
value |= tmp << lsb;
lsb += this.mem.get_n_bytes() * 8;
end
for (uvm_vreg_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.post_read(this, idx, value, path, map, status);
end
this.post_read(idx, value, path, map, status);
foreach (fields[i]) begin
uvm_vreg_field_cb_iter cbs = new(fields[i]);
uvm_vreg_field f = fields[i];
lsb = f.get_lsb_pos_in_register();
msk = ((1<<f.get_n_bits())-1) << lsb;
tmp = (value & msk) >> lsb;
for (uvm_vreg_field_cbs cb = cbs.first(); cb != null;
cb = cbs.next()) begin
cb.fname = this.fname;
cb.lineno = this.lineno;
cb.post_read(f, idx, tmp, path, map, status);
end
f.post_read(idx, tmp, path, map, status);
value = (value & ~msk) | (tmp << lsb);
end
`uvm_info("RegModel", $sformatf("Read virtual register \"%s\"[%0d] via %s: 'h%h",
this.get_full_name(), idx,
(path == UVM_FRONTDOOR) ? "frontdoor" : "backdoor",
value),UVM_MEDIUM);
this.read_in_progress = 1'b0;
this.fname = "";
this.lineno = 0;
endtask: read
task uvm_vreg::poke(input longint unsigned idx,
output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_addr_t addr;
uvm_reg_data_t tmp;
uvm_reg_data_t msk;
int lsb;
this.fname = fname;
this.lineno = lineno;
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot poke in unimplemented virtual register \"%s\".", this.get_full_name()));
status = UVM_NOT_OK;
return;
end
addr = this.offset + (idx * this.incr);
lsb = 0;
status = UVM_IS_OK;
for (int i = 0; i < this.get_n_memlocs(); i++) begin
uvm_status_e s;
msk = ((1<<(this.mem.get_n_bytes() * 8))-1) << lsb;
tmp = (value & msk) >> lsb;
this.mem.poke(status, addr + i, tmp, "", parent, extension, fname, lineno);
if (s != UVM_IS_OK && s != UVM_HAS_X) status = s;
lsb += this.mem.get_n_bytes() * 8;
end
`uvm_info("RegModel", $sformatf("Poked virtual register \"%s\"[%0d] with: 'h%h",
this.get_full_name(), idx, value),UVM_MEDIUM);
this.fname = "";
this.lineno = 0;
endtask: poke
task uvm_vreg::peek(input longint unsigned idx,
output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_addr_t addr;
uvm_reg_data_t tmp;
uvm_reg_data_t msk;
int lsb;
this.fname = fname;
this.lineno = lineno;
if (this.mem == null) begin
`uvm_error("RegModel", $sformatf("Cannot peek in from unimplemented virtual register \"%s\".", this.get_full_name()));
status = UVM_NOT_OK;
return;
end
addr = this.offset + (idx * this.incr);
lsb = 0;
value = 0;
status = UVM_IS_OK;
for (int i = 0; i < this.get_n_memlocs(); i++) begin
uvm_status_e s;
this.mem.peek(status, addr + i, tmp, "", parent, extension, fname, lineno);
if (s != UVM_IS_OK && s != UVM_HAS_X) status = s;
value |= tmp << lsb;
lsb += this.mem.get_n_bytes() * 8;
end
`uvm_info("RegModel", $sformatf("Peeked virtual register \"%s\"[%0d]: 'h%h",
this.get_full_name(), idx, value),UVM_MEDIUM);
this.fname = "";
this.lineno = 0;
endtask: peek
function void uvm_vreg::do_print (uvm_printer printer);
super.do_print(printer);
printer.print_generic("initiator", parent.get_type_name(), -1, convert2string());
endfunction
function string uvm_vreg::convert2string();
string res_str;
string t_str;
bit with_debug_info;
$sformat(convert2string, "Virtual register %s -- ",
this.get_full_name());
if (this.size == 0)
$sformat(convert2string, "%sunimplemented", convert2string);
else begin
uvm_reg_map maps[$];
mem.get_maps(maps);
$sformat(convert2string, "%s[%0d] in %0s['h%0h+'h%0h]\n", convert2string,
this.size, this.mem.get_full_name(), this.offset, this.incr);
foreach (maps[i]) begin
uvm_reg_addr_t addr0 = this.get_address(0, maps[i]);
$sformat(convert2string, " Address in map '%s' -- @'h%0h+%0h",
maps[i].get_full_name(), addr0, this.get_address(1, maps[i]) - addr0);
end
end
foreach(this.fields[i]) begin
$sformat(convert2string, "%s\n%s", convert2string,
this.fields[i].convert2string());
end
endfunction: convert2string
//TODO - add fatal messages
function uvm_object uvm_vreg::clone();
return null;
endfunction
function void uvm_vreg::do_copy (uvm_object rhs);
endfunction
function bit uvm_vreg::do_compare (uvm_object rhs,
uvm_comparer comparer);
return 0;
endfunction
function void uvm_vreg::do_pack (uvm_packer packer);
endfunction
function void uvm_vreg::do_unpack (uvm_packer packer);
endfunction