Jump to content

Missing get methods in uvm_reg_field for local members; hard to extend the class...


Recommended Posts

Hi,

In our company we have been using our own scripts since 2005 to parse the functional specification (FS) of the registers and to generate the registers (it used to be RVM, not we changed the script to generate UVM code). The register access masks in our FS are slightly different than the UVM built-in ones. Other than changing our names to match the UVM ones, there are 2 different types of accesses for which we need to change the default implementation:

- register fields defined as sticky (a soft reset applied to it, would make it preserve its current value and not reset it to the power-on-reset value; a hard reset would reset it to the power-on-reset value)

- RE fields: act as "RW" before boot complete, act as "RO" afterwards (this is different than built-in "W1" documented as "Changed to written value if this is the first write operation after a hard reset. Otherwise, has no effect." because if the first write happens after boot_complete it should not change the field value.

I have extended the uvm_reg_field class into an ivm_reg_field class and reimplemented the methods that needed to be changed (code is shown lower in the e-mail). Our script generates the registers with the register fields extended from ivm_reg_field instead of uvm_reg_field.

I found that the uvm_reg_field class is missing get methods for the local members that would allow them to be changed in extensions (m_desired in one case) and that the base class code is not consistent in implementing the case statements related to m_access: when get_access() was used to return the m_access value, my extension worked because of reimplementing get_access() to support the new access type, but if the base class was using case(m_access), that was a big problem.

The set() in my extension works because of happening to default to some behavior in the base class, otherwise with missing ways to access the local members in the base class it would not have been possible to reimplement it.

Here is the code with comments:

class ivm_reg_field extends uvm_reg_field;

local bit m_sticky; ///< A reset with kind "SOFT" will not change the register field if m_sticky is set.

bit boot_complete; ///< Use resource database to get the value; if not successful, assume it is 1

`uvm_object_utils(ivm_reg_field)

//--------------

// Function: new

//--------------

// Create a new field instance

//

extern function new(string name = "ivm_reg_field");

//--------------------

// Function: configure - added support for sticky

//--------------------

// Has one extra parameter "is_sticky", by default set to 0,

// which is used to set the local member m_sticky.

// Calls super.configure()

// Also, needs to set the new "RE" access mask before executing

// the parent's configure which prints an error if the mask is not

// already inside the policy_names: if (!m_policy_names.exists(m_access))->uvm error

extern function void configure(uvm_reg parent,

int unsigned size,

int unsigned lsb_pos,

string access,

bit volatile,

uvm_reg_data_t reset,

bit has_reset,

bit is_rand,

bit individually_accessible,

bit is_sticky = 0); // set field stickiness (default non-sticky)

//---------------------

// Function: get_sticky

//---------------------

// Returns the value of the m_sticky field (1 - sticky, 0 - not sticky (default))

//

extern virtual function bit get_sticky();

//---------------------

// Function: get_access - added support for "RE" access mask

//---------------------

// Get the access policy of the field

//

// Base class does not know how to handle "RE" fields.

// For "RE" fields get_access will return "RW" when boot_complete == 0 or

// "RO" when boot_complete == 1 (or when the map is not RW)

//

extern virtual function string get_access(uvm_reg_map map = null);

//--------------------------------------------

// Function: XupdateX - no need to reimplement

//--------------------------------------------

// For m_access == "RE" the default case is executed: XupdateX = m_desired;

// Same result would have been optained if the function would have been

// called for "RW" or "RO" - the 2 behaviors of the "RE" field.

// case (m_access)

// "RO": XupdateX = m_desired;

// "RW": XupdateX = m_desired;

// ...

// default: XupdateX = m_desired;

// endcase

// extern virtual function uvm_reg_data_t XupdateX();

//--------------------------------------------------

// Function: set - does not support m_access == "RE"

//--------------------------------------------------

// Set the desired value for this field

//

// Sets the desired value of the field to the specified value.

// Does not actually set the value of the field in the design,

// only the desired value in the abstrcation class.

// Use the <uvm_reg::update()> method to update the actual register

// with the desired value or the <uvm_reg_field::write()> method

// to actually write the field and update its mirrored value.

//

// The base class implementation executes among other masks:

//

// case (m_access)

// "RO": m_desired = m_desired;

// "RW": m_desired = value;

// ...

// default: m_desired = value;

// endcase

// For "RE", the correct "RO" or "RW" behavior has to be selected first and

// this is done by calling first super.get_mask().

extern virtual function void set(uvm_reg_data_t value,

string fname = "",

int lineno = 0);

//----------------

// Function: reset

//----------------

// Reset the desired/mirrored value for this field.

//

// Base class does not know how to handle "SOFT" reset for sticky fields.

// "SOFT" reset applied on sticky fields, does not reset the field.

// "SOFT" reset applied to non-sticky fields, acts like "HARD" reset.

// "HARD" reset applied to sticky fields, resets the field.

//

extern virtual function void reset(string kind = "HARD");

endclass: ivm_reg_field

//------------------------------------------------------------------------------

// IMPLEMENTATION

//------------------------------------------------------------------------------

// new

function ivm_reg_field::new(string name = "ivm_reg_field");

super.new(name);

endfunction: new

// configure

function void ivm_reg_field::configure(uvm_reg parent,

int unsigned size,

int unsigned lsb_pos,

string access,

bit volatile,

uvm_reg_data_t reset,

bit has_reset,

bit is_rand,

bit individually_accessible,

bit is_sticky = 0);

bit result_define_new_access;

// need to add new access mask "RE" before parent code executes because

// it prints an error if the access mask does not exist

if(access == "RE")

begin

result_define_new_access = this.define_access(access);

if(result_define_new_access == 1) begin

`uvm_info("RegModel",

$psprintf("Successful adding adding new access mask: %0s",

access), UVM_LOW);

end else begin

`uvm_info("RegModel",

$psprintf("Not successful adding adding new access mask: %0s",

access), UVM_LOW);

end

end

super.configure(parent, size, lsb_pos, access, volatile, reset, has_reset, is_rand, individually_accessible);

// TODO: look at the end of super.configure and decide if

// is_rand should be set to 0 and value.rand_mode(0); for "RE" mask

// - currently for "RE" value.rand_mode() is based on is_rand input parameter

m_sticky = is_sticky;

// Use the resource database to get a handle to boot_complete

// If there is no resource with that name, set boot_complete to 1

if(!(uvm_resource_db#(bit)::read_by_name("*", "boot_complete",

boot_complete, this)))

begin

// the error message below should be removed (keep it only for debugging)

`uvm_error("RSRCNF", $sformatf("resource id: boot_complete not found"));

boot_complete = 1; // if it has not been provided, then assumed to be 1

end

endfunction: configure

// get_sticky

function bit ivm_reg_field::get_sticky();

return m_sticky;

endfunction: get_sticky

// reset

function void ivm_reg_field::reset(string kind = "HARD");

/* -----\/----- EXCLUDED -----\/-----

// base class code

if (!m_reset.exists(kind))

return;

m_mirrored = m_reset[kind];

m_desired = m_mirrored;

value = m_mirrored;

if (kind == "HARD")

m_written = 0;

-----/\----- EXCLUDED -----/\----- */

if(kind == "SOFT" && m_sticky == 1) begin

// leave field unchanged

return;

end

if(kind == "SOFT" && m_sticky == 0) begin

// act as HARD reset (change kind to HARD in order to find power-on-reset value and call super)

kind = "HARD";

end

// call base implementation which deals with HARD reset or any other reset kind

super.reset(kind);

endfunction: reset

// get_access

function string ivm_reg_field::get_access(uvm_reg_map map = null);

uvm_reg m_parent;

get_access = super.get_access(map);

if(get_access == "RE") // get_access will be "RO" or "RW"

begin

if(this.boot_complete == 1)

get_access = "RO";

else

get_access = "RW";

// Updated below base class code for the RE case.

m_parent = get_parent(); // need to access local member in base class

if (m_parent.get_n_maps() == 1 || map == uvm_reg_map::backdoor())

return get_access;

// Is the register restricted in this map?

case (m_parent.get_rights(map))

"RW": return get_access; // No restrictions

"RO": if(get_access == "RW") get_access = "RO";

"WO": if(get_access == "RW")

get_access = "WO";

else

begin

`uvm_error("RegModel", {get_access," field '",get_full_name(),

"' restricted to WO in map '",map.get_full_name(),"'"})

end

endcase

end

endfunction: get_access

// set

function void ivm_reg_field::set(uvm_reg_data_t value,

string fname = "",

int lineno = 0);

string get_access;

uvm_reg_data_t mask;

get_access = super.get_access();

if(get_access == "RE") // get_access will be "RO" or "RW"

begin

if(this.boot_complete == 1)

get_access = "RO";

else

get_access = "RW";

/* -----\/----- EXCLUDED -----\/-----

// implementation copied from base class

mask = ('b1 << this.get_n_bits())-1;

// m_fname = fname; // local member in base class

// m_lineno = lineno; // local member in base class

if (value >> this.get_n_bits()) begin

`uvm_warning("RegModel",

$psprintf("Specified value (0x%h) greater than field \"%s\" size (%0d bits)",

value, get_name(), this.get_n_bits()));

value &= mask;

end

-----/\----- EXCLUDED -----/\----- */

// The code below can't be executed because m_desired is a local member in the base class

// case (get_access)

// "RO": m_desired = m_desired;

// "RW": m_desired = value;

// default: m_desired = value;

// endcase

// this.value = m_desired;

//

// Replaced with this code instead:

case(get_access)

"RO": ; // do nothing, m_desired preserves its previous value // Effect: m_desired = m_desired;;

"RW": super.set(value, fname, lineno); // will execute default case in base class for case(m_access) because "RE" is not on any branch // Effect: m_desired = value;

endcase

// this.value = m_desired; get sets for "RW" when calling super; for "RO" it does not change

end

else

begin

// call base class implementation

super.set(value, fname, lineno);

end

endfunction: set

Here is an automatically generated register class that uses ivm_reg_field extension to configure the register fields:

/** <B>This register defines the arbitration weights for Block DMA channels</B> */

class bdm_dma_arb0 extends uvm_reg;

`uvm_object_utils(bdm_dma_arb0)

// FIELDS (MSB first)

/** \brief <B>Bits [31:29]<BR><BR>RESERVED</B>*/

rand ivm_reg_field RESERVED_29; // register bits[31:29] of width [2:0]

/** \brief <B>Bits [28:24]<BR><BR>Number of consecutive packets to send for DMA channel 3 before moving to the next DMA channel. <BR>Weight of 0x00 is an unused DMA channel. </B>*/

rand ivm_reg_field WEIGHT_3; // register bits[28:24] of width [4:0]

/** \brief <B>Bits [23:21]<BR><BR>RESERVED</B>*/

rand ivm_reg_field RESERVED_21; // register bits[23:21] of width [2:0]

/** \brief <B>Bits [20:16]<BR><BR>Number of consecutive packets to send for DMA channel 2 before moving to the next DMA channel.<BR>Weight of 0x00 is an unused DMA channel.</B>*/

rand ivm_reg_field WEIGHT_2; // register bits[20:16] of width [4:0]

/** \brief <B>Bits [15:13]<BR><BR>RESERVED</B>*/

rand ivm_reg_field RESERVED_13; // register bits[15:13] of width [2:0]

/** \brief <B>Bits [12:8]<BR><BR>Number of consecutive packets to send for DMA channel 1 before moving to the next DMA channel.<BR>Weight of 0x00 is an unused DMA channel.</B>*/

rand ivm_reg_field WEIGHT_1; // register bits[12:8] of width [4:0]

/** \brief <B>Bits [7:5]<BR><BR>RESERVED</B>*/

rand ivm_reg_field RESERVED_5; // register bits[7:5] of width [2:0]

/** \brief <B>Bits [4:0]<BR><BR>Number of consecutive packets to send for DMA channel 0 before moving to the next DMA channel.<BR>Weight of 0x00 is an unused DMA channel.</B>*/

rand ivm_reg_field WEIGHT_0; // register bits[4:0] of width [4:0]

function new(string name = "bdm_dma_arb0");

super.new(name, 32, UVM_NO_COVERAGE);

endfunction

virtual function void build();

WEIGHT_0 = ivm_reg_field::type_id::create("WEIGHT_0");

WEIGHT_0.configure(this, 5, 0, "RW", 0, 5'b00001, 1, 1, 0);

RESERVED_5 = ivm_reg_field::type_id::create("RESERVED_5");

RESERVED_5.configure(this, 3, 5, "RO", 0, 3'b000, 1, 1, 0);

WEIGHT_1 = ivm_reg_field::type_id::create("WEIGHT_1");

WEIGHT_1.configure(this, 5, 8, "RE", 0, 5'b00001, 1, 1, 0); // RE mask

RESERVED_13 = ivm_reg_field::type_id::create("RESERVED_13");

RESERVED_13.configure(this, 3, 13, "RO", 0, 3'b000, 1, 1, 0);

WEIGHT_2 = ivm_reg_field::type_id::create("WEIGHT_2");

WEIGHT_2.configure(this, 5, 16, "RW", 0, 5'b00001, 1, 1, 0);

RESERVED_21 = ivm_reg_field::type_id::create("RESERVED_21");

RESERVED_21.configure(this, 3, 21, "RO", 0, 3'b000, 1, 1, 0);

WEIGHT_3 = ivm_reg_field::type_id::create("WEIGHT_3");

WEIGHT_3.configure(this, 5, 24, "RW", 0, 5'b00001, 1, 1, 0, 1); // last param is to set m_sticky=1

RESERVED_29 = ivm_reg_field::type_id::create("RESERVED_29");

RESERVED_29.configure(this, 3, 29, "RO", 0, 3'b000, 1, 1, 0);

endfunction

endclass---

Should the uvm_reg_field class add get methods for the local members that would have to be changed in extensions?

Thanks,

Mona

Edited by mbeimers
Changed the font
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...