tinyriscv/tools/regtool/topgen/intermodule.py

1008 lines
37 KiB
Python

# 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