Source code for uvm.reg.uvm_reg_block

#
# -------------------------------------------------------------
#    Copyright 2004-2011 Synopsys, Inc.
#    Copyright 2010-2011 Mentor Graphics Corporation
#    Copyright 2010-2011 Cadence Design Systems, Inc.
#    Copyright 2019 Tuomas Poikela
#    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.
#
#    Original SV code has been adapter to Python.
# -------------------------------------------------------------

#import cocotb

from typing import Dict, Optional, List

from ..base.sv import sv
from ..base.uvm_object import UVMObject
from ..base.uvm_globals import uvm_check_output_args
from ..base.uvm_object_globals import (UVM_HIGH)
from ..base.uvm_pool import UVMPool, UVMObjectStringPool
from ..base.uvm_queue import UVMQueue
from ..base.uvm_resource_db import UVMResourceDb
from .uvm_reg_model import (UVM_IS_OK, UVM_HAS_X, UVM_NO_COVERAGE,
    UVM_DEFAULT_PATH, UVM_HIER, uvm_reg_cvr_rsrc_db, UVM_FRONTDOOR,
    UVM_NO_CHECK, UVM_CVR_ALL, UVM_NOT_OK)
from .uvm_reg_map import UVMRegMap
from .uvm_mem import UVMMem
from .uvm_reg_field import UVMRegField
from .uvm_reg import UVMReg
from ..macros import (uvm_info, uvm_error, uvm_warning, uvm_fatal, UVM_REG_DATA_WIDTH)

ERR_MSG1 = ("There are %0d root register models named %s. The names of the root"
    + " register models have to be unique")

ERR_MSG3 = ("Register model requires that UVM_REG_DATA_WIDTH be defined as %0d"
    + " or greater. Currently defined as %0d")


