blob: ee159a756a3c2f2bdc7ca1501d776c91eb0de3c9 [file] [log] [blame]
//
// -------------------------------------------------------------
// Copyright 2010-2012 Mentor Graphics Corporation
// Copyright 2011-2014 Semifore
// Copyright 2018 Intel Corporation
// Copyright 2004-2018 Synopsys, Inc.
// Copyright 2010-2018 Cadence Design Systems, Inc.
// Copyright 2010 AMD
// Copyright 2014-2018 NVIDIA Corporation
// Copyright 2012 Accellera Systems Initiative
// All Rights Reserved Worldwide
//
// Licensed under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in
// writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See
// the License for the specific language governing
// permissions and limitations under the License.
// -------------------------------------------------------------
//
typedef class uvm_reg_cbs;
typedef class uvm_reg_frontdoor;
// @uvm-ieee 1800.2-2017 auto 18.4.1
class uvm_reg extends uvm_object;
local bit m_locked;
local uvm_reg_block m_parent;
local uvm_reg_file m_regfile_parent;
local int unsigned m_n_bits;
local int unsigned m_n_used_bits;
protected bit m_maps[uvm_reg_map];
protected uvm_reg_field m_fields[$]; // Fields in LSB to MSB order
local int m_has_cover;
local int m_cover_on;
local semaphore m_atomic;
local process m_process;
local string m_fname;
local int m_lineno;
local bit m_read_in_progress;
local bit m_write_in_progress;
protected bit m_update_in_progress;
/*local*/ bit m_is_busy;
/*local*/ bit m_is_locked_by_field;
local uvm_reg_backdoor m_backdoor;
local static int unsigned m_max_size;
local uvm_object_string_pool
#(uvm_queue #(uvm_hdl_path_concat)) m_hdl_paths_pool;
//----------------------
// Group -- NODOCS -- Initialization
//----------------------
// @uvm-ieee 1800.2-2017 auto 18.4.2.1
extern function new (string name="",
int unsigned n_bits,
int has_coverage);
// @uvm-ieee 1800.2-2017 auto 18.4.2.2
extern function void configure (uvm_reg_block blk_parent,
uvm_reg_file regfile_parent = null,
string hdl_path = "");
// @uvm-ieee 1800.2-2017 auto 18.4.2.3
extern virtual function void set_offset (uvm_reg_map map,
uvm_reg_addr_t offset,
bit unmapped = 0);
/*local*/ extern virtual function void set_parent (uvm_reg_block blk_parent,
uvm_reg_file regfile_parent);
/*local*/ extern virtual function void add_field (uvm_reg_field field);
/*local*/ extern virtual function void add_map (uvm_reg_map map);
/*local*/ extern function void Xlock_modelX;
// remove the knowledge that the register resides in the map from the register instance
// @uvm-ieee 1800.2-2017 auto 18.4.2.5
virtual function void unregister(uvm_reg_map map);
m_maps.delete(map);
endfunction
//---------------------
// Group -- NODOCS -- Introspection
//---------------------
// Function -- NODOCS -- get_name
//
// Get the simple name
//
// Return the simple object name of this register.
//
// Function -- NODOCS -- 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 get_full_name();
// @uvm-ieee 1800.2-2017 auto 18.4.3.1
extern virtual function uvm_reg_block get_parent ();
extern virtual function uvm_reg_block get_block ();
// @uvm-ieee 1800.2-2017 auto 18.4.3.2
extern virtual function uvm_reg_file get_regfile ();
// @uvm-ieee 1800.2-2017 auto 18.4.3.3
extern virtual function int get_n_maps ();
// @uvm-ieee 1800.2-2017 auto 18.4.3.4
extern function bit is_in_map (uvm_reg_map map);
// @uvm-ieee 1800.2-2017 auto 18.4.3.5
extern virtual function void get_maps (ref uvm_reg_map maps[$]);
/*local*/ extern virtual function uvm_reg_map get_local_map (uvm_reg_map map);
/*local*/ extern virtual function uvm_reg_map get_default_map ();
// @uvm-ieee 1800.2-2017 auto 18.4.3.6
extern virtual function string get_rights (uvm_reg_map map = null);
// Function -- NODOCS -- get_n_bits
//
// Returns the width, in bits, of this register.
//
extern virtual function int unsigned get_n_bits ();
// Function -- NODOCS -- get_n_bytes
//
// Returns the width, in bytes, of this register. Rounds up to
// next whole byte if register is not a multiple of 8.
//
extern virtual function int unsigned get_n_bytes();
// Function -- NODOCS -- get_max_size
//
// Returns the maximum width, in bits, of all registers.
//
extern static function int unsigned get_max_size();
// @uvm-ieee 1800.2-2017 auto 18.4.3.10
extern virtual function void get_fields (ref uvm_reg_field fields[$]);
// @uvm-ieee 1800.2-2017 auto 18.4.3.11
extern virtual function uvm_reg_field get_field_by_name(string name);
/*local*/ extern function string Xget_fields_accessX(uvm_reg_map map);
// @uvm-ieee 1800.2-2017 auto 18.4.3.12
extern virtual function uvm_reg_addr_t get_offset (uvm_reg_map map = null);
// @uvm-ieee 1800.2-2017 auto 18.4.3.13
extern virtual function uvm_reg_addr_t get_address (uvm_reg_map map = null);
// @uvm-ieee 1800.2-2017 auto 18.4.3.14
extern virtual function int get_addresses (uvm_reg_map map = null,
ref uvm_reg_addr_t addr[]);
//--------------
// Group -- NODOCS -- Access
//--------------
// @uvm-ieee 1800.2-2017 auto 18.4.4.2
extern virtual function void set (uvm_reg_data_t value,
string fname = "",
int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.4.1
extern virtual function uvm_reg_data_t get(string fname = "",
int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.4.3
extern virtual function uvm_reg_data_t get_mirrored_value(string fname = "",
int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.4.4
extern virtual function bit needs_update();
// @uvm-ieee 1800.2-2017 auto 18.4.4.5
extern virtual function void reset(string kind = "HARD");
// Function -- NODOCS -- get_reset
//
// Get the specified reset value for this register
//
// Return the reset value for this register
// for the specified reset ~kind~.
//
extern virtual function uvm_reg_data_t
// @uvm-ieee 1800.2-2017 auto 18.4.4.6
get_reset(string kind = "HARD");
// @uvm-ieee 1800.2-2017 auto 18.4.4.7
extern virtual function bit has_reset(string kind = "HARD",
bit delete = 0);
// Function -- NODOCS -- set_reset
//
// Specify or modify the reset value for this register
//
// Specify or modify the reset value for all the fields in the register
// corresponding to the cause specified by ~kind~.
//
extern virtual function void
// @uvm-ieee 1800.2-2017 auto 18.4.4.8
set_reset(uvm_reg_data_t value,
string kind = "HARD");
// @uvm-ieee 1800.2-2017 auto 18.4.4.9
// @uvm-ieee 1800.2-2017 auto 18.8.5.3
extern virtual task write(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_door_e path = UVM_DEFAULT_DOOR,
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-ieee 1800.2-2017 auto 18.4.4.10
// @uvm-ieee 1800.2-2017 auto 18.8.5.4
extern virtual task read(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_door_e path = UVM_DEFAULT_DOOR,
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-ieee 1800.2-2017 auto 18.4.4.11
// @uvm-ieee 1800.2-2017 auto 18.8.5.5
// @uvm-ieee 1800.2-2017 auto 18.8.5.6
extern virtual task poke(output uvm_status_e status,
input uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.4.12
extern virtual task peek(output uvm_status_e status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.4.13
extern virtual task update(output uvm_status_e status,
input uvm_door_e path = UVM_DEFAULT_DOOR,
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-ieee 1800.2-2017 auto 18.4.4.14
// @uvm-ieee 1800.2-2017 auto 18.8.5.8
extern virtual task mirror(output uvm_status_e status,
input uvm_check_e check = UVM_NO_CHECK,
input uvm_door_e path = UVM_DEFAULT_DOOR,
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-ieee 1800.2-2017 auto 18.4.4.15
// @uvm-ieee 1800.2-2017 auto 18.8.5.9
extern virtual function bit predict (uvm_reg_data_t value,
uvm_reg_byte_en_t be = -1,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_door_e path = UVM_FRONTDOOR,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.4.16
extern function bit is_busy();
/*local*/ extern function void Xset_busyX(bit busy);
/*local*/ extern task XreadX (output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_door_e path,
input uvm_reg_map map,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
/*local*/ extern task XatomicX(bit on);
/*local*/ extern virtual function bit Xcheck_accessX
(input uvm_reg_item rw,
output uvm_reg_map_info map_info);
/*local*/ extern function bit Xis_locked_by_fieldX();
extern virtual function bit do_check(uvm_reg_data_t expected,
uvm_reg_data_t actual,
uvm_reg_map map);
extern virtual task do_write(uvm_reg_item rw);
extern virtual task do_read(uvm_reg_item rw);
extern virtual function void do_predict
(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
//-----------------
// Group -- NODOCS -- Frontdoor
//-----------------
// @uvm-ieee 1800.2-2017 auto 18.4.5.2
extern function void set_frontdoor(uvm_reg_frontdoor ftdr,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.5.1
extern function uvm_reg_frontdoor get_frontdoor(uvm_reg_map map = null);
//----------------
// Group -- NODOCS -- Backdoor
//----------------
// @uvm-ieee 1800.2-2017 auto 18.4.6.2
extern function void set_backdoor(uvm_reg_backdoor bkdr,
string fname = "",
int lineno = 0);
// @uvm-ieee 1800.2-2017 auto 18.4.6.1
extern function uvm_reg_backdoor get_backdoor(bit inherited = 1);
// @uvm-ieee 1800.2-2017 auto 18.4.6.3
extern function void clear_hdl_path (string kind = "RTL");
// @uvm-ieee 1800.2-2017 auto 18.4.6.4
extern function void add_hdl_path (uvm_hdl_path_slice slices[],
string kind = "RTL");
// @uvm-ieee 1800.2-2017 auto 18.4.6.5
extern function void add_hdl_path_slice(string name,
int offset,
int size,
bit first = 0,
string kind = "RTL");
// @uvm-ieee 1800.2-2017 auto 18.4.6.6
extern function bit has_hdl_path (string kind = "");
// @uvm-ieee 1800.2-2017 auto 18.4.6.7
extern function void get_hdl_path (ref uvm_hdl_path_concat paths[$],
input string kind = "");
// @uvm-ieee 1800.2-2017 auto 18.4.6.8
extern function void get_hdl_path_kinds (ref string kinds[$]);
// @uvm-ieee 1800.2-2017 auto 18.4.6.9
extern function void get_full_hdl_path (ref uvm_hdl_path_concat paths[$],
input string kind = "",
input string separator = ".");
// @uvm-ieee 1800.2-2017 auto 18.4.6.10
extern virtual task backdoor_read(uvm_reg_item rw);
// @uvm-ieee 1800.2-2017 auto 18.4.6.11
extern virtual task backdoor_write(uvm_reg_item rw);
extern virtual function uvm_status_e backdoor_read_func(uvm_reg_item rw);
// @uvm-ieee 1800.2-2017 auto 18.4.6.12
virtual task backdoor_watch(); endtask
//----------------
// Group -- NODOCS -- Coverage
//----------------
// @uvm-ieee 1800.2-2017 auto 18.4.7.1
extern static function void include_coverage(string scope,
uvm_reg_cvr_t models,
uvm_object accessor = null);
// @uvm-ieee 1800.2-2017 auto 18.4.7.2
extern protected function uvm_reg_cvr_t build_coverage(uvm_reg_cvr_t models);
// @uvm-ieee 1800.2-2017 auto 18.4.7.3
extern virtual protected function void add_coverage(uvm_reg_cvr_t models);
// @uvm-ieee 1800.2-2017 auto 18.4.7.4
extern virtual function bit has_coverage(uvm_reg_cvr_t models);
// @uvm-ieee 1800.2-2017 auto 18.4.7.6
extern virtual function uvm_reg_cvr_t set_coverage(uvm_reg_cvr_t is_on);
// @uvm-ieee 1800.2-2017 auto 18.4.7.5
extern virtual function bit get_coverage(uvm_reg_cvr_t is_on);
// @uvm-ieee 1800.2-2017 auto 18.4.7.7
protected virtual function void sample(uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
endfunction
// @uvm-ieee 1800.2-2017 auto 18.4.7.8
virtual function void sample_values();
endfunction
/*local*/ function void XsampleX(uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
sample(data, byte_en, is_read, map);
endfunction
//-----------------
// Group -- NODOCS -- Callbacks
//-----------------
`uvm_register_cb(uvm_reg, uvm_reg_cbs)
// @uvm-ieee 1800.2-2017 auto 18.4.8.1
virtual task pre_write(uvm_reg_item rw); endtask
// @uvm-ieee 1800.2-2017 auto 18.4.8.2
virtual task post_write(uvm_reg_item rw); endtask
// @uvm-ieee 1800.2-2017 auto 18.4.8.3
virtual task pre_read(uvm_reg_item rw); endtask
// @uvm-ieee 1800.2-2017 auto 18.4.8.4
virtual task post_read(uvm_reg_item rw); endtask
extern virtual function void do_print (uvm_printer printer);
extern virtual function string convert2string();
extern virtual function uvm_object clone ();
extern virtual function void do_copy (uvm_object rhs);
extern virtual function bit do_compare (uvm_object rhs,
uvm_comparer comparer);
extern virtual function void do_pack (uvm_packer packer);
extern virtual function void do_unpack (uvm_packer packer);
endclass: uvm_reg
//------------------------------------------------------------------------------
// IMPLEMENTATION
//------------------------------------------------------------------------------
// new
function uvm_reg::new(string name="", int unsigned n_bits, int has_coverage);
super.new(name);
if (n_bits == 0) begin
`uvm_error("RegModel", $sformatf("Register \"%s\" cannot have 0 bits", get_name()))
n_bits = 1;
end
m_n_bits = n_bits;
m_has_cover = has_coverage;
m_atomic = new(1);
m_n_used_bits = 0;
m_locked = 0;
m_is_busy = 0;
m_is_locked_by_field = 1'b0;
m_hdl_paths_pool = new("hdl_paths");
if (n_bits > m_max_size)
m_max_size = n_bits;
endfunction: new
// configure
function void uvm_reg::configure (uvm_reg_block blk_parent,
uvm_reg_file regfile_parent=null,
string hdl_path = "");
if (blk_parent == null) begin
`uvm_error("UVM/REG/CFG/NOBLK", {"uvm_reg::configure() called without a parent block for instance \"", get_name(), "\" of register type \"", get_type_name(), "\"."})
return;
end
m_parent = blk_parent;
m_parent.add_reg(this);
m_regfile_parent = regfile_parent;
if (hdl_path != "")
add_hdl_path_slice(hdl_path, -1, -1);
endfunction: configure
// add_field
function void uvm_reg::add_field(uvm_reg_field field);
int offset;
int idx;
if (m_locked) begin
`uvm_error("RegModel", "Cannot add field to locked register model")
return;
end
if (field == null) `uvm_fatal("RegModel", "Attempting to register NULL field")
// Store fields in LSB to MSB order
offset = field.get_lsb_pos();
idx = -1;
foreach (m_fields[i]) begin
if (offset < m_fields[i].get_lsb_pos()) begin
int j = i;
m_fields.insert(j, field);
idx = i;
break;
end
end
if (idx < 0) begin
m_fields.push_back(field);
idx = m_fields.size()-1;
end
m_n_used_bits += field.get_n_bits();
// Check if there are too many fields in the register
if (m_n_used_bits > m_n_bits) begin
`uvm_error("RegModel",
$sformatf("Fields use more bits (%0d) than available in register \"%s\" (%0d)",
m_n_used_bits, get_name(), m_n_bits))
end
// Check if there are overlapping fields
if (idx > 0) begin
if (m_fields[idx-1].get_lsb_pos() +
m_fields[idx-1].get_n_bits() > offset) begin
`uvm_error("RegModel", $sformatf("Field %s overlaps field %s in register \"%s\"",
m_fields[idx-1].get_name(),
field.get_name(), get_name()))
end
end
if (idx < m_fields.size()-1) begin
if (offset + field.get_n_bits() >
m_fields[idx+1].get_lsb_pos()) begin
`uvm_error("RegModel", $sformatf("Field %s overlaps field %s in register \"%s\"",
field.get_name(),
m_fields[idx+1].get_name(),
get_name()))
end
end
endfunction: add_field
// Xlock_modelX
function void uvm_reg::Xlock_modelX();
if (m_locked)
return;
m_locked = 1;
endfunction
//----------------------
// Group- User Frontdoor
//----------------------
// set_frontdoor
function void uvm_reg::set_frontdoor(uvm_reg_frontdoor ftdr,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
uvm_reg_map_info map_info;
ftdr.fname = m_fname;
ftdr.lineno = m_lineno;
map = get_local_map(map);
if (map == null)
return;
map_info = map.get_reg_map_info(this);
if (map_info == null)
map.add_reg(this, -1, "RW", 1, ftdr);
else begin
map_info.frontdoor = ftdr;
end
endfunction: set_frontdoor
// get_frontdoor
function uvm_reg_frontdoor uvm_reg::get_frontdoor(uvm_reg_map map = null);
uvm_reg_map_info map_info;
map = get_local_map(map);
if (map == null)
return null;
map_info = map.get_reg_map_info(this);
return map_info.frontdoor;
endfunction: get_frontdoor
// set_backdoor
function void uvm_reg::set_backdoor(uvm_reg_backdoor bkdr,
string fname = "",
int lineno = 0);
bkdr.fname = fname;
bkdr.lineno = lineno;
if (m_backdoor != null &&
m_backdoor.has_update_threads()) begin
`uvm_warning("RegModel", "Previous register backdoor still has update threads running. Backdoors with active mirroring should only be set before simulation starts.")
end
m_backdoor = bkdr;
endfunction: set_backdoor
// get_backdoor
function uvm_reg_backdoor uvm_reg::get_backdoor(bit inherited = 1);
if (m_backdoor == null && inherited) begin
uvm_reg_block blk = get_parent();
uvm_reg_backdoor bkdr;
while (blk != null) begin
bkdr = blk.get_backdoor();
if (bkdr != null) begin
m_backdoor = bkdr;
break;
end
blk = blk.get_parent();
end
end
return m_backdoor;
endfunction: get_backdoor
// clear_hdl_path
function void uvm_reg::clear_hdl_path(string kind = "RTL");
if (kind == "ALL") begin
m_hdl_paths_pool = new("hdl_paths");
return;
end
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
if (!m_hdl_paths_pool.exists(kind)) begin
`uvm_warning("RegModel",{"Unknown HDL Abstraction '",kind,"'"})
return;
end
m_hdl_paths_pool.delete(kind);
endfunction
// add_hdl_path
function void uvm_reg::add_hdl_path(uvm_hdl_path_slice slices[],
string kind = "RTL");
uvm_queue #(uvm_hdl_path_concat) paths = m_hdl_paths_pool.get(kind);
uvm_hdl_path_concat concat = new();
concat.set(slices);
paths.push_back(concat);
endfunction
// add_hdl_path_slice
function void uvm_reg::add_hdl_path_slice(string name,
int offset,
int size,
bit first = 0,
string kind = "RTL");
uvm_queue #(uvm_hdl_path_concat) paths = m_hdl_paths_pool.get(kind);
uvm_hdl_path_concat concat;
if (first || paths.size() == 0) begin
concat = new();
paths.push_back(concat);
end
else
concat = paths.get(paths.size()-1);
concat.add_path(name, offset, size);
endfunction
// has_hdl_path
function bit uvm_reg::has_hdl_path(string kind = "");
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
return m_hdl_paths_pool.exists(kind);
endfunction
// get_hdl_path_kinds
function void uvm_reg::get_hdl_path_kinds (ref string kinds[$]);
string kind;
kinds.delete();
if (!m_hdl_paths_pool.first(kind))
return;
do
kinds.push_back(kind);
while (m_hdl_paths_pool.next(kind));
endfunction
// get_hdl_path
function void uvm_reg::get_hdl_path(ref uvm_hdl_path_concat paths[$],
input string kind = "");
uvm_queue #(uvm_hdl_path_concat) hdl_paths;
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
if (!has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"Register does not have hdl path defined for abstraction '",kind,"'"})
return;
end
hdl_paths = m_hdl_paths_pool.get(kind);
for (int i=0; i<hdl_paths.size();i++) begin
paths.push_back(hdl_paths.get(i));
end
endfunction
// get_full_hdl_path
function void uvm_reg::get_full_hdl_path(ref uvm_hdl_path_concat paths[$],
input string kind = "",
input string separator = ".");
if (kind == "") begin
if (m_regfile_parent != null)
kind = m_regfile_parent.get_default_hdl_path();
else
kind = m_parent.get_default_hdl_path();
end
if (!has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"Register ",get_full_name()," does not have hdl path defined for abstraction '",kind,"'"})
return;
end
begin
uvm_queue #(uvm_hdl_path_concat) hdl_paths = m_hdl_paths_pool.get(kind);
string parent_paths[$];
if (m_regfile_parent != null)
m_regfile_parent.get_full_hdl_path(parent_paths, kind, separator);
else
m_parent.get_full_hdl_path(parent_paths, kind, separator);
for (int i=0; i<hdl_paths.size();i++) begin
uvm_hdl_path_concat hdl_concat = hdl_paths.get(i);
foreach (parent_paths[j]) begin
uvm_hdl_path_concat t = new;
foreach (hdl_concat.slices[k]) begin
if (hdl_concat.slices[k].path == "")
t.add_path(parent_paths[j]);
else
t.add_path({ parent_paths[j], separator, hdl_concat.slices[k].path },
hdl_concat.slices[k].offset,
hdl_concat.slices[k].size);
end
paths.push_back(t);
end
end
end
endfunction
// set_offset
function void uvm_reg::set_offset (uvm_reg_map map,
uvm_reg_addr_t offset,
bit unmapped = 0);
uvm_reg_map orig_map = map;
if (m_maps.num() > 1 && map == null) begin
`uvm_error("RegModel",{"set_offset requires a non-null map when register '",
get_full_name(),"' belongs to more than one map."})
return;
end
map = get_local_map(map);
if (map == null)
return;
map.m_set_reg_offset(this, offset, unmapped);
endfunction
// set_parent
function void uvm_reg::set_parent(uvm_reg_block blk_parent,
uvm_reg_file regfile_parent);
if (m_parent != null) begin
// ToDo: remove register from previous parent
end
m_parent = blk_parent;
m_regfile_parent = regfile_parent;
endfunction
// get_parent
function uvm_reg_block uvm_reg::get_parent();
return get_block();
endfunction
// get_regfile
function uvm_reg_file uvm_reg::get_regfile();
return m_regfile_parent;
endfunction
// get_full_name
function string uvm_reg::get_full_name();
if (m_regfile_parent != null)
return {m_regfile_parent.get_full_name(), ".", get_name()};
if (m_parent != null)
return {m_parent.get_full_name(), ".", get_name()};
return get_name();
endfunction: get_full_name
// add_map
function void uvm_reg::add_map(uvm_reg_map map);
m_maps[map] = 1;
endfunction
// get_maps
function void uvm_reg::get_maps(ref uvm_reg_map maps[$]);
foreach (m_maps[map])
maps.push_back(map);
endfunction
// get_n_maps
function int uvm_reg::get_n_maps();
return m_maps.num();
endfunction
// is_in_map
function bit uvm_reg::is_in_map(uvm_reg_map map);
if (m_maps.exists(map))
return 1;
foreach (m_maps[l]) begin
uvm_reg_map local_map = l;
uvm_reg_map parent_map = local_map.get_parent_map();
while (parent_map != null) begin
if (parent_map == map)
return 1;
parent_map = parent_map.get_parent_map();
end
end
return 0;
endfunction
// get_local_map
function uvm_reg_map uvm_reg::get_local_map(uvm_reg_map map);
if (map == null)
return get_default_map();
if (m_maps.exists(map))
return map;
foreach (m_maps[l]) begin
uvm_reg_map local_map=l;
uvm_reg_map parent_map = local_map.get_parent_map();
while (parent_map != null) begin
if (parent_map == map)
return local_map;
parent_map = parent_map.get_parent_map();
end
end
`uvm_warning("RegModel",
{"Register '",get_full_name(),"' is not contained within map '",map.get_full_name(),"'"})
return null;
endfunction
// get_default_map
function uvm_reg_map uvm_reg::get_default_map();
// if reg is not associated with any map, return ~null~
if (m_maps.num() == 0) begin
`uvm_warning("RegModel",
{"Register '",get_full_name(),"' is not registered with any map"})
return null;
end
// if only one map, choose that
if (m_maps.num() == 1) begin
uvm_reg_map map;
void'(m_maps.first(map));
return map;
end
// try to choose one based on default_map in parent blocks.
foreach (m_maps[l]) begin
uvm_reg_map map = l;
uvm_reg_block blk = map.get_parent();
uvm_reg_map default_map = blk.get_default_map();
if (default_map != null) begin
uvm_reg_map local_map = get_local_map(default_map);
if (local_map != null)
return local_map;
end
end
// if that fails, choose the first in this reg's maps
begin
uvm_reg_map map;
void'(m_maps.first(map));
return map;
end
endfunction
// get_rights
function string uvm_reg::get_rights(uvm_reg_map map = null);
uvm_reg_map_info info;
map = get_local_map(map);
if (map == null)
return "RW";
info = map.get_reg_map_info(this);
return info.rights;
endfunction
// get_block
function uvm_reg_block uvm_reg::get_block();
get_block = m_parent;
endfunction
// get_offset
function uvm_reg_addr_t uvm_reg::get_offset(uvm_reg_map map = null);
uvm_reg_map_info map_info;
uvm_reg_map orig_map = map;
map = get_local_map(map);
if (map == null)
return -1;
map_info = map.get_reg_map_info(this);
if (map_info.unmapped) begin
`uvm_warning("RegModel", {"Register '",get_name(),
"' is unmapped in map '",
((orig_map == null) ? map.get_full_name() : orig_map.get_full_name()),"'"})
return -1;
end
return map_info.offset;
endfunction
// get_addresses
function int uvm_reg::get_addresses(uvm_reg_map map=null, ref uvm_reg_addr_t addr[]);
uvm_reg_map_info map_info;
uvm_reg_map orig_map = map;
map = get_local_map(map);
if (map == null)
return -1;
map_info = map.get_reg_map_info(this);
if (map_info.unmapped) begin
`uvm_warning("RegModel", {"Register '",get_name(),
"' is unmapped in map '",
((orig_map == null) ? map.get_full_name() : orig_map.get_full_name()),"'"})
return -1;
end
addr = map_info.addr;
return map.get_n_bytes();
endfunction
// get_address
function uvm_reg_addr_t uvm_reg::get_address(uvm_reg_map map = null);
uvm_reg_addr_t addr[];
void'(get_addresses(map,addr));
return addr[0];
endfunction
// get_n_bits
function int unsigned uvm_reg::get_n_bits();
return m_n_bits;
endfunction
// get_n_bytes
function int unsigned uvm_reg::get_n_bytes();
return ((m_n_bits-1) / 8) + 1;
endfunction
// get_max_size
function int unsigned uvm_reg::get_max_size();
return m_max_size;
endfunction: get_max_size
// get_fields
function void uvm_reg::get_fields(ref uvm_reg_field fields[$]);
foreach(m_fields[i])
fields.push_back(m_fields[i]);
endfunction
// get_field_by_name
function uvm_reg_field uvm_reg::get_field_by_name(string name);
foreach (m_fields[i])
if (m_fields[i].get_name() == name)
return m_fields[i];
`uvm_warning("RegModel", {"Unable to locate field '",name,
"' in register '",get_name(),"'"})
return null;
endfunction
// Xget_field_accessX
//
// Returns "WO" if all of the fields in the registers are write-only
// Returns "RO" if all of the fields in the registers are read-only
// Returns "RW" otherwise.
function string uvm_reg::Xget_fields_accessX(uvm_reg_map map);
bit is_R;
bit is_W;
foreach(m_fields[i]) begin
case (m_fields[i].get_access(map))
"RO",
"RC",
"RS":
is_R = 1;
"WO",
"WOC",
"WOS",
"WO1":
is_W = 1;
default:
return "RW";
endcase
if (is_R && is_W) return "RW";
end
case ({is_R, is_W})
2'b01: return "WO";
2'b10: return "RO";
endcase
return "RW";
endfunction
//---------
// COVERAGE
//---------
// include_coverage
function void uvm_reg::include_coverage(string scope,
uvm_reg_cvr_t models,
uvm_object accessor = null);
uvm_reg_cvr_rsrc_db::set({"uvm_reg::", scope},
"include_coverage",
models, accessor);
endfunction
// build_coverage
function uvm_reg_cvr_t uvm_reg::build_coverage(uvm_reg_cvr_t models);
build_coverage = UVM_NO_COVERAGE;
void'(uvm_reg_cvr_rsrc_db::read_by_name({"uvm_reg::", get_full_name()},
"include_coverage",
build_coverage, this));
return build_coverage & models;
endfunction: build_coverage
// add_coverage
function void uvm_reg::add_coverage(uvm_reg_cvr_t models);
m_has_cover |= models;
endfunction: add_coverage
// has_coverage
function bit uvm_reg::has_coverage(uvm_reg_cvr_t models);
return ((m_has_cover & models) == models);
endfunction: has_coverage
// set_coverage
function uvm_reg_cvr_t uvm_reg::set_coverage(uvm_reg_cvr_t is_on);
if (is_on == uvm_reg_cvr_t'(UVM_NO_COVERAGE)) begin
m_cover_on = is_on;
return m_cover_on;
end
m_cover_on = m_has_cover & is_on;
return m_cover_on;
endfunction: set_coverage
// get_coverage
function bit uvm_reg::get_coverage(uvm_reg_cvr_t is_on);
if (has_coverage(is_on) == 0)
return 0;
return ((m_cover_on & is_on) == is_on);
endfunction: get_coverage
//---------
// ACCESS
//---------
// set
function void uvm_reg::set(uvm_reg_data_t value,
string fname = "",
int lineno = 0);
// Split the value into the individual fields
m_fname = fname;
m_lineno = lineno;
foreach (m_fields[i])
m_fields[i].set((value >> m_fields[i].get_lsb_pos()) &
((1 << m_fields[i].get_n_bits()) - 1));
endfunction: set
// predict
function bit uvm_reg::predict (uvm_reg_data_t value,
uvm_reg_byte_en_t be = -1,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_door_e path = UVM_FRONTDOOR,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
uvm_reg_item rw = new;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.fname = fname;
rw.lineno = lineno;
do_predict(rw, kind, be);
predict = (rw.status == UVM_NOT_OK) ? 0 : 1;
endfunction: predict
// do_predict
function void uvm_reg::do_predict(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
uvm_reg_data_t reg_value = rw.value[0];
m_fname = rw.fname;
m_lineno = rw.lineno;
if (rw.status ==UVM_IS_OK )
rw.status = UVM_IS_OK;
if (m_is_busy && kind == UVM_PREDICT_DIRECT) begin
`uvm_warning("RegModel", {"Trying to predict value of register '",
get_full_name(),"' while it is being accessed"})
rw.status = UVM_NOT_OK;
return;
end
foreach (m_fields[i]) begin
rw.value[0] = (reg_value >> m_fields[i].get_lsb_pos()) &
((1 << m_fields[i].get_n_bits())-1);
m_fields[i].do_predict(rw, kind, be>>(m_fields[i].get_lsb_pos()/8));
end
rw.value[0] = reg_value;
endfunction: do_predict
// get
function uvm_reg_data_t uvm_reg::get(string fname = "",
int lineno = 0);
// Concatenate the value of the individual fields
// to form the register value
m_fname = fname;
m_lineno = lineno;
get = 0;
foreach (m_fields[i])
get |= m_fields[i].get() << m_fields[i].get_lsb_pos();
endfunction: get
// get_mirrored_value
function uvm_reg_data_t uvm_reg::get_mirrored_value(string fname = "",
int lineno = 0);
// Concatenate the value of the individual fields
// to form the register value
m_fname = fname;
m_lineno = lineno;
get_mirrored_value = 0;
foreach (m_fields[i])
get_mirrored_value |= m_fields[i].get_mirrored_value() << m_fields[i].get_lsb_pos();
endfunction: get_mirrored_value
// reset
function void uvm_reg::reset(string kind = "HARD");
foreach (m_fields[i])
m_fields[i].reset(kind);
// Put back a key in the semaphore if it is checked out
// in case a thread was killed during an operation
void'(m_atomic.try_get(1));
m_atomic.put(1);
m_process = null;
Xset_busyX(0);
endfunction: reset
// get_reset
function uvm_reg_data_t uvm_reg::get_reset(string kind = "HARD");
// Concatenate the value of the individual fields
// to form the register value
get_reset = 0;
foreach (m_fields[i])
get_reset |= m_fields[i].get_reset(kind) << m_fields[i].get_lsb_pos();
endfunction: get_reset
// has_reset
function bit uvm_reg::has_reset(string kind = "HARD",
bit delete = 0);
has_reset = 0;
foreach (m_fields[i]) begin
has_reset |= m_fields[i].has_reset(kind, delete);
if (!delete && has_reset)
return 1;
end
endfunction: has_reset
// set_reset
function void uvm_reg::set_reset(uvm_reg_data_t value,
string kind = "HARD");
foreach (m_fields[i]) begin
m_fields[i].set_reset(value >> m_fields[i].get_lsb_pos(), kind);
end
endfunction: set_reset
//-----------
// BUS ACCESS
//-----------
// needs_update
function bit uvm_reg::needs_update();
needs_update = 0;
foreach (m_fields[i]) begin
if (m_fields[i].needs_update()) begin
return 1;
end
end
endfunction: needs_update
// update
task uvm_reg::update(output uvm_status_e status,
input uvm_door_e path = UVM_DEFAULT_DOOR,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_data_t upd;
status = UVM_IS_OK;
if (!needs_update()) return;
// Concatenate the write-to-update values from each field
// Fields are stored in LSB or MSB order
upd = 0;
foreach (m_fields[i])
upd |= m_fields[i].XupdateX() << m_fields[i].get_lsb_pos();
write(status, upd, path, map, parent, prior, extension, fname, lineno);
endtask: update
// write
task uvm_reg::write(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_door_e path = UVM_DEFAULT_DOOR,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// create an abstract transaction for this operation
uvm_reg_item rw;
XatomicX(1);
set(value);
rw = uvm_reg_item::type_id::create("write_item",,get_full_name());
rw.element = this;
rw.element_kind = UVM_REG;
rw.kind = UVM_WRITE;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.parent = parent;
rw.prior = prior;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
do_write(rw);
status = rw.status;
XatomicX(0);
endtask
// do_write
task uvm_reg::do_write (uvm_reg_item rw);
uvm_reg_cb_iter cbs = new(this);
uvm_reg_map_info map_info;
uvm_reg_data_t value;
m_fname = rw.fname;
m_lineno = rw.lineno;
if (!Xcheck_accessX(rw,map_info))
return;
XatomicX(1);
m_write_in_progress = 1'b1;
rw.value[0] &= ((1 << m_n_bits)-1);
value = rw.value[0];
rw.status = UVM_IS_OK;
// PRE-WRITE CBS - FIELDS
begin : pre_write_callbacks
uvm_reg_data_t msk;
int lsb;
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
lsb = f.get_lsb_pos();
msk = ((1<<f.get_n_bits())-1) << lsb;
rw.value[0] = (value & msk) >> lsb;
f.pre_write(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next()) begin
rw.element = f;
rw.element_kind = UVM_FIELD;
cb.pre_write(rw);
end
value = (value & ~msk) | (rw.value[0] << lsb);
end
end
rw.element = this;
rw.element_kind = UVM_REG;
rw.value[0] = value;
// PRE-WRITE CBS - REG
pre_write(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.pre_write(rw);
if (rw.status != UVM_IS_OK) begin
m_write_in_progress = 1'b0;
XatomicX(0);
return;
end
// EXECUTE WRITE...
case (rw.path)
// ...VIA USER BACKDOOR
UVM_BACKDOOR: begin
uvm_reg_data_t final_val;
uvm_reg_backdoor bkdr = get_backdoor();
if (rw.map != null)
rw.local_map = rw.map;
else
rw.local_map = get_default_map();
value = rw.value[0];
// Mimick the final value after a physical read
rw.kind = UVM_READ;
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
if (rw.status == UVM_NOT_OK) begin
m_write_in_progress = 1'b0;
return;
end
begin
foreach (m_fields[i]) begin
uvm_reg_data_t field_val;
int lsb = m_fields[i].get_lsb_pos();
int sz = m_fields[i].get_n_bits();
field_val = m_fields[i].XpredictX((rw.value[0] >> lsb) & ((1<<sz)-1),
(value >> lsb) & ((1<<sz)-1),
rw.local_map);
final_val |= field_val << lsb;
end
end
rw.kind = UVM_WRITE;
rw.value[0] = final_val;
if (get_rights(rw.local_map) inside {"RW", "WO"}) begin
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
do_predict(rw, UVM_PREDICT_WRITE);
end
else begin
rw.status = UVM_NOT_OK;
end
end
UVM_FRONTDOOR: begin
uvm_reg_map system_map = rw.local_map.get_root_map();
m_is_busy = 1;
// ...VIA USER FRONTDOOR
if (map_info.frontdoor != null) begin
uvm_reg_frontdoor fd = map_info.frontdoor;
fd.rw_info = rw;
if (fd.sequencer == null)
fd.sequencer = system_map.get_sequencer();
fd.start(fd.sequencer, rw.parent);
end
// ...VIA BUILT-IN FRONTDOOR
else begin : built_in_frontdoor
rw.local_map.do_write(rw);
end
m_is_busy = 0;
if (system_map.get_auto_predict()) begin
uvm_status_e status;
if (rw.status != UVM_NOT_OK) begin
sample(value, -1, 0, rw.map);
m_parent.XsampleX(map_info.offset, 0, rw.map);
end
status = rw.status; // do_predict will override rw.status, so we save it here
do_predict(rw, UVM_PREDICT_WRITE);
rw.status = status;
end
end
endcase
value = rw.value[0];
// POST-WRITE CBS - REG
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_write(rw);
post_write(rw);
// POST-WRITE CBS - FIELDS
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
rw.element = f;
rw.element_kind = UVM_FIELD;
rw.value[0] = (value >> f.get_lsb_pos()) & ((1<<f.get_n_bits())-1);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_write(rw);
f.post_write(rw);
end
rw.value[0] = value;
rw.element = this;
rw.element_kind = UVM_REG;
// REPORT
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "RegModel")) begin
string path_s,value_s;
if (rw.path == UVM_FRONTDOOR)
path_s = (map_info.frontdoor != null) ? "user frontdoor" :
{"map ",rw.map.get_full_name()};
else
path_s = (get_backdoor() != null) ? "user backdoor" : "DPI backdoor";
value_s = $sformatf("=0x%0h",rw.value[0]);
uvm_report_info("RegModel", {"Wrote register via ",path_s,": ",
get_full_name(),value_s}, UVM_HIGH);
end
m_write_in_progress = 1'b0;
XatomicX(0);
endtask: do_write
// read
task uvm_reg::read(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_door_e path = UVM_DEFAULT_DOOR,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
XatomicX(1);
XreadX(status, value, path, map, parent, prior, extension, fname, lineno);
XatomicX(0);
endtask: read
// XreadX
task uvm_reg::XreadX(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_door_e path,
input uvm_reg_map map,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// create an abstract transaction for this operation
uvm_reg_item rw;
rw = uvm_reg_item::type_id::create("read_item",,get_full_name());
rw.element = this;
rw.element_kind = UVM_REG;
rw.kind = UVM_READ;
rw.value[0] = 0;
rw.path = path;
rw.map = map;
rw.parent = parent;
rw.prior = prior;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
do_read(rw);
status = rw.status;
value = rw.value[0];
endtask: XreadX
// do_read
task uvm_reg::do_read(uvm_reg_item rw);
uvm_reg_cb_iter cbs = new(this);
uvm_reg_map_info map_info;
uvm_reg_data_t value;
uvm_reg_data_t exp;
m_fname = rw.fname;
m_lineno = rw.lineno;
if (!Xcheck_accessX(rw,map_info))
return;
m_read_in_progress = 1'b1;
rw.status = UVM_IS_OK;
// PRE-READ CBS - FIELDS
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
rw.element = f;
rw.element_kind = UVM_FIELD;
m_fields[i].pre_read(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.pre_read(rw);
end
rw.element = this;
rw.element_kind = UVM_REG;
// PRE-READ CBS - REG
pre_read(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.pre_read(rw);
if (rw.status != UVM_IS_OK) begin
m_read_in_progress = 1'b0;
return;
end
// EXECUTE READ...
case (rw.path)
// ...VIA USER BACKDOOR
UVM_BACKDOOR: begin
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_map map; // = uvm_reg_map::backdoor();
if (rw.map != null)
rw.local_map = rw.map;
else
rw.local_map = get_default_map();
map = rw.local_map;
if (map.get_check_on_read()) exp = get();
if (get_rights(rw.local_map) inside {"RW", "RO"}) begin
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
end
else begin
rw.status = UVM_NOT_OK;
end
value = rw.value[0];
// Need to clear RC fields, set RS fields and mask WO fields
if (rw.status != UVM_NOT_OK) begin
uvm_reg_data_t wo_mask;
foreach (m_fields[i]) begin
// string acc = m_fields[i].get_access(uvm_reg_map::backdoor());
string acc = m_fields[i].get_access(rw.local_map);
if (acc == "RC" ||
acc == "WRC" ||
acc == "WSRC" ||
acc == "W1SRC" ||
acc == "W0SRC") begin
value &= ~(((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos());
end
else if (acc == "RS" ||
acc == "WRS" ||
acc == "WCRS" ||
acc == "W1CRS" ||
acc == "W0CRS") begin
value |= (((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos());
end
else if (acc == "WO" ||
acc == "WOC" ||
acc == "WOS" ||
acc == "WO1") begin
wo_mask |= ((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos();
end
end
if (get_rights(rw.local_map) inside {"RW", "RO"}) begin
if (value != rw.value[0]) begin
uvm_reg_data_t saved;
saved = rw.value[0];
rw.value[0] = value;
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
rw.value[0] = saved;
end
rw.value[0] &= ~wo_mask;
if (map.get_check_on_read() &&
rw.status != UVM_NOT_OK) begin
void'(do_check(exp, rw.value[0], map));
end
do_predict(rw, UVM_PREDICT_READ);
end
else begin
rw.status = UVM_NOT_OK;
end
end
end
UVM_FRONTDOOR: begin
uvm_reg_map system_map = rw.local_map.get_root_map();
m_is_busy = 1;
if (rw.local_map.get_check_on_read()) exp = get();
// ...VIA USER FRONTDOOR
if (map_info.frontdoor != null) begin
uvm_reg_frontdoor fd = map_info.frontdoor;
fd.rw_info = rw;
if (fd.sequencer == null)
fd.sequencer = system_map.get_sequencer();
fd.start(fd.sequencer, rw.parent);
end
// ...VIA BUILT-IN FRONTDOOR
else begin
rw.local_map.do_read(rw);
end
m_is_busy = 0;
if (system_map.get_auto_predict()) begin
uvm_status_e status;
if (rw.local_map.get_check_on_read() &&
rw.status != UVM_NOT_OK) begin
void'(do_check(exp, rw.value[0], system_map));
end
if (rw.status != UVM_NOT_OK) begin
sample(rw.value[0], -1, 1, rw.map);
m_parent.XsampleX(map_info.offset, 1, rw.map);
end
status = rw.status; // do_predict will override rw.status, so we save it here
do_predict(rw, UVM_PREDICT_READ);
rw.status = status;
end
end
endcase
value = rw.value[0]; // preserve
// POST-READ CBS - REG
for (uvm_reg_cbs cb = cbs.first(); cb != null; cb = cbs.next())
cb.post_read(rw);
post_read(rw);
// POST-READ CBS - FIELDS
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
rw.element = f;
rw.element_kind = UVM_FIELD;
rw.value[0] = (value >> f.get_lsb_pos()) & ((1<<f.get_n_bits())-1);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.post_read(rw);
f.post_read(rw);
end
rw.value[0] = value; // restore
rw.element = this;
rw.element_kind = UVM_REG;
// REPORT
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "RegModel")) begin
string path_s,value_s;
if (rw.path == UVM_FRONTDOOR)
path_s = (map_info.frontdoor != null) ? "user frontdoor" :
{"map ",rw.map.get_full_name()};
else
path_s = (get_backdoor() != null) ? "user backdoor" : "DPI backdoor";
value_s = $sformatf("=%0h",rw.value[0]);
uvm_report_info("RegModel", {"Read register via ",path_s,": ",
get_full_name(),value_s}, UVM_HIGH);
end
m_read_in_progress = 1'b0;
endtask: do_read
// Xcheck_accessX
function bit uvm_reg::Xcheck_accessX (input uvm_reg_item rw,
output uvm_reg_map_info map_info);
if (rw.path == UVM_DEFAULT_DOOR)
rw.path = m_parent.get_default_door();
if (rw.path == UVM_BACKDOOR) begin
if (get_backdoor() == null && !has_hdl_path()) begin
`uvm_warning("RegModel",
{"No backdoor access available for register '",get_full_name(),
"' . Using frontdoor instead."})
rw.path = UVM_FRONTDOOR;
end
else if (rw.map == null) begin
uvm_reg_map bkdr_map = get_default_map();
if (bkdr_map != null)
rw.map = bkdr_map;
else
rw.map = uvm_reg_map::backdoor();
end
end
if (rw.path != UVM_BACKDOOR) begin
rw.local_map = get_local_map(rw.map);
if (rw.local_map == null) begin
if (rw.map == null)
`uvm_error(get_type_name(), "Unable to physically access register with null map")
else
`uvm_error(get_type_name(),
{"No transactor available to physically access register on map '",
rw.map.get_full_name(),"'"})
rw.status = UVM_NOT_OK;
return 0;
end
map_info = rw.local_map.get_reg_map_info(this);
if (map_info.frontdoor == null && map_info.unmapped) begin
`uvm_error("RegModel", {"Register '",get_full_name(),
"' unmapped in map '",
(rw.map==null)? rw.local_map.get_full_name():rw.map.get_full_name(),
"' and does not have a user-defined frontdoor"})
rw.status = UVM_NOT_OK;
return 0;
end
if (rw.map == null)
rw.map = rw.local_map;
end
return 1;
endfunction
// is_busy
function bit uvm_reg::is_busy();
return m_is_busy;
endfunction
// Xset_busyX
function void uvm_reg::Xset_busyX(bit busy);
m_is_busy = busy;
endfunction
// Xis_loacked_by_fieldX
function bit uvm_reg::Xis_locked_by_fieldX();
return m_is_locked_by_field;
endfunction
// backdoor_write
task uvm_reg::backdoor_write(uvm_reg_item rw);
uvm_hdl_path_concat paths[$];
bit ok=1;
get_full_hdl_path(paths,rw.bd_kind);
foreach (paths[i]) begin
uvm_hdl_path_concat hdl_concat = paths[i];
foreach (hdl_concat.slices[j]) begin
`uvm_info("RegMem", $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,rw.value[0]);
continue;
end
begin
uvm_reg_data_t slice;
slice = rw.value[0] >> hdl_concat.slices[j].offset;
slice &= (1 << hdl_concat.slices[j].size)-1;
ok &= uvm_hdl_deposit(hdl_concat.slices[j].path, slice);
end
end
end
rw.status = (ok ? UVM_IS_OK : UVM_NOT_OK);
endtask
// backdoor_read
task uvm_reg::backdoor_read (uvm_reg_item rw);
rw.status = backdoor_read_func(rw);
endtask
// backdoor_read_func
function uvm_status_e uvm_reg::backdoor_read_func(uvm_reg_item rw);
uvm_hdl_path_concat paths[$];
uvm_reg_data_t val;
bit ok=1;
get_full_hdl_path(paths,rw.bd_kind);
foreach (paths[i]) begin
uvm_hdl_path_concat hdl_concat = paths[i];
val = 0;
foreach (hdl_concat.slices[j]) begin
`uvm_info("RegMem", $sformatf("backdoor_read from %s ",
hdl_concat.slices[j].path),UVM_DEBUG)
if (hdl_concat.slices[j].offset < 0) begin
ok &= uvm_hdl_read(hdl_concat.slices[j].path,val);
continue;
end
begin
uvm_reg_data_t slice;
int k = hdl_concat.slices[j].offset;
ok &= uvm_hdl_read(hdl_concat.slices[j].path, slice);
repeat (hdl_concat.slices[j].size) begin
val[k++] = slice[0];
slice >>= 1;
end
end
end
val &= (1 << m_n_bits)-1;
if (i == 0)
rw.value[0] = val;
if (val != rw.value[0]) begin
`uvm_error("RegModel", $sformatf("Backdoor read of register %s with multiple HDL copies: values are not the same: %0h at path '%s', and %0h at path '%s'. Returning first value.",
get_full_name(),
rw.value[0], uvm_hdl_concat2string(paths[0]),
val, uvm_hdl_concat2string(paths[i])))
return UVM_NOT_OK;
end
`uvm_info("RegMem",
$sformatf("returned backdoor value 0x%0x",rw.value[0]),UVM_DEBUG)
end
rw.status = (ok) ? UVM_IS_OK : UVM_NOT_OK;
return rw.status;
endfunction
// poke
task uvm_reg::poke(output uvm_status_e status,
input uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_item rw;
m_fname = fname;
m_lineno = lineno;
if (bkdr == null && !has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"No backdoor access available to poke register '",get_full_name(),"'"})
status = UVM_NOT_OK;
return;
end
if (!m_is_locked_by_field)
XatomicX(1);
// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("reg_poke_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_REG;
rw.kind = UVM_WRITE;
rw.bd_kind = kind;
rw.value[0] = value & ((1 << m_n_bits)-1);
rw.parent = parent;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
status = rw.status;
`uvm_info("RegModel", $sformatf("Poked register \"%s\": 'h%h",
get_full_name(), value),UVM_HIGH)
do_predict(rw, UVM_PREDICT_WRITE);
if (!m_is_locked_by_field)
XatomicX(0);
endtask: poke
// peek
task uvm_reg::peek(output uvm_status_e status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_item rw;
m_fname = fname;
m_lineno = lineno;
if (bkdr == null && !has_hdl_path(kind)) begin
`uvm_error("RegModel",
$sformatf("No backdoor access available to peek register \"%s\"",
get_full_name()))
status = UVM_NOT_OK;
return;
end
if(!m_is_locked_by_field)
XatomicX(1);
// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("mem_peek_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_REG;
rw.kind = UVM_READ;
rw.bd_kind = kind;
rw.parent = parent;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
status = rw.status;
value = rw.value[0];
`uvm_info("RegModel", $sformatf("Peeked register \"%s\": 'h%h",
get_full_name(), value),UVM_HIGH)
do_predict(rw, UVM_PREDICT_READ);
if (!m_is_locked_by_field)
XatomicX(0);
endtask: peek
// do_check
function bit uvm_reg::do_check(input uvm_reg_data_t expected,
input uvm_reg_data_t actual,
uvm_reg_map map);
uvm_reg_data_t valid_bits_mask = 0; // elements 1 indicating bit we care about
foreach(m_fields[i]) begin
string acc = m_fields[i].get_access(map);
acc = acc.substr(0, 1);
if (!(m_fields[i].get_compare() == UVM_NO_CHECK ||acc == "WO")) begin
valid_bits_mask |= ((1 << m_fields[i].get_n_bits())-1)<< m_fields[i].get_lsb_pos();
end
end
if ((actual&valid_bits_mask) === (expected&valid_bits_mask)) return 1;
`uvm_error("RegModel", $sformatf("Register \"%s\" value read from DUT (0x%h) does not match mirrored value (0x%h) (valid bit mask = 0x%h)",
get_full_name(), actual, expected,valid_bits_mask))
foreach(m_fields[i]) begin
string acc = m_fields[i].get_access(map);
acc = acc.substr(0, 1);
if (!(m_fields[i].get_compare() == UVM_NO_CHECK ||
acc == "WO")) begin
uvm_reg_data_t mask = ((1 << m_fields[i].get_n_bits())-1);
uvm_reg_data_t val = actual >> m_fields[i].get_lsb_pos() & mask;
uvm_reg_data_t exp = expected >> m_fields[i].get_lsb_pos() & mask;
if (val !== exp) begin
`uvm_info("RegModel",
$sformatf("Field %s (%s[%0d:%0d]) mismatch read=%0d'h%0h mirrored=%0d'h%0h ",
m_fields[i].get_name(), get_full_name(),
m_fields[i].get_lsb_pos() + m_fields[i].get_n_bits() - 1,
m_fields[i].get_lsb_pos(),
m_fields[i].get_n_bits(), val,
m_fields[i].get_n_bits(), exp),
UVM_NONE)
end
end
end
return 0;
endfunction
// mirror
task uvm_reg::mirror(output uvm_status_e status,
input uvm_check_e check = UVM_NO_CHECK,
input uvm_door_e path = UVM_DEFAULT_DOOR,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_data_t v;
uvm_reg_data_t exp;
uvm_reg_backdoor bkdr = get_backdoor();
XatomicX(1);
m_fname = fname;
m_lineno = lineno;
if (path == UVM_DEFAULT_DOOR)
path = m_parent.get_default_door();
if (path == UVM_BACKDOOR && (bkdr != null || has_hdl_path()))
map = uvm_reg_map::backdoor();
else
map = get_local_map(map);
if (map == null)
return;
// Remember what we think the value is before it gets updated
if (check == UVM_CHECK)
exp = get_mirrored_value();
XreadX(status, v, path, map, parent, prior, extension, fname, lineno);
if (status == UVM_NOT_OK) begin
XatomicX(0);
return;
end
if (check == UVM_CHECK) void'(do_check(exp, v, map));
XatomicX(0);
endtask: mirror
// XatomicX
task uvm_reg::XatomicX(bit on);
process m_reg_process;
m_reg_process=process::self();
if (on) begin
if (m_reg_process == m_process)
return;
m_atomic.get(1);
m_process = m_reg_process;
end
else begin
// Maybe a key was put back in by a spurious call to reset()
void'(m_atomic.try_get(1));
m_atomic.put(1);
m_process = null;
end
endtask: XatomicX
//-------------
// STANDARD OPS
//-------------
// convert2string
function string uvm_reg::convert2string();
string res_str;
string t_str;
bit with_debug_info;
string prefix;
$sformat(convert2string, "Register %s -- %0d bytes, mirror value:'h%h",
get_full_name(), get_n_bytes(),get());
if (m_maps.num()==0)
convert2string = {convert2string, " (unmapped)\n"};
else
convert2string = {convert2string, "\n"};
foreach (m_maps[map]) begin
uvm_reg_map parent_map = map;
int unsigned offset;
while (parent_map != null) begin
uvm_reg_map this_map = parent_map;
parent_map = this_map.get_parent_map();
offset = parent_map == null ? this_map.get_base_addr(UVM_NO_HIER) :
parent_map.get_submap_offset(this_map);
prefix = {prefix, " "};
begin
uvm_endianness_e e = this_map.get_endian();
$sformat(convert2string,
"%sMapped in '%s' -- %d bytes, %s, offset 'h%0h\n",
prefix, this_map.get_full_name(), this_map.get_n_bytes(),
e.name(), offset);
end
end
end
prefix = " ";
foreach(m_fields[i]) begin
$sformat(convert2string, "%s\n%s", convert2string,
m_fields[i].convert2string());
end
if (m_read_in_progress == 1'b1) begin
if (m_fname != "" && m_lineno != 0)
$sformat(res_str, "%s:%0d ",m_fname, m_lineno);
convert2string = {convert2string, "\n", res_str,
"currently executing read method"};
end
if ( m_write_in_progress == 1'b1) begin
if (m_fname != "" && m_lineno != 0)
$sformat(res_str, "%s:%0d ",m_fname, m_lineno);
convert2string = {convert2string, "\n", res_str,
"currently executing write method"};
end
endfunction: convert2string
// do_print
function void uvm_reg::do_print (uvm_printer printer);
uvm_reg_field f[$];
super.do_print(printer);
get_fields(f);
foreach(f[i]) printer.print_generic(f[i].get_name(),f[i].get_type_name(),-2,f[i].convert2string());
endfunction
// clone
function uvm_object uvm_reg::clone();
`uvm_fatal("RegModel","RegModel registers cannot be cloned")
return null;
endfunction
// do_copy
function void uvm_reg::do_copy(uvm_object rhs);
`uvm_fatal("RegModel","RegModel registers cannot be copied")
endfunction
// do_compare
function bit uvm_reg::do_compare (uvm_object rhs,
uvm_comparer comparer);
`uvm_warning("RegModel","RegModel registers cannot be compared")
return 0;
endfunction
// do_pack
function void uvm_reg::do_pack (uvm_packer packer);
`uvm_warning("RegModel","RegModel registers cannot be packed")
endfunction
// do_unpack
function void uvm_reg::do_unpack (uvm_packer packer);
`uvm_warning("RegModel","RegModel registers cannot be unpacked")
endfunction