| # 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', 'io'] | 
 | IM_ACTS = ['req', 'rsp', 'rcv', 'none'] | 
 | IM_VALID_TYPEACT = {'uni': ['req', 'rcv'], 'req_rsp': ['req', 'rsp'], 'io': ['none']} | 
 | 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 | 
 |         sig["conn_type"] = conn_type | 
 |  | 
 |         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)])) | 
 |         elif sig["type"] == "io": | 
 |             sigsuffix = "_io" | 
 |             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', "inout"), | 
 |                              ('conn_type', conn_type), | 
 |                              ('index', index), | 
 |                              ('netname', netname)])) | 
 |         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 = { | 
 |         ('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 | 
 |  | 
 |  | 
 | def get_direction(sig): | 
 |     if sig["direction"] == "in": | 
 |         return "input " | 
 |     elif sig["direction"] == "out": | 
 |         return "output" | 
 |     elif sig["direction"] == "inout": | 
 |         return "inout " | 
 |  | 
 |  | 
 | # Template functions | 
 | def im_defname(obj: OrderedDict) -> str: | 
 |     """return definition struct name | 
 |  | 
 |     e.g. flash_ctrl::flash_req_t | 
 |     """ | 
 |     if obj["type"] == "io": | 
 |         # io type, don't declare anything | 
 |         return "" | 
 |  | 
 |     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", "io"] | 
 |  | 
 |     # io types have no role | 
 |     if suffix == "io": | 
 |         suffix_s = "" | 
 |     else: | 
 |         suffix_s = "_{suffix}".format(suffix=suffix) if suffix != "" else suffix | 
 |  | 
 |     # External signal handling | 
 |     if "external" in obj and obj["external"]: | 
 |         if obj["conn_type"]: | 
 |             pairs = { | 
 |                 # act , suffix: additional suffix | 
 |                 ("req", "req"): "", | 
 |                 ("req", "rsp"): "_i", | 
 |                 ("rsp", "req"): "_i", | 
 |                 ("rsp", "rsp"): "", | 
 |                 ("req", ""): "", | 
 |                 ("rcv", ""): "_i", | 
 |                 ("none", "io"): "" | 
 |             } | 
 |         else: | 
 |             pairs = { | 
 |                 # act , suffix: additional suffix | 
 |                 ("req", "req"): "_o", | 
 |                 ("req", "rsp"): "_i", | 
 |                 ("rsp", "req"): "_i", | 
 |                 ("rsp", "rsp"): "_o", | 
 |                 ("req", ""): "_o", | 
 |                 ("rcv", ""): "_i", | 
 |                 ("none", "io"): "_io" | 
 |             } | 
 |         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" | 
 |     elif suffix == "io": | 
 |         suffix_s = "_io" | 
 |     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 |