// // ------------------------------------------------------------- // 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 uvm_mem_region; typedef class uvm_mem_mam; typedef class 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 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 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 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 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 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 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 method. // It must be released using the method. // extern virtual function uvm_mem_region 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 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 method. // It must be released using the method. // extern virtual function uvm_mem_region 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 release_region(); /*local*/ extern virtual function void set_parent(uvm_reg_block parent); /*local*/ extern function void Xlock_modelX(); /*local*/ extern function void add_field(uvm_vreg_field field); /*local*/ extern task 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 get_full_name(); // // FUNCTION: get_parent // Get the parent block // extern virtual function uvm_reg_block get_parent(); extern virtual function uvm_reg_block get_block(); // // FUNCTION: get_memory // Get the memory where the virtual register array is implemented // extern virtual function uvm_mem get_memory(); // // Function: get_n_maps // Returns the number of address maps this virtual register array is mapped in // extern virtual function int get_n_maps (); // // Function: is_in_map // Return TRUE if this virtual register array is in the specified address ~map~ // extern function bit 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 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 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 get_access(uvm_reg_map map = null); // // FUNCTION: get_size // Returns the size of the virtual register array. // extern virtual function int unsigned 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 get_n_bytes(); // // FUNCTION: get_n_memlocs // Returns the number of memory locations used // by a single virtual register. // extern virtual function int unsigned 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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_vreg //------------------------------------------------------------------------------ // Class: uvm_vreg_cbs // // Pre/post read/write callback facade class // //------------------------------------------------------------------------------ class uvm_vreg_cbs extends uvm_callback; string fname; int lineno; function 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 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 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 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 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 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 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 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 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) 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) 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<> 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<> 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<> 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