mbeimers Posted May 27, 2011 Report Share Posted May 27, 2011 (edited) 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 May 27, 2011 by mbeimers Changed the font Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.