# Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 import logging as log import re from collections import OrderedDict from enum import Enum from typing import Dict, List, Tuple from reggen.ip_block import IpBlock from reggen.inter_signal import InterSignal from reggen.validate import check_int from topgen import lib IM_TYPES = ['uni', 'req_rsp'] IM_ACTS = ['req', 'rsp', 'rcv'] IM_VALID_TYPEACT = {'uni': ['req', 'rcv'], 'req_rsp': ['req', 'rsp']} IM_CONN_TYPE = ['1-to-1', '1-to-N', 'broadcast'] class ImType(Enum): Uni = 1 ReqRsp = 2 class ImAct(Enum): Req = 1 Rsp = 2 Rcv = 3 class ImConn(Enum): OneToOne = 1 # req <-> {rsp,rcv} with same width OneToN = 2 # req width N <-> N x {rsp,rcv}s width 1 Broadcast = 3 # req width 1 <-> N x rcvs width 1 def intersignal_format(req: Dict) -> str: """Determine the signal format of the inter-module connections @param[req] Request struct. It has instance name, package format and etc. """ # TODO: Handle array signal result = "{req}_{struct}".format(req=req["inst_name"], struct=req["name"]) # check signal length if exceeds 100 # 7 : space + . # 3 : _{i|o}( # 6 : _{req|rsp}), req_length = 7 + len(req["name"]) + 3 + len(result) + 6 if req_length > 100: logmsg = "signal {0} length cannot be greater than 100" log.warning(logmsg.format(result)) log.warning("Please consider shorten the instance name") return result def get_suffixes(ims: OrderedDict) -> Tuple[str, str]: """Get suffixes of the struct. TL-UL struct uses `h2d`, `d2h` suffixes for req, rsp pair. """ if ims["package"] == "tlul_pkg" and ims["struct"] == "tl": return ("_h2d", "_d2h") return ("_req", "_rsp") def add_intermodule_connection(obj: OrderedDict, req_m: str, req_s: str, rsp_m: str, rsp_s: str): """Add if doesn't exist the connection Add a connection into obj['inter_module']['connect'] dictionary if doesn't exist. Parameters: obj: Top dictionary object req_m: Requester module name req_s: Requester signal name rsp_m: Responder module name rsp_s: Responder signal name Returns: No return type for this function """ req_key = "{}.{}".format(req_m, req_s) rsp_key = "{}.{}".format(rsp_m, rsp_s) connect = obj["inter_module"]["connect"] if req_key in connect: # check if rsp has data if rsp_key in connect[req_key]: return req_key.append(rsp_key) return # req_key is not in connect: # check if rsp_key if rsp_key in connect: # check if rsp has data if req_key in connect[rsp_key]: return rsp_key.append(req_key) return # Add new key and connect connect[req_key] = [rsp_key] def autoconnect_xbar(topcfg: OrderedDict, name_to_block: Dict[str, IpBlock], xbar: OrderedDict) -> None: # The crossbar is connecting to modules and memories in topcfg, plus # possible external connections. Make indices for the modules and memories # for quick lookup and add some assertions to make sure no name appears in # multiple places. name_to_module = {} for mod in topcfg['module']: assert mod['name'] not in name_to_module if lib.is_inst(mod): name_to_module[mod['name']] = mod name_to_memory = {} for mem in topcfg['memory']: assert mem['name'] not in name_to_memory if lib.is_inst(mem): name_to_memory[mem['name']] = mem # The names of modules and memories should be disjoint assert not (set(name_to_module.keys()) & set(name_to_memory.keys())) external_names = (set(topcfg['inter_module']['top']) | set(topcfg["inter_module"]["external"].keys())) ports = [x for x in xbar["nodes"] if x["type"] in ["host", "device"]] for port in ports: # Here, we expect port_name to either be a single identifier (in which # case, it's taken as the name of some module or memory) to be a dotted # pair MOD.INAME where MOD is the name of some module and INAME is the # associated interface name. name_parts = port['name'].split('.', 1) port_base = name_parts[0] port_iname = name_parts[1] if len(name_parts) > 1 else None esc_name = port['name'].replace('.', '__') if port["xbar"]: if port_iname is not None: log.error('A crossbar connection may not ' 'have a target of the form MOD.INAME (saw {!r})' .format(port['name'])) continue if port["type"] == "host": # Skip as device will add connection continue # Device port adds signal add_intermodule_connection(obj=topcfg, req_m=xbar["name"], req_s="tl_" + esc_name, rsp_m=esc_name, rsp_s="tl_" + xbar["name"]) continue # xbar port case port_mod = name_to_module.get(port_base) port_mem = name_to_memory.get(port_base) assert port_mod is None or port_mem is None if not (port_mod or port_mem): # if not in module, memory, should be existed in top or ext field module_key = "{}.tl_{}".format(xbar["name"], esc_name) if module_key not in external_names: log.error("Inter-module key {} cannot be found in module, " "memory, top, or external lists.".format(module_key)) continue if port_iname is not None and port_mem is not None: log.error('Cannot make connection for {!r}: the base of the name ' 'points to a memory but memories do not support ' 'interface names.' .format(port['name'])) is_host = port['type'] == 'host' # If the hit is a module, it originally came from reggen (via # merge.py's amend_ip() function). In this case, we should have a # BusInterfaces object as well as a list of InterSignal objects. # # If not, this is a memory that will just have a dictionary of inter # signals. if port_mod is not None: block = name_to_block[port_mod['type']] try: sig_name = block.bus_interfaces.find_port_name(is_host, port_iname) except KeyError: log.error('Cannot make {} connection for {!r}: the base of ' 'the target module has no matching bus interface.' .format('host' if is_host else 'device', port['name'])) continue else: inter_signal_list = port_mem['inter_signal_list'] act = 'req' if is_host else 'rsp' matches = [ x for x in inter_signal_list if (x.get('package') == 'tlul_pkg' and x['struct'] == 'tl' and x['act'] == act) ] if not matches: log.error('Cannot make {} connection for {!r}: the memory ' 'has no signal with an action of {}.' .format('host' if is_host else 'device', port['name'], act)) continue assert len(matches) == 1 sig_name = matches[0]['name'] if is_host: add_intermodule_connection(obj=topcfg, req_m=xbar["name"], req_s="tl_" + esc_name, rsp_m=port_base, rsp_s=sig_name) else: add_intermodule_connection(obj=topcfg, req_m=port_base, req_s=sig_name, rsp_m=xbar["name"], rsp_s="tl_" + esc_name) def autoconnect(topcfg: OrderedDict, name_to_block: Dict[str, IpBlock]): """Matching the connection based on the naming rule between {memory, module} <-> Xbar. """ # Add xbar connection to the modules, memories for xbar in topcfg["xbar"]: autoconnect_xbar(topcfg, name_to_block, xbar) def _get_default_name(sig, suffix): """Generate default for a net if one does not already exist. """ # The else case covers the scenario where neither package nor default is provided. # Specifically, the interface is 'logic' and has no default value. # In this situation, just return 0's if sig['default']: return sig['default'] elif sig['package']: return "{}::{}_DEFAULT".format(sig['package'], (sig["struct"] + suffix).upper()) else: return "'0" def elab_intermodule(topcfg: OrderedDict): """Check the connection of inter-module and categorize them In the top template, it uses updated inter_module fields to create connections between the modules (incl. memories). This function is to create and check the validity of the connections `inter_module` using IPs' `inter_signal_list`. """ list_of_intersignals = [] if "inter_signal" not in topcfg: topcfg["inter_signal"] = OrderedDict() # Gather the inter_signal_list instances = topcfg["module"] + topcfg["memory"] + topcfg["xbar"] + \ topcfg["port"] for x in instances: old_isl = x.get('inter_signal_list') if old_isl is None: continue new_isl = [] for entry in old_isl: # Convert any InterSignal objects to the expected dictionary format. sig = (entry.as_dict() if isinstance(entry, InterSignal) else entry.copy()) # Add instance name to the entry and add to list_of_intersignals sig["inst_name"] = x["name"] list_of_intersignals.append(sig) new_isl.append(sig) x['inter_signal_list'] = new_isl # Add field to the topcfg topcfg["inter_signal"]["signals"] = list_of_intersignals # TODO: Cross check Can be done here not in validate as ipobj is not # available in validate error = check_intermodule(topcfg, "Inter-module Check") assert error == 0, "Inter-module validation is failed cannot move forward." # intermodule definitions = [] # Check the originator # As inter-module connection allow only 1:1, 1:N, or N:1, pick the most # common signals. If a requester connects to multiple responders/receivers, # the requester is main connector so that the definition becomes array. # # For example: # inter_module: { # 'connect': { # 'pwr_mgr.pwrup': ['lc.pwrup', 'otp.pwrup'] # } # } # The tool adds `struct [1:0] pwr_mgr_pwrup` # It doesn't matter whether `pwr_mgr.pwrup` is requester or responder. # If the key is responder type, then the connection is made in reverse, # such that `lc.pwrup --> pwr_mgr.pwrup[0]` and # `otp.pwrup --> pwr_mgr.pwrup[1]` uid = 0 # Unique connection ID across the top for req, rsps in topcfg["inter_module"]["connect"].items(): log.info("{req} --> {rsps}".format(req=req, rsps=rsps)) # Split index req_module, req_signal, req_index = filter_index(req) # get the module signal req_struct = find_intermodule_signal(list_of_intersignals, req_module, req_signal) # decide signal format based on the `key` sig_name = intersignal_format(req_struct) req_struct["top_signame"] = sig_name # Find package in req, rsps if "package" in req_struct: package = req_struct["package"] else: for rsp in rsps: rsp_module, rsp_signal, rsp_index = filter_index(rsp) rsp_struct = find_intermodule_signal(list_of_intersignals, rsp_module, rsp_signal) if "package" in rsp_struct: package = rsp_struct["package"] break if not package: package = "" # Add to definition if req_struct["type"] == "req_rsp": req_suffix, rsp_suffix = get_suffixes(req_struct) req_default = _get_default_name(req_struct, req_suffix) rsp_default = _get_default_name(req_struct, rsp_suffix) # based on the active direction of the req_struct, one of the directions does not # need a default since it will be an output if (req_struct["act"] == 'req'): req_default = '' else: rsp_default = '' # Add two definitions definitions.append( OrderedDict([('package', package), ('struct', req_struct["struct"] + req_suffix), ('signame', sig_name + "_req"), ('width', req_struct["width"]), ('type', req_struct["type"]), ('end_idx', req_struct["end_idx"]), ('act', req_struct["act"]), ('suffix', "req"), ('default', req_default)])) definitions.append( OrderedDict([('package', package), ('struct', req_struct["struct"] + rsp_suffix), ('signame', sig_name + "_rsp"), ('width', req_struct["width"]), ('type', req_struct["type"]), ('end_idx', req_struct["end_idx"]), ('act', req_struct["act"]), ('suffix', "rsp"), ('default', rsp_default)])) else: # unidirection default = _get_default_name(req_struct, "") definitions.append( OrderedDict([('package', package), ('struct', req_struct["struct"]), ('signame', sig_name), ('width', req_struct["width"]), ('type', req_struct["type"]), ('end_idx', req_struct["end_idx"]), ('act', req_struct["act"]), ('suffix', ""), ('default', default)])) req_struct["index"] = -1 for i, rsp in enumerate(rsps): # Split index rsp_module, rsp_signal, rsp_index = filter_index(rsp) rsp_struct = find_intermodule_signal(list_of_intersignals, rsp_module, rsp_signal) # determine the signal name rsp_struct["top_signame"] = sig_name if req_struct["type"] == "uni" and req_struct[ "top_type"] == "broadcast": rsp_struct["index"] = -1 elif rsp_struct["width"] == req_struct["width"] and len(rsps) == 1: rsp_struct["index"] = -1 else: rsp_struct["index"] = -1 if req_struct["width"] == 1 else i # Assume it is logic # req_rsp doesn't allow logic if req_struct["struct"] == "logic": assert req_struct[ "type"] != "req_rsp", "logic signal cannot have req_rsp type" # increase Unique ID uid += 1 # TODO: Check unconnected port if "top" not in topcfg["inter_module"]: topcfg["inter_module"]["top"] = [] for s in topcfg["inter_module"]["top"]: sig_m, sig_s, sig_i = filter_index(s) assert sig_i == -1, 'top net connection should not use bit index' sig = find_intermodule_signal(list_of_intersignals, sig_m, sig_s) sig_name = intersignal_format(sig) sig["top_signame"] = sig_name if "index" not in sig: sig["index"] = -1 if sig["type"] == "req_rsp": req_suffix, rsp_suffix = get_suffixes(sig) # Add two definitions definitions.append( OrderedDict([('package', sig["package"]), ('struct', sig["struct"] + req_suffix), ('signame', sig_name + "_req"), ('width', sig["width"]), ('type', sig["type"]), ('end_idx', -1), ('default', sig["default"])])) definitions.append( OrderedDict([('package', sig["package"]), ('struct', sig["struct"] + rsp_suffix), ('signame', sig_name + "_rsp"), ('width', sig["width"]), ('type', sig["type"]), ('end_idx', -1), ('default', sig["default"])])) else: # if sig["type"] == "uni": definitions.append( OrderedDict([('package', sig["package"]), ('struct', sig["struct"]), ('signame', sig_name), ('width', sig["width"]), ('type', sig["type"]), ('end_idx', -1), ('default', sig["default"])])) topcfg["inter_module"].setdefault('external', []) topcfg["inter_signal"].setdefault('external', []) for s, port in topcfg["inter_module"]["external"].items(): sig_m, sig_s, sig_i = filter_index(s) assert sig_i == -1, 'top net connection should not use bit index' sig = find_intermodule_signal(list_of_intersignals, sig_m, sig_s) # To make netname `_o` or `_i` sig['external'] = True sig_name = port if port != "" else intersignal_format(sig) # if top signame already defined, then a previous connection category # is already connected to external pin. Sig name is only used for # port definition conn_type = False if "top_signame" not in sig: sig["top_signame"] = sig_name else: conn_type = True if "index" not in sig: sig["index"] = -1 # Add the port definition to top external ports index = sig["index"] netname = sig["top_signame"] if sig["type"] == "req_rsp": req_suffix, rsp_suffix = get_suffixes(sig) if sig["act"] == "req": req_sigsuffix, rsp_sigsuffix = ("_o", "_i") else: req_sigsuffix, rsp_sigsuffix = ("_i", "_o") topcfg["inter_signal"]["external"].append( OrderedDict([('package', sig["package"]), ('struct', sig["struct"] + req_suffix), ('signame', sig_name + "_req" + req_sigsuffix), ('width', sig["width"]), ('type', sig["type"]), ('default', sig["default"]), ('direction', 'out' if sig['act'] == "req" else 'in'), ('conn_type', conn_type), ('index', index), ('netname', netname + req_suffix)])) topcfg["inter_signal"]["external"].append( OrderedDict([('package', sig["package"]), ('struct', sig["struct"] + rsp_suffix), ('signame', sig_name + "_rsp" + rsp_sigsuffix), ('width', sig["width"]), ('type', sig["type"]), ('default', sig["default"]), ('direction', 'in' if sig['act'] == "req" else 'out'), ('conn_type', conn_type), ('index', index), ('netname', netname + rsp_suffix)])) else: # uni if sig["act"] == "req": sigsuffix = "_o" else: sigsuffix = "_i" topcfg["inter_signal"]["external"].append( OrderedDict([('package', sig.get("package", "")), ('struct', sig["struct"]), ('signame', sig_name + sigsuffix), ('width', sig["width"]), ('type', sig["type"]), ('default', sig["default"]), ('direction', 'out' if sig['act'] == "req" else 'in'), ('conn_type', conn_type), ('index', index), ('netname', netname)])) for sig in topcfg["inter_signal"]["signals"]: # Check if it exist in definitions if "top_signame" in sig: continue # Set index to -1 sig["index"] = -1 # TODO: Handle the unconnected port rule if "definitions" not in topcfg["inter_signal"]: topcfg["inter_signal"]["definitions"] = definitions def filter_index(signame: str) -> Tuple[str, str, int]: """If the signal has array indicator `[N]` then split and return name and array index. If not, array index is -1. param signame module.sig{[N]} result (module_name, signal_name, array_index) """ m = re.match(r'(\w+)\.(\w+)(\[(\d+)\])*', signame) if not m: # Cannot match the pattern return "", "", -1 if m.group(3): # array index is not None return m.group(1), m.group(2), m.group(4) return m.group(1), m.group(2), -1 def find_intermodule_signal(sig_list, m_name, s_name) -> Dict: """Return the intermodule signal structure """ filtered = [ x for x in sig_list if x["name"] == s_name and x["inst_name"] == m_name ] if len(filtered) == 1: return filtered[0] log.error("Found {num} entry/entries for {m_name}.{s_name}:".format( num=len(filtered), m_name=m_name, s_name=s_name)) return None # Validation def check_intermodule_field(sig: OrderedDict, prefix: str = "") -> Tuple[int, OrderedDict]: error = 0 # type check if sig["type"] not in IM_TYPES: log.error("{prefix} Inter_signal {name} " "type {type} is incorrect.".format(prefix=prefix, name=sig["name"], type=sig["type"])) error += 1 if sig["act"] not in IM_ACTS: log.error("{prefix} Inter_signal {name} " "act {act} is incorrect.".format(prefix=prefix, name=sig["name"], act=sig["act"])) error += 1 # Check if type and act are matched if error == 0: if sig["act"] not in IM_VALID_TYPEACT[sig['type']]: log.error("{type} and {act} of {name} are not a valid pair." "".format(type=sig['type'], act=sig['act'], name=sig['name'])) error += 1 # Check 'width' field width = 1 if "width" not in sig: sig["width"] = 1 elif not isinstance(sig["width"], int): width, err = check_int(sig["width"], sig["name"]) if err: log.error("{prefix} Inter-module {inst}.{sig} 'width' " "should be int type.".format(prefix=prefix, inst=sig["inst_name"], sig=sig["name"])) error += 1 else: # convert to int value sig["width"] = width # Add empty string if no explicit default for dangling pins is given. # In that case, dangling pins of type struct will be tied to the default # parameter in the corresponding package, and dangling pins of type logic # will be tied off to '0. if "default" not in sig: sig["default"] = "" if "package" not in sig: sig["package"] = "" return error, sig def find_otherside_modules(topcfg: OrderedDict, m, s) -> List[Tuple[str, str, str]]: """Find far-end port based on given module and signal name """ # TODO: handle special cases special_inst_names = { ('main', 'tl_ram_main'): ('tl_adapter_ram_main', 'tl'), ('main', 'tl_eflash'): ('tl_adapter_eflash', 'tl'), ('peri', 'tl_ram_ret_aon'): ('tl_adapter_ram_ret_aon', 'tl'), ('peri', 'tl_ast'): ('ast', 'tl') } special_result = special_inst_names.get((m, s)) if special_result is not None: return [('top', special_result[0], special_result[1])] signame = "{}.{}".format(m, s) for req, rsps in topcfg["inter_module"]["connect"].items(): if req.startswith(signame): # return rsps after splitting module instance name and the port result = [] for rsp in rsps: rsp_m, rsp_s, rsp_i = filter_index(rsp) result.append(('connect', rsp_m, rsp_s)) return result for rsp in rsps: if signame == rsp: req_m, req_s, req_i = filter_index(req) return [('connect', req_m, req_s)] # if reaches here, it means either the format is wrong, or floating port. log.error("`find_otherside_modules()`: " "No such signal {}.{} exists.".format(m, s)) return [] def check_intermodule(topcfg: Dict, prefix: str) -> int: if "inter_module" not in topcfg: return 0 total_error = 0 for req, rsps in topcfg["inter_module"]["connect"].items(): error = 0 # checking the key, value are in correct format # Allowed format # 1. module.signal # 2. module.signal[index] // Remember array is not yet supported # // But allow in format checker # # Example: # inter_module: { # 'connect': { # 'flash_ctrl.flash': ['eflash.flash_ctrl'], # 'life_cycle.provision': ['debug_tap.dbg_en', 'dft_ctrl.en'], # 'otp.pwr_hold': ['pwrmgr.peri[0]'], # 'flash_ctrl.pwr_hold': ['pwrmgr.peri[1]'], # } # } # # If length of value list is > 1, then key should be array (width need to match) # If key is format #2, then length of value list shall be 1 # If one of the value is format #2, then the key should be 1 bit width and # entries of value list should be 1 req_m, req_s, req_i = filter_index(req) if req_s == "": log.error( "Cannot parse the inter-module signal key '{req}'".format( req=req)) error += 1 # Check rsps signal format is list if not isinstance(rsps, list): log.error("Value of key '{req}' should be a list".format(req=req)) error += 1 continue req_struct = find_intermodule_signal(topcfg["inter_signal"]["signals"], req_m, req_s) err, req_struct = check_intermodule_field(req_struct) error += err if req_i != -1 and len(rsps) != 1: # Array format should have one entry log.error( "If key {req} has index, only one entry is allowed.".format( req=req)) error += 1 total_width = 0 widths = [] # Check rsp format for i, rsp in enumerate(rsps): rsp_m, rsp_s, rsp_i = filter_index(rsp) if rsp_s == "": log.error( "Cannot parse the inter-module signal key '{req}->{rsp}'". format(req=req, rsp=rsp)) error += 1 rsp_struct = find_intermodule_signal( topcfg["inter_signal"]["signals"], rsp_m, rsp_s) err, rsp_struct = check_intermodule_field(rsp_struct) error += err total_width += rsp_struct["width"] widths.append(rsp_struct["width"]) # Type check # If no package was declared, it is declared with an empty string if not rsp_struct["package"]: rsp_struct["package"] = req_struct.get("package", "") elif req_struct["package"] != rsp_struct["package"]: log.error( "Inter-module package should be matched: " "{req}->{rsp} exp({expected}), actual({actual})".format( req=req_struct["name"], rsp=rsp_struct["name"], expected=req_struct["package"], actual=rsp_struct["package"])) error += 1 if req_struct["type"] != rsp_struct["type"]: log.error( "Inter-module type should be matched: " "{req}->{rsp} exp({expected}), actual({actual})".format( req=req_struct["name"], rsp=rsp_struct["name"], expected=req_struct["type"], actual=rsp_struct["type"])) error += 1 # If len(rsps) is 1, then the width should be matched to req if req_struct["width"] != 1: if rsp_struct["width"] not in [1, req_struct["width"]]: log.error( "If req {req} is an array, " "rsp {rsp} shall be non-array or array with same width" .format(req=req, rsp=rsp)) error += 1 elif rsp_i != -1: # If rsp has index, req should be width 1 log.error( "If rsp {rsp} has an array index, only one-to-one map is allowed." .format(rsp=rsp)) error += 1 # Determine if broadcast or one-to-N log.debug("Handling inter-sig {} {}".format(req_struct['name'], total_width)) req_struct["end_idx"] = -1 if req_struct["width"] > 1 or len(rsps) != 1: # If req width is same to the every width of rsps ==> broadcast if len(rsps) * [req_struct["width"]] == widths: log.debug("broadcast type") req_struct["top_type"] = "broadcast" # If req width is same as total width of rsps ==> one-to-N elif req_struct["width"] == total_width: log.debug("one-to-N type") req_struct["top_type"] = "one-to-N" # one-to-N connection is not fully populated elif req_struct["width"] > total_width: log.debug("partial one-to-N type") req_struct["top_type"] = "partial-one-to-N" req_struct["end_idx"] = len(rsps) # If not, error else: log.error("'uni' type connection {req} should be either " "OneToN or Broadcast".format(req=req)) error += 1 elif req_struct["type"] == "uni": # one-to-one connection req_struct["top_type"] = "broadcast" # If req is array, it is not allowed to have partial connections. # Doing for loop again here: Make code separate from other checker # for easier maintenance total_error += error if error != 0: # Skip the check continue for item in topcfg["inter_module"]["top"] + list( topcfg["inter_module"]["external"].keys()): sig_m, sig_s, sig_i = filter_index(item) if sig_i != -1: log.error("{item} cannot have index".format(item=item)) total_error += 1 sig_struct = find_intermodule_signal(topcfg["inter_signal"]["signals"], sig_m, sig_s) err, sig_struct = check_intermodule_field(sig_struct) total_error += err return total_error # Template functions def im_defname(obj: OrderedDict) -> str: """return definition struct name e.g. flash_ctrl::flash_req_t """ if obj["package"] == "": # should be logic return "logic" return "{package}::{struct}_t".format(package=obj["package"], struct=obj["struct"]) def im_netname(sig: OrderedDict, suffix: str = "", default_name=False) -> str: """return top signal name with index It also adds suffix for external signal. The default name input forces function to return default name, even if object has a connection. """ # Basic check and add missing fields err, obj = check_intermodule_field(sig) assert not err # Floating signals # TODO: Find smarter way to assign default? if "top_signame" not in obj or default_name: if obj["act"] == "req" and suffix == "req": return "" if obj["act"] == "rsp" and suffix == "rsp": return "" if obj["act"] == "req" and suffix == "rsp": # custom default has been specified if obj["default"]: return obj["default"] if obj["package"] == "tlul_pkg" and obj["struct"] == "tl": return "{package}::{struct}_D2H_DEFAULT".format( package=obj["package"], struct=obj["struct"].upper()) return "{package}::{struct}_RSP_DEFAULT".format( package=obj["package"], struct=obj["struct"].upper()) if obj["act"] == "rsp" and suffix == "req": # custom default has been specified if obj["default"]: return obj["default"] if obj.get("package") == "tlul_pkg" and obj["struct"] == "tl": return "{package}::{struct}_H2D_DEFAULT".format( package=obj["package"], struct=obj["struct"].upper()) # default is used for dangling ports in definitions. # the struct name already has `_req` suffix return "{package}::{struct}_REQ_DEFAULT".format( package=obj.get("package", ''), struct=obj["struct"].upper()) if obj["act"] == "rcv" and suffix == "" and obj["struct"] == "logic": # custom default has been specified if obj["default"]: return obj["default"] return "'0" if obj["act"] == "rcv" and suffix == "": # custom default has been specified if obj["default"]: return obj["default"] return "{package}::{struct}_DEFAULT".format( package=obj["package"], struct=obj["struct"].upper()) return "" # Connected signals assert suffix in ["", "req", "rsp"] suffix_s = "_{suffix}".format(suffix=suffix) if suffix != "" else suffix # External signal handling if "external" in obj and obj["external"]: pairs = { # act , suffix: additional suffix ("req", "req"): "_o", ("req", "rsp"): "_i", ("rsp", "req"): "_i", ("rsp", "rsp"): "_o", ("req", ""): "_o", ("rcv", ""): "_i" } suffix_s += pairs[(obj['act'], suffix)] return "{top_signame}{suffix}{index}".format( top_signame=obj["top_signame"], suffix=suffix_s, index=lib.index(obj["index"])) def im_portname(obj: OrderedDict, suffix: str = "") -> str: """return IP's port name e.g signame_o for requester req signal """ act = obj['act'] name = obj['name'] if suffix == "": suffix_s = "_o" if act == "req" else "_i" elif suffix == "req": suffix_s = "_o" if act == "req" else "_i" else: suffix_s = "_o" if act == "rsp" else "_i" return name + suffix_s def get_dangling_im_def(objs: OrderedDict) -> str: """return partial inter-module definitions Dangling intermodule connections happen when a one-to-N assignment is not fully populated. This can result in two types of dangling: - outgoing requests not used - incoming responses not driven The determination of which category we fall into follows similar rules as those used by im_netname. When the direction of the net is the same as the active direction of the the connecting module, it is "unused". When the direction of the net is opposite of the active direction of the the connecting module, it is "undriven". As an example, edn is defined as "rsp" of a "req_rsp" pair. It is also used as the "active" module in inter-module connection. If there are not enough connecting modules, the 'req' line is undriven, while the 'rsp' line is unused. """ unused_def = [obj for obj in objs if obj['end_idx'] > 0 and obj['act'] == obj['suffix']] undriven_def = [obj for obj in objs if obj['end_idx'] > 0 and (obj['act'] == 'req' and obj['suffix'] == 'rsp' or obj['act'] == 'rsp' and obj['suffix'] == 'req')] return unused_def, undriven_def