[docs]class UVMRegBlock(UVMObject): """ Block abstraction base class A block represents a design hierarchy. It can contain registers, register files, memories and sub-blocks. A block has one or more address maps, each corresponding to a physical interface on the block. """ m_roots: Dict['UVMRegBlock', int] = {} id = 0 def __init__(self, name="", has_coverage=UVM_NO_COVERAGE): """ Create a new instance and type-specific configuration Creates an instance of a block abstraction class with the specified name. ~has_coverage~ specifies which functional coverage models are present in the extension of the block abstraction class. Multiple functional coverage models may be specified by adding their symbolic names, as defined by the <uvm_coverage_model_e> type. """ super().__init__(name) self.hdl_paths_pool = UVMObjectStringPool("hdl_paths", UVMQueue) self.has_cover = has_coverage self.cover_on = 0 # Root block until registered with a parent UVMRegBlock.m_roots[self] = 0 self.locked = False self.maps = UVMPool() # bit[UVMRegMap] self.regs = UVMPool() # int unsigned[uvm_reg] self.blks = UVMPool() self.mems = UVMPool() self.vregs = UVMPool() self.root_hdl_paths = UVMPool() self.backdoor = None self.default_hdl_path = "RTL" self.parent = None # // Variable: default_path # // Default access path for the registers and memories in this block. self.default_path = UVM_DEFAULT_PATH self.fname = "" self.lineno = 0
[docs] def configure(self, parent=None, hdl_path="") -> None: """ Instance-specific configuration Specify the parent block of this block. A block without parent is a root block. If the block file corresponds to a hierarchical RTL structure, its contribution to the HDL path is specified as the ~hdl_path~. Otherwise, the block does not correspond to a hierarchical RTL structure (e.g. it is physically flattened) and does not contribute to the hierarchical HDL path of any contained registers or memories. """ self.parent = parent if parent is not None: self.parent.add_block(self) self.add_hdl_path(hdl_path) UVMResourceDb.set("uvm_reg::*", self.get_full_name(), self)
[docs] def create_map(self, name: str, base_addr: int, n_bytes: int, endian: int, byte_addressing=True) -> Optional[UVMRegMap]: """ Function: create_map Create an address map in this block Create an address map with the specified ~name~, then configures it with the following properties. base_addr - the base address for the map. All registers, memories, and sub-blocks within the map will be at offsets to this address n_bytes - the byte-width of the bus on which this map is used endian - the endian format. See <uvm_endianness_e> for possible values byte_addressing - specifies whether consecutive addresses refer are 1 byte apart (TRUE) or ~n_bytes~ apart (FALSE). Default is TRUE:: APB = create_map("APB", 0, 1, UVM_LITTLE_ENDIAN, 1) """ if self.locked is True: uvm_error("RegModel", "Cannot add map to locked model") return None _map = UVMRegMap.type_id.create(name, None, self.get_full_name()) _map.configure(self, base_addr, n_bytes, endian, byte_addressing) self.maps[_map] = True if self.maps.num() == 1: self.default_map = _map return _map
# // Function: check_data_width # // # // Check that the specified data width (in bits) is less than # // or equal to the value of `UVM_REG_DATA_WIDTH # // # // This method is designed to be called by a static initializer # // # //| class my_blk extends uvm_reg_block # //| local static bit m_data_width = check_data_width(356) # //| ... # //| endclass # //
[docs] def check_data_width(self, width: int) -> int: # if width <= sv.bits(uvm_reg_data_t): if width <= UVM_REG_DATA_WIDTH: return 1 uvm_fatal("RegModel", sv.sformatf( "Register model requires that UVM_REG_DATA_WIDTH be defined as %0d or greater. Currently defined as %0d", width, UVM_REG_DATA_WIDTH)) return 0
[docs] def set_default_map(self, _map: UVMRegMap) -> None: """ Defines the default address map Set the specified address map as the <default_map> for this block. The address map must be a map of this address block. """ if _map not in self.maps: uvm_warning("RegModel", "Map '" + _map.get_full_name() + "' does not exist in block") self.default_map = _map
# // Variable: default_map # // # // Default address map # // # // Default address map for this block, to be used when no # // address map is specified for a register operation and that # // register is accessible from more than one address map. # // # // It is also the implicit address map for a block with a single, # // unnamed address map because it has only one physical interface. # // # UVMRegMap default_map
[docs] def get_default_map(self) -> UVMRegMap: return self.default_map
# extern virtual function void set_parent(uvm_reg_block parent)
[docs] def set_parent(self, parent: 'UVMRegBlock'): if self != parent: self.parent = parent
# function void add_block (uvm_reg_block blk)
[docs] def add_block(self, blk: 'UVMRegBlock'): if self.is_locked(): uvm_error("RegModel", "Cannot add subblock to locked block model") return if blk in self.blks: uvm_error("RegModel", "Subblock '" + blk.get_name() + "' has already been registered with block '" + self.get_name() + "'") return self.blks[blk] = UVMRegBlock.id UVMRegBlock.id += 1 if blk in UVMRegBlock.m_roots: del UVMRegBlock.m_roots[blk]
# /*local*/ extern function void add_map (UVMRegMap map)
[docs] def add_map(self, _map: UVMRegMap): if (self.locked): uvm_error("RegModel", "Cannot add map to locked model") return if self.maps.exists(_map): uvm_error("RegModel", "Map '" + _map.get_name() + "' already exists in '" + self.get_full_name() + "'") return self.maps[_map] = 1 if self.maps.num() == 1: self.default_map = _map
# add_reg
[docs] def add_reg(self, rg: UVMReg): if self.is_locked(): uvm_error("RegModel", "Cannot add register to locked block model") return if rg in self.regs: uvm_error("RegModel", ("Register '" + rg.get_name() + "' has already been registered with block '" + self.get_name() + "'")) return self.regs[rg] = UVMRegBlock.id UVMRegBlock.id += 1
# /*local*/ extern function void add_vreg (uvm_vreg vreg)
[docs] def add_vreg(self, vreg): if self.is_locked(): uvm_error("RegModel", "Cannot add virtual register to locked block model") return if self.vregs.exists(vreg): uvm_error("RegModel", ("Virtual register '" + vreg.get_name() + "' has already been registered with block '" + self.get_name() + "'")) return self.vregs[vreg] = UVMRegBlock.id UVMRegBlock.id += 1
# /*local*/ extern function void add_mem (uvm_mem mem)
[docs] def add_mem(self, mem: UVMMem): if self.is_locked(): uvm_error("RegModel", "Cannot add memory to locked block model") return if mem in self.mems: uvm_error("RegModel", "Memory '" + mem.get_name() + "' has already been registered with block '" + self.get_name() + "'") return self.mems[mem] = UVMRegBlock.id UVMRegBlock.id += 1
[docs] def lock_model(self): """ Function: lock_model Lock a model and build the address map. Recursively lock an entire register model and build the address maps to enable the `UVMRegMap.get_reg_by_offset()` and `UVMRegMap.get_mem_by_offset()` methods. Once locked, no further structural changes, such as adding registers or memories, can be made. It is not possible to unlock a model. """ if self.is_locked(): return self.locked = 1 for rr in self.regs.key_list(): rr.Xlock_modelX() for mem in self.mems.key_list(): mem.Xlock_modelX() for blk in self.blks.key_list(): blk.lock_model() # TODO finish this if (self.parent is None): max_size = UVMReg.get_max_size() if (UVMRegField.get_max_size() > max_size): max_size = UVMRegField.get_max_size() if (UVMMem.get_max_size() > max_size): max_size = UVMMem.get_max_size() if (max_size > UVM_REG_DATA_WIDTH): uvm_fatal("RegModel", sv.sformatf(ERR_MSG3, max_size, UVM_REG_DATA_WIDTH)) self.Xinit_address_mapsX() # Check that root register models have unique names # Has this name has been checked before? if self in UVMRegBlock.m_roots and UVMRegBlock.m_roots[self] != 1: n = 0 for _blk in UVMRegBlock.m_roots: blk = _blk # uvm_reg_block if (blk.get_name() == self.get_name()): UVMRegBlock.m_roots[blk] = 1 n += 1 if (n > 1): uvm_error("UVM/REG/DUPLROOT", sv.sformatf(ERR_MSG1, n, self.get_name()))
# // Function: is_locked # // # // Return TRUE if the model is locked. # //
[docs] def is_locked(self): return self.locked
# //--------------------- # // Group: Introspection # //--------------------- # # # // Function: get_name # // # // Get the simple name # // # // Return the simple object name of this block. # // # // Function: get_full_name # // # // Get the hierarchical name # // # // Return the hierarchal name of this block. # // The base of the hierarchical name is the root block.
[docs] def get_full_name(self) -> str: if self.parent is None: return self.get_name() return self.parent.get_full_name() + "." + self.get_name()
# // Function: get_parent # // # // Get the parent block # // # // If this a top-level block, returns ~None~.
[docs] def get_parent(self) -> Optional['UVMRegBlock']: return self.parent
# // Function: get_root_blocks # // # // Get the all root blocks # // # // Returns an array of all root blocks in the simulation. # // # extern static function void get_root_blocks(ref uvm_reg_block blks[$])
[docs] def get_root_blocks(self, blks): for blk in UVMRegBlock.m_roots: blks.append(blk)
# // Function: find_blocks # // # // Find the blocks whose hierarchical names match the # // specified ~name~ glob. # // If a ~root~ block is specified, the name of the blocks are # // relative to that block, otherwise they are absolute. # // # // Returns the number of blocks found. # // # extern static function int find_blocks(input string name, # ref uvm_reg_block blks[$], # input uvm_reg_block root = None, # input uvm_object accessor = None) # // Function: find_block # // # // Find the first block whose hierarchical names match the # // specified ~name~ glob. # // If a ~root~ block is specified, the name of the blocks are # // relative to that block, otherwise they are absolute. # // # // Returns the first block found or ~None~ otherwise. # // A warning is issued if more than one block is found. # // # extern static function uvm_reg_block find_block(input string name, # input uvm_reg_block root = None, # input uvm_object accessor = None) # // Function: get_blocks # // # // Get the sub-blocks # // # // Get the blocks instantiated in this blocks. # // If ~hier~ is TRUE, recursively includes any sub-blocks. # // # extern virtual function void get_blocks (ref uvm_reg_block blks[$], # input uvm_hier_e hier=UVM_HIER)
[docs] def get_blocks(self, blks: List, hier=UVM_HIER) -> None: for blk_ in self.blks.key_list(): blk = blk_ blks.append(blk) if hier == UVM_HIER: blk.get_blocks(blks)
# // Function: get_maps # // # // Get the address maps # // # // Get the address maps instantiated in this block. # // # extern virtual function void get_maps (ref UVMRegMap maps[$])
[docs] def get_maps(self, maps: List) -> None: for key in self.maps.key_list(): maps.append(key)
[docs] def get_registers(self, regs: List, hier=UVM_HIER) -> None: """ Get the registers Get the registers instantiated in this block. If ~hier~ is TRUE, recursively includes the registers in the sub-blocks. Note that registers may be located in different and/or multiple address maps. To get the registers in a specific address map, use the `UVMRegMap.get_registers()` method. """ for rg in self.regs.key_list(): regs.append(rg) if hier == UVM_HIER: for blk_ in self.blks.key_list(): blk = blk_ blk.get_registers(regs)
# // Function: get_fields # // # // Get the fields # // # // Get the fields in the registers instantiated in this block. # // If ~hier~ is TRUE, recursively includes the fields of the registers # // in the sub-blocks. # // # extern virtual function void get_fields (ref uvm_reg_field fields[$], # input uvm_hier_e hier=UVM_HIER)
[docs] def get_memories(self, mems: List, hier=UVM_HIER) -> None: """ Function: get_memories Get the memories Get the memories instantiated in this block. If ~hier~ is TRUE, recursively includes the memories in the sub-blocks. Note that memories may be located in different and/or multiple address maps. To get the memories in a specific address map, use the `UVMRegMap.get_memories()` method. """ for mem_ in self.mems.key_list(): mem = mem_ mems.append(mem) if hier == UVM_HIER: for blk_ in self.blks.key_list(): blk = blk_ blk.get_memories(mems)
# // Function: get_virtual_registers # // # // Get the virtual registers # // # // Get the virtual registers instantiated in this block. # // If ~hier~ is TRUE, recursively includes the virtual registers # // in the sub-blocks. # // # extern virtual function void get_virtual_registers(ref uvm_vreg regs[$], # input uvm_hier_e hier=UVM_HIER) # // Function: get_virtual_fields # // # // Get the virtual fields # // # // Get the virtual fields from the virtual registers instantiated # // in this block. # // If ~hier~ is TRUE, recursively includes the virtual fields # // in the virtual registers in the sub-blocks. # // # extern virtual function void get_virtual_fields (ref uvm_vreg_field fields[$], # input uvm_hier_e hier=UVM_HIER) # // Function: get_block_by_name # // # // Finds a sub-block with the specified simple name. # // # // The name is the simple name of the block, not a hierarchical name. # // relative to this block. # // If no block with that name is found in this block, the sub-blocks # // are searched for a block of that name and the first one to be found # // is returned. # // # // If no blocks are found, returns ~None~. # // # extern virtual function uvm_reg_block get_block_by_name (string name)
[docs] def get_map_by_name(self, name) -> Optional[UVMRegMap]: """ Function: get_map_by_name Finds an address map with the specified simple name. The name is the simple name of the address map, not a hierarchical name. relative to this block. If no map with that name is found in this block, the sub-blocks are searched for a map of that name and the first one to be found is returned. If no address maps are found, returns ~None~. """ maps: List[UVMRegMap] = [] self.get_maps(maps) for i in range(len(maps)): if (maps[i].get_name() == name): return maps[i] for i in range(len(maps)): submaps: List[UVMRegMap] = [] maps[i].get_submaps(submaps, UVM_HIER) for j in range(len(submaps)): if (submaps[j].get_name() == name): return submaps[j] uvm_warning("RegModel", "Map with name '" + name + "' does not exist in block") return None
[docs] def get_reg_by_name(self, name: str) -> Optional[UVMReg]: """ Function: get_reg_by_name Finds a register with the specified simple name. The name is the simple name of the register, not a hierarchical name. relative to this block. If no register with that name is found in this block, the sub-blocks are searched for a register of that name and the first one to be found is returned. If no registers are found, returns ~None~. """ for rg in self.regs.key_list(): if rg.get_name() == name: return rg for blk_ in self.blks.key_list(): # blk = blk_ # uvm_reg_block subregs = [] # uvm_reg[$] blk_.get_registers(subregs, UVM_HIER) for j in range(len(subregs)): if subregs[j].get_name() == name: return subregs[j] uvm_warning("RegModel", "Unable to locate register '" + name + "' in block '" + self.get_full_name() + "'") return None
# // Function: get_field_by_name # // # // Finds a field with the specified simple name. # // # // The name is the simple name of the field, not a hierarchical name. # // relative to this block. # // If no field with that name is found in this block, the sub-blocks # // are searched for a field of that name and the first one to be found # // is returned. # // # // If no fields are found, returns ~None~. # // # extern virtual function uvm_reg_field get_field_by_name (string name) # // Function: get_mem_by_name # // # // Finds a memory with the specified simple name. # // # // The name is the simple name of the memory, not a hierarchical name. # // relative to this block. # // If no memory with that name is found in this block, the sub-blocks # // are searched for a memory of that name and the first one to be found # // is returned. # // # // If no memories are found, returns ~None~. # // # extern virtual function uvm_mem get_mem_by_name (string name) # // Function: get_vreg_by_name # // # // Finds a virtual register with the specified simple name. # // # // The name is the simple name of the virtual register, # // not a hierarchical name. # // relative to this block. # // If no virtual register with that name is found in this block, # // the sub-blocks are searched for a virtual register of that name # // and the first one to be found is returned. # // # // If no virtual registers are found, returns ~None~. # // # extern virtual function uvm_vreg get_vreg_by_name (string name) # // Function: get_vfield_by_name # // # // Finds a virtual field with the specified simple name. # // # // The name is the simple name of the virtual field, # // not a hierarchical name. # // relative to this block. # // If no virtual field with that name is found in this block, # // the sub-blocks are searched for a virtual field of that name # // and the first one to be found is returned. # // # // If no virtual fields are found, returns ~None~. # // # extern virtual function uvm_vreg_field get_vfield_by_name (string name) """ Group: Coverage =================== """
[docs] def build_coverage(self, models): """ Function: build_coverage Check if all of the specified coverage model must be built. Check which of the specified coverage model must be built in this instance of the block abstraction class, as specified by calls to `UVMReg.include_coverage()`. Models are specified by adding the symbolic value of individual coverage model as defined in <uvm_coverage_model_e>. Returns the sum of all coverage models to be built in the block model. """ build_coverage = UVM_NO_COVERAGE cov_arr = [] uvm_reg_cvr_rsrc_db.read_by_name("uvm_reg::" + self.get_full_name(), "include_coverage", cov_arr, self) build_coverage = cov_arr[0] return build_coverage & models
[docs] def add_coverage(self, models): """ Function: add_coverage Specify that additional coverage models are available. Add the specified coverage model to the coverage models available in this class. Models are specified by adding the symbolic value of individual coverage model as defined in <uvm_coverage_model_e>. This method shall be called only in the constructor of subsequently derived classes. """ self.has_cover |= models
[docs] def has_coverage(self, models): """ Function: has_coverage Check if block has coverage model(s) Returns TRUE if the block abstraction class contains a coverage model for all of the models specified. Models are specified by adding the symbolic value of individual coverage model as defined in <uvm_coverage_model_e>. """ return ((self.has_cover & models) == models)
[docs] def set_coverage(self, is_on): """ Function: set_coverage Turns on coverage measurement. Turns the collection of functional coverage measurements on or off for this block and all blocks, registers, fields and memories within it. The functional coverage measurement is turned on for every coverage model specified using <uvm_coverage_model_e> symbolic identifiers. Multiple functional coverage models can be specified by adding the functional coverage model identifiers. All other functional coverage models are turned off. Returns the sum of all functional coverage models whose measurements were previously on. This method can only control the measurement of functional coverage models that are present in the various abstraction classes, then enabled during construction. See the `UVMRegBlock.has_coverage()` method to identify the available functional coverage models. """ self.cover_on = self.has_cover & is_on for rg_ in self.regs.key_list(): rg = rg_ rg.set_coverage(is_on) for mem in self.mems.key_list(): mem.set_coverage(is_on) for blk_ in self.blks.key_list(): blk = blk_ blk.set_coverage(is_on) return self.cover_on
# // Function: get_coverage # // # // Check if coverage measurement is on. # // # // Returns TRUE if measurement for all of the specified functional # // coverage models are currently on. # // Multiple functional coverage models can be specified by adding the # // functional coverage model identifiers. # // # // See <uvm_reg_block::set_coverage()> for more details.
[docs] def get_coverage(self, is_on=UVM_CVR_ALL) -> bool: if self.has_coverage(is_on) == 0: return 0 return ((self.cover_on & is_on) == is_on)
# // Function: sample # // # // Functional coverage measurement method # // # // This method is invoked by the block abstraction class # // whenever an address within one of its address map # // is successfully read or written. # // The specified offset is the offset within the block, # // not an absolute address. # // # // Empty by default, this method may be extended by the # // abstraction class generator to perform the required sampling # // in any provided functional coverage model. # // # protected virtual function void sample(uvm_reg_addr_t offset, # bit is_read, # UVMRegMap map) # endfunction
[docs] def sample(self, offset, is_read, _map): pass
# // Function: sample_values # // # // Functional coverage measurement method for field values # // # // This method is invoked by the user # // or by the <uvm_reg_block::sample_values()> method of the parent block # // to trigger the sampling # // of the current field values in the # // block-level functional coverage model. # // It recursively invokes the <uvm_reg_block::sample_values()> # // and <uvm_reg::sample_values()> methods # // in the blocks and registers in this block. # // # // This method may be extended by the # // abstraction class generator to perform the required sampling # // in any provided field-value functional coverage model. # // If this method is extended, it MUST call super.sample_values(). # // # extern virtual function void sample_values()
[docs] def sample_values(self): for rg_ in self.regs.key_list(): rg = rg_ rg.sample_values() for blk_ in self.blks.key_list(): blk = blk_ blk.sample_values()
# /*local*/ extern function void XsampleX(uvm_reg_addr_t addr, # bit is_read, # UVMRegMap map)
[docs] def XsampleX(self, addr, is_read, _map): self.sample(addr, is_read, _map) if (self.parent is not None): pass
# ToDo: Call XsampleX in the parent block # with the offset and map within that block's context # //-------------- # // Group: Access # //-------------- # // Function: get_default_path # // # // Default access path # // # // Returns the default access path for this block. # // # extern virtual function uvm_path_e get_default_path()
[docs] def get_default_path(self): if (self.default_path != UVM_DEFAULT_PATH): return self.default_path if (self.parent is not None): return self.parent.get_default_path() return UVM_FRONTDOOR
# // Function: reset # // # // Reset the mirror for this block. # // # // Sets the mirror value of all registers in the block and sub-blocks # // to the reset value corresponding to the specified reset event. # // See <uvm_reg_field::reset()> for more details. # // Does not actually set the value of the registers in the design, # // only the values mirrored in their corresponding mirror. # // # extern virtual function void reset(string kind = "HARD")
[docs] def reset(self, kind="HARD"): # for rg_ in self.regs.key_list(): rg = rg_ rg.reset(kind) for blk_ in self.blks.key_list(): blk = blk_ # uvm_reg_block blk.reset(kind)
#endfunction # // Function: needs_update # // # // Check if DUT registers need to be written # // # // If a mirror value has been modified in the abstraction model # // without actually updating the actual register # // (either through randomization or via the <uvm_reg::set()> method, # // the mirror and state of the registers are outdated. # // The corresponding registers in the DUT need to be updated. # // # // This method returns TRUE if the state of at least one register in # // the block or sub-blocks needs to be updated to match the mirrored # // values. # // The mirror values, or actual content of registers, are not modified. # // For additional information, see <uvm_reg_block::update()> method. # // # extern virtual function bit needs_update()
[docs] def needs_update(self): for rg_ in self.regs.key_list(): rg = rg_ if rg.needs_update(): return 1 for blk_ in self.blks.key_list(): blk = blk_ if blk.needs_update(): return 1 return 0
# // Task: update # // # // Batch update of register. # // # // Using the minimum number of write operations, updates the registers # // in the design to match the mirrored values in this block and sub-blocks. # // The update can be performed using the physical # // interfaces (front-door access) or back-door accesses. # // This method performs the reverse operation of <uvm_reg_block::mirror()>. # // # extern virtual task update(output uvm_status_e status, # input uvm_path_e path = UVM_DEFAULT_PATH, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0)
[docs] async def update(self, status, path=UVM_DEFAULT_PATH, parent=None, prior=-1, extension=None, fname="", lineno=0): uvm_check_output_args([status]) stat_all = UVM_IS_OK if self.needs_update() is False: uvm_info("RegModel", sv.sformatf("%s:%0d - RegModel block %s does not need updating", fname, lineno, self.get_name()), UVM_HIGH) return uvm_info("RegModel", sv.sformatf("%s:%0d - Updating model block %s with %s path", fname, lineno, self.get_name(), path), UVM_HIGH) for rg_ in self.regs.key_list(): rg = rg_ if rg.needs_update(): stat = [] await rg.update(stat, path, None, parent, prior, extension) if (stat[0] != UVM_IS_OK and stat[0] != UVM_HAS_X): uvm_error("RegModel", sv.sformatf("Register \"%s\" could not be updated", rg.get_full_name())) status.append(stat[0]) return for blk_ in self.blks.key_list(): blk = blk_ stat_blk = [] await blk.update(status,path,parent,prior,extension,fname,lineno) if (stat_blk[0] != UVM_IS_OK and stat_blk[0] != UVM_HAS_X): stat_all = stat_blk status.append(stat_all)
# // Task: mirror # // # // Update the mirrored values # // # // Read all of the registers in this block and sub-blocks and update their # // mirror values to match their corresponding values in the design. # // The mirroring can be performed using the physical interfaces # // (front-door access) or back-door accesses. # // If the ~check~ argument is specified as <UVM_CHECK>, # // an error message is issued if the current mirrored value # // does not match the actual value in the design. # // This method performs the reverse operation of <uvm_reg_block::update()>. # // # extern virtual task mirror(output uvm_status_e status, # input uvm_check_e check = UVM_NO_CHECK, # input uvm_path_e path = UVM_DEFAULT_PATH, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0)
[docs] async def mirror(self, status, check=UVM_NO_CHECK, path=UVM_DEFAULT_PATH, parent=None, prior=-1, extension=None, fname="", lineno=0): final_status = UVM_IS_OK for rg_ in self.regs.key_list(): rg = rg_ curr_stat = [] await rg.mirror(curr_stat, check, path, None, parent, prior, extension, fname, lineno) if (curr_stat[0] != UVM_IS_OK and curr_stat[0] != UVM_HAS_X): final_status = curr_stat[0] for blk_ in self.blks.key_list(): blk = blk_ # uvm_reg_block curr_stat = [] await blk.mirror(curr_stat, check, path, parent, prior, extension, fname, lineno) if (curr_stat[0] != UVM_IS_OK and curr_stat[0] != UVM_HAS_X): final_status = curr_stat[0] status.append(final_status)
# // Task: write_reg_by_name # // # // Write the named register # // # // Equivalent to <get_reg_by_name()> followed by <uvm_reg::write()> # // # extern virtual task write_reg_by_name( # output uvm_status_e status, # input string name, # input uvm_reg_data_t data, # input uvm_path_e path = UVM_DEFAULT_PATH, # input UVMRegMap map = None, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0)
[docs] async def write_reg_by_name(self, status, name, data, path=UVM_DEFAULT_PATH, map=None, parent=None, prior=-1, extension=None, fname="", lineno=0): self.fname = fname self.lineno = lineno status = UVM_NOT_OK rg = self.get_reg_by_name(name) if rg is not None: await rg.write(status, data, path, map, parent, prior, extension)
# // Task: read_reg_by_name # // # // Read the named register # // # // Equivalent to <get_reg_by_name()> followed by <uvm_reg::read()> # // # extern virtual task read_reg_by_name( # output uvm_status_e status, # input string name, # output uvm_reg_data_t data, # input uvm_path_e path = UVM_DEFAULT_PATH, # input UVMRegMap map = None, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0) # // Task: write_mem_by_name # // # // Write the named memory # // # // Equivalent to <get_mem_by_name()> followed by <uvm_mem::write()> # // # extern virtual task write_mem_by_name( # output uvm_status_e status, # input string name, # input uvm_reg_addr_t offset, # input uvm_reg_data_t data, # input uvm_path_e path = UVM_DEFAULT_PATH, # input UVMRegMap map = None, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0) # // Task: read_mem_by_name # // # // Read the named memory # // # // Equivalent to <get_mem_by_name()> followed by <uvm_mem::read()> # // # extern virtual task read_mem_by_name( # output uvm_status_e status, # input string name, # input uvm_reg_addr_t offset, # output uvm_reg_data_t data, # input uvm_path_e path = UVM_DEFAULT_PATH, # input UVMRegMap map = None, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0) # extern virtual task readmemh(string filename) # extern virtual task writememh(string filename) # //---------------- # // Group: Backdoor # //---------------- # // Function: get_backdoor # // # // Get the user-defined backdoor for all registers in this block # // # // Return the user-defined backdoor for all register in this # // block and all sub-blocks -- unless overridden by a backdoor set # // in a lower-level block or in the register itself. # // # // If ~inherited~ is TRUE, returns the backdoor of the parent block # // if none have been specified for this block. # // # extern function uvm_reg_backdoor get_backdoor(bit inherited = 1)
[docs] def get_backdoor(self, inherited=True): if (self.backdoor is None and inherited): blk = self.get_parent() while (blk is not None): bkdr = blk.get_backdoor() if (bkdr is not None): return bkdr blk = blk.get_parent() return self.backdoor
#endfunction: get_backdoor # // Function: set_backdoor # // # // Set the user-defined backdoor for all registers in this block # // # // Defines the backdoor mechanism for all registers instantiated # // in this block and sub-blocks, unless overridden by a definition # // in a lower-level block or register. # // # extern function void set_backdoor (uvm_reg_backdoor bkdr, # string fname = "", # int lineno = 0) # // Function: clear_hdl_path # // # // Delete HDL paths # // # // Remove any previously specified HDL path to the block instance # // for the specified design abstraction. # // # extern function void clear_hdl_path (string kind = "RTL") # # # // Function: add_hdl_path # // # // Add an HDL path # // # // Add the specified HDL path to the block instance for the specified # // design abstraction. This method may be called more than once for the # // same design abstraction if the block is physically duplicated # // in the design abstraction # // # extern function void add_hdl_path (string path, string kind = "RTL")
[docs] def add_hdl_path(self, path, kind="RTL"): # uvm_queue #(string) paths paths = self.hdl_paths_pool.get(kind) paths.push_back(path)
# // Function: has_hdl_path # // # // Check if a HDL path is specified # // # // Returns TRUE if the block instance has a HDL path defined for the # // specified design abstraction. If no design abstraction is specified, # // uses the default design abstraction specified for this block or # // the nearest block ancestor with a specified default design abstraction. # //
[docs] def has_hdl_path(self, kind=""): if kind == "": kind = self.get_default_hdl_path() return self.hdl_paths_pool.exists(kind)
# // Function: get_hdl_path # // # // Get the incremental HDL path(s) # // # // Returns the HDL path(s) defined for the specified design abstraction # // in the block instance. # // Returns only the component of the HDL paths that corresponds to # // the block, not a full hierarchical path # // # // If no design abstraction is specified, the default design abstraction # // for this block is used. # // # extern function void get_hdl_path (ref string paths[$], input string kind = "") # // Function: get_full_hdl_path # // # // Get the full hierarchical HDL path(s) # // # // Returns the full hierarchical HDL path(s) defined for the specified # // design abstraction in the block instance. # // There may be more than one path returned even # // if only one path was defined for the block instance, if any of the # // parent components have more than one path defined for the same design # // abstraction # // # // If no design abstraction is specified, the default design abstraction # // for each ancestor block is used to get each incremental path. # // # extern function void get_full_hdl_path (ref string paths[$], # input string kind = "", # string separator = ".")
[docs] def get_full_hdl_path(self, paths, kind="", separator="."): if kind == "": kind = self.get_default_hdl_path() paths.clear() if self.is_hdl_path_root(kind): if self.root_hdl_paths[kind] != "": paths.append(self.root_hdl_paths[kind]) return if not self.has_hdl_path(kind): uvm_error("RegModel", "Block " + self.get_full_name() + " does not have hdl path defined for abstraction '" + kind + "'") return hdl_paths = self.hdl_paths_pool.get(kind) # uvm_queue #(string) parent_paths = [] if self.parent is not None: self.parent.get_full_hdl_path(parent_paths, kind, separator) for i in range(len(hdl_paths)): hdl_path = hdl_paths.get(i) if len(parent_paths) == 0: if hdl_path != "": paths.append(hdl_path) continue for j in range(len(parent_paths)): if hdl_path == "": paths.append(parent_paths[j]) else: paths.append(parent_paths[j] + separator + hdl_path)
# // Function: set_default_hdl_path # // # // Set the default design abstraction # // # // Set the default design abstraction for this block instance. # // # extern function void set_default_hdl_path (string kind)
[docs] def set_default_hdl_path(self, kind: str): if kind == "": if self.parent is None: uvm_error("RegModel", ("Block has no parent. " + "Must specify a valid HDL abstraction (kind)")) else: kind = self.parent.get_default_hdl_path() self.default_hdl_path = kind
# // Function: get_default_hdl_path # // # // Get the default design abstraction # // # // Returns the default design abstraction for this block instance. # // If a default design abstraction has not been explicitly set for this # // block instance, returns the default design abstraction for the # // nearest block ancestor. # // Returns "" if no default design abstraction has been specified. # // # extern function string get_default_hdl_path ()
[docs] def get_default_hdl_path(self) -> str: if self.default_hdl_path == "" and self.parent is not None: return self.parent.get_default_hdl_path() return self.default_hdl_path
# // Function: set_hdl_path_root # // # // Specify a root HDL path # // # // Set the specified path as the absolute HDL path to the block instance # // for the specified design abstraction. # // This absolute root path is prepended to all hierarchical paths # // under this block. The HDL path of any ancestor block is ignored. # // This method overrides any incremental path for the # // same design abstraction specified using <add_hdl_path>. # // # extern function void set_hdl_path_root (string path, string kind = "RTL") # set_hdl_path_root
[docs] def set_hdl_path_root(self, path, kind="RTL"): if kind == "": kind = self.get_default_hdl_path() self.root_hdl_paths[kind] = path
# // Function: is_hdl_path_root # // # // Check if this block has an absolute path # // # // Returns TRUE if an absolute HDL path to the block instance # // for the specified design abstraction has been defined. # // If no design abstraction is specified, the default design abstraction # // for this block is used. # // # extern function bit is_hdl_path_root (string kind = "")
[docs] def is_hdl_path_root(self, kind=""): if kind == "": kind = self.get_default_hdl_path() return self.root_hdl_paths.exists(kind)
#endfunction # extern virtual function void do_print (uvm_printer printer) # 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) # extern virtual function string convert2string () # extern virtual function uvm_object clone() # extern local function void Xinit_address_mapsX()
[docs] def Xinit_address_mapsX(self): for map_ in self.maps.key_list(): map_.Xinit_address_mapX()
# map.Xverify_map_configX() #------------------------------------------------------------------------ # #--------------- # Initialization #--------------- # # # # # #-------------------------- # Get Hierarchical Elements #-------------------------- # # get_fields # #function void uvm_reg_block::get_fields(ref uvm_reg_field fields[$], # input uvm_hier_e hier=UVM_HIER) # # foreach (regs[rg_]): # uvm_reg rg = rg_ # rg.get_fields(fields) # end # # if (hier == UVM_HIER) # foreach (blks[blk_]) # begin # uvm_reg_block blk = blk_ # blk.get_fields(fields) # end # #endfunction: get_fields # # # get_virtual_fields # #function void uvm_reg_block::get_virtual_fields(ref uvm_vreg_field fields[$], # input uvm_hier_e hier=UVM_HIER) # # foreach (vregs[vreg_]): # uvm_vreg vreg = vreg_ # vreg.get_fields(fields) # end # # if (hier == UVM_HIER) # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # blk.get_virtual_fields(fields) # end #endfunction: get_virtual_fields # # # # # get_virtual_registers # #function void uvm_reg_block::get_virtual_registers(ref uvm_vreg regs[$], # input uvm_hier_e hier=UVM_HIER) # # foreach (vregs[rg]) # regs.push_back(rg) # # if (hier == UVM_HIER) # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # blk.get_virtual_registers(regs) # end #endfunction: get_virtual_registers # # find_blocks # #function int uvm_reg_block::find_blocks(input string name, # ref uvm_reg_block blks[$], # input uvm_reg_block root = None, # input uvm_object accessor = None) # # uvm_resource_pool rpl = uvm_resource_pool::get() # uvm_resource_types::rsrc_q_t rs # # blks.delete() # # if (root is not None) name = {root.get_full_name(), ".", name} # # rs = rpl.lookup_regex(name, "uvm_reg::") # for (int i = 0; i < rs.size(); i++): # uvm_resource#(uvm_reg_block) blk # if (!$cast(blk, rs.get(i))) continue # blks.push_back(blk.read(accessor)) # end # # return blks.size() #endfunction # # # find_blocks # #function uvm_reg_block uvm_reg_block::find_block(input string name, # input uvm_reg_block root = None, # input uvm_object accessor = None) # # uvm_reg_block blks[$] # if (!find_blocks(name, blks, root, accessor)) # return None # # if (blks.size() > 1): # `uvm_warning("MRTH1BLK", # {"More than one block matched the name \"", name, "\"."}) # end # # # return blks[0] #endfunction # # # #------------ # Get-By-Name #------------ # # get_block_by_name # #function uvm_reg_block uvm_reg_block::get_block_by_name(string name) # # if (get_name() == name) # return this # # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # # if (blk.get_name() == name) # return blk # end # # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # uvm_reg_block subblks[$] # blk_.get_blocks(subblks, UVM_HIER) # # foreach (subblks[j]) # if (subblks[j].get_name() == name) # return subblks[j] # end # # `uvm_warning("RegModel", {"Unable to locate block '",name, # "' in block '",get_full_name(),"'"}) # return None # #endfunction: get_block_by_name # # # # # get_vreg_by_name # #function uvm_vreg uvm_reg_block::get_vreg_by_name(string name) # # foreach (vregs[rg_]): # uvm_vreg rg = rg_ # if (rg.get_name() == name) # return rg # end # # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # uvm_vreg subvregs[$] # blk_.get_virtual_registers(subvregs, UVM_HIER) # # foreach (subvregs[j]) # if (subvregs[j].get_name() == name) # return subvregs[j] # end # # `uvm_warning("RegModel", {"Unable to locate virtual register '",name, # "' in block '",get_full_name(),"'"}) # return None # #endfunction: get_vreg_by_name # # # get_mem_by_name # #function uvm_mem uvm_reg_block::get_mem_by_name(string name) # # foreach (mems[mem_]): # uvm_mem mem = mem_ # if (mem.get_name() == name) # return mem # end # # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # uvm_mem submems[$] # blk_.get_memories(submems, UVM_HIER) # # foreach (submems[j]) # if (submems[j].get_name() == name) # return submems[j] # end # # `uvm_warning("RegModel", {"Unable to locate memory '",name, # "' in block '",get_full_name(),"'"}) # return None # #endfunction: get_mem_by_name # # # get_field_by_name # #function uvm_reg_field uvm_reg_block::get_field_by_name(string name) # # foreach (regs[rg_]): # uvm_reg rg = rg_ # uvm_reg_field fields[$] # # rg.get_fields(fields) # foreach (fields[i]) # if (fields[i].get_name() == name) # return fields[i] # end # # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # uvm_reg subregs[$] # blk_.get_registers(subregs, UVM_HIER) # # foreach (subregs[j]): # uvm_reg_field fields[$] # subregs[j].get_fields(fields) # foreach (fields[i]) # if (fields[i].get_name() == name) # return fields[i] # end # end # # `uvm_warning("RegModel", {"Unable to locate field '",name, # "' in block '",get_full_name(),"'"}) # # return None # #endfunction: get_field_by_name # # # get_vfield_by_name # #function uvm_vreg_field uvm_reg_block::get_vfield_by_name(string name) # # foreach (vregs[rg_]): # uvm_vreg rg =rg_ # uvm_vreg_field fields[$] # # rg.get_fields(fields) # foreach (fields[i]) # if (fields[i].get_name() == name) # return fields[i] # end # # foreach (blks[blk_]): # uvm_reg_block blk = blk_ # uvm_vreg subvregs[$] # blk_.get_virtual_registers(subvregs, UVM_HIER) # # foreach (subvregs[j]): # uvm_vreg_field fields[$] # subvregs[j].get_fields(fields) # foreach (fields[i]) # if (fields[i].get_name() == name) # return fields[i] # end # end # # `uvm_warning("RegModel", {"Unable to locate virtual field '",name, # "' in block '",get_full_name(),"'"}) # # return None # #endfunction: get_vfield_by_name # # #---------------- # Run-Time Access #---------------- # # # # read_reg_by_name # #task uvm_reg_block::read_reg_by_name(output uvm_status_e status, # input string name, # output uvm_reg_data_t data, # input uvm_path_e path = UVM_DEFAULT_PATH, # input UVMRegMap map = None, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0) # uvm_reg rg # self.fname = fname # self.lineno = lineno # # status = UVM_NOT_OK # rg = self.get_reg_by_name(name) # if (rg is not None) # rg.read(status, data, path, map, parent, prior, extension) #endtask: read_reg_by_name # # # write_mem_by_name # #task uvm_reg_block::write_mem_by_name(output uvm_status_e status, # input string name, # input uvm_reg_addr_t offset, # input uvm_reg_data_t data, # input uvm_path_e path = UVM_DEFAULT_PATH, # input UVMRegMap map = None, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0) # uvm_mem mem # self.fname = fname # self.lineno = lineno # # status = UVM_NOT_OK # mem = get_mem_by_name(name) # if (mem is not None) # mem.write(status, offset, data, path, map, parent, prior, extension) #endtask: write_mem_by_name # # # read_mem_by_name # #task uvm_reg_block::read_mem_by_name(output uvm_status_e status, # input string name, # input uvm_reg_addr_t offset, # output uvm_reg_data_t data, # input uvm_path_e path = UVM_DEFAULT_PATH, # input UVMRegMap map = None, # input uvm_sequence_base parent = None, # input int prior = -1, # input uvm_object extension = None, # input string fname = "", # input int lineno = 0) # uvm_mem mem # self.fname = fname # self.lineno = lineno # # status = UVM_NOT_OK # mem = get_mem_by_name(name) # if (mem is not None) # mem.read(status, offset, data, path, map, parent, prior, extension) #endtask: read_mem_by_name # # # readmemh # #task uvm_reg_block::readmemh(string filename) # // TODO #endtask: readmemh # # # writememh # #task uvm_reg_block::writememh(string filename) # // TODO #endtask: writememh # # #--------------- # Map Management #--------------- # #---------------- # Group- Backdoor #---------------- # # set_backdoor # #function void uvm_reg_block::set_backdoor(uvm_reg_backdoor bkdr, # string fname = "", # int lineno = 0) # bkdr.fname = fname # bkdr.lineno = lineno # if (self.backdoor is not None && # self.backdoor.has_update_threads()): # `uvm_warning("RegModel", "Previous register backdoor still has update threads running. Backdoors with active mirroring should only be set before simulation starts.") # end # self.backdoor = bkdr #endfunction: set_backdoor # # # # clear_hdl_path # #function void uvm_reg_block::clear_hdl_path(string kind = "RTL") # # if (kind == "ALL"): # hdl_paths_pool = new("hdl_paths") # return # end # # if (kind == "") # kind = get_default_hdl_path() # # if (!hdl_paths_pool.exists(kind)): # `uvm_warning("RegModel",{"Unknown HDL Abstraction '",kind,"'"}) # return # end # # hdl_paths_pool.delete(kind) #endfunction # # # get_hdl_path # #function void uvm_reg_block::get_hdl_path(ref string paths[$], input string kind = "") # # uvm_queue #(string) hdl_paths # # if (kind == "") # kind = get_default_hdl_path() # # if (!has_hdl_path(kind)): # `uvm_error("RegModel",{"Block does not have hdl path defined for abstraction '",kind,"'"}) # return # end # # hdl_paths = hdl_paths_pool.get(kind) # # for (int i=0; i<hdl_paths.size();i++) # paths.push_back(hdl_paths.get(i)) # #endfunction # #---------------------------------- # Group- Basic Object Operations #---------------------------------- # # do_print #function void uvm_reg_block::do_print (uvm_printer printer) # super.do_print(printer) # # foreach(blks[i]): # uvm_reg_block b = i # uvm_object obj = b # printer.print_object(obj.get_name(), obj) # end # # foreach(regs[i]): # uvm_reg r = i # uvm_object obj = r # printer.print_object(obj.get_name(), obj) # end # # foreach(vregs[i]): # uvm_vreg r = i # uvm_object obj = r # printer.print_object(obj.get_name(), obj) # end # # foreach(mems[i]): # uvm_mem m = i # uvm_object obj = m # printer.print_object(obj.get_name(), obj) # end # # foreach(maps[i]): # UVMRegMap m = i # uvm_object obj = m # printer.print_object(obj.get_name(), obj) # end # #endfunction # # # # clone # #function uvm_object uvm_reg_block::clone() # `uvm_fatal("RegModel","RegModel blocks cannot be cloned") # return None #endfunction # # do_copy # #function void uvm_reg_block::do_copy(uvm_object rhs) # `uvm_fatal("RegModel","RegModel blocks cannot be copied") #endfunction # # # do_compare # #function bit uvm_reg_block::do_compare (uvm_object rhs, # uvm_comparer comparer) # `uvm_warning("RegModel","RegModel blocks cannot be compared") # return 0 #endfunction # # # do_pack # #function void uvm_reg_block::do_pack (uvm_packer packer) # `uvm_warning("RegModel","RegModel blocks cannot be packed") #endfunction # # # do_unpack # #function void uvm_reg_block::do_unpack (uvm_packer packer) # `uvm_warning("RegModel","RegModel blocks cannot be unpacked") #endfunction # #// convert2string # #def string uvm_reg_block::convert2string(self): # string image # string maps[] # string blk_maps[] # bit single_map # uvm_endianness_e endian # string prefix = " " # #`ifdef TODO # single_map = 1 # if (map == ""): # self.get_maps(maps) # if (maps.size() > 1) single_map = 0 # end # # if (single_map): # $sformat(image, "%sBlock %s", prefix, self.get_full_name()) # # if (map != "") # $sformat(image, "%s.%s", image, map) # # endian = self.get_endian(map) # # $sformat(image, "%s -- %0d bytes (%s)", image, # self.get_n_bytes(map), endian.name()) # # foreach (blks[i]): # string img # img = blks[i].convert2string({prefix, " "}, blk_maps[i]) # image = {image, "\n", img} # end # # end # else begin # $sformat(image, "%Block %s", prefix, self.get_full_name()) # foreach (maps[i]): # string img # endian = self.get_endian(maps[i]) # $sformat(img, "%s Map \"%s\" -- %0d bytes (%s)", # prefix, maps[i], # self.get_n_bytes(maps[i]), endian.name()) # image = {image, "\n", img} # # self.get_blocks(blks, blk_maps, maps[i]) # foreach (blks[j]): # img = blks[j].convert2string({prefix, " "}, # blk_maps[j]) # image = {image, "\n", img} # end # # self.get_subsys(sys, blk_maps, maps[i]) # foreach (sys[j]): # img = sys[j].convert2string({prefix, " "}, # blk_maps[j]) # image = {image, "\n", img} # end # end # end #`endif # return image