blob: 53018ef206945bec8d2bd8ee075b97484dd341da [file] [log] [blame]
# 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
import sys
from collections import OrderedDict
from copy import deepcopy
from pathlib import Path
from typing import Dict, Optional, Tuple
import hjson
from reggen.ip_block import IpBlock
# Ignore flake8 warning as the function is used in the template
# disable isort formating, as conflicting with flake8
from .intermodule import find_otherside_modules # noqa : F401 # isort:skip
from .intermodule import im_portname, im_defname, im_netname # noqa : F401 # isort:skip
from .intermodule import get_dangling_im_def # noqa : F401 # isort:skip
def is_ipcfg(ip: Path) -> bool: # return bool
log.info("IP Path: %s" % repr(ip))
ip_name = ip.parents[1].name
hjson_name = ip.name
log.info("IP Name(%s) and HJSON name (%s)" % (ip_name, hjson_name))
if ip_name + ".hjson" == hjson_name or ip_name + "_reg.hjson" == hjson_name:
return True
return False
def search_ips(ip_path): # return list of config files
# list the every Hjson file
p = ip_path.glob('*/data/*.hjson')
# filter only ip_name/data/ip_name{_reg|''}.hjson
ips = [x for x in p if is_ipcfg(x)]
log.info("Filtered-in IP files: %s" % repr(ips))
return ips
def is_xbarcfg(xbar_obj):
if "type" in xbar_obj and xbar_obj["type"] == "xbar":
return True
return False
def get_hjsonobj_xbars(xbar_path):
""" Search crossbars Hjson files from given path.
Search every Hjson in the directory and check Hjson type.
It could be type: "top" or type: "xbar"
returns [(name, obj), ... ]
"""
p = xbar_path.glob('*.hjson')
try:
xbar_objs = [
hjson.load(x.open('r'),
use_decimal=True,
object_pairs_hook=OrderedDict) for x in p
]
except ValueError:
raise SystemExit(sys.exc_info()[1])
xbar_objs = [x for x in xbar_objs if is_xbarcfg(x)]
return xbar_objs
def get_module_by_name(top, name):
"""Search in top["module"] by name
"""
module = None
for m in top["module"]:
if m["name"] == name:
module = m
break
return module
def intersignal_to_signalname(top, m_name, s_name) -> str:
# TODO: Find the signal in the `inter_module_list` and get the correct signal name
return "{m_name}_{s_name}".format(m_name=m_name, s_name=s_name)
def get_package_name_by_intermodule_signal(top, struct) -> str:
"""Search inter-module signal package with the struct name
For instance, if `flash_ctrl` has inter-module signal package,
this function returns the package name
"""
instances = top["module"] + top["memory"]
intermodule_instances = [
x["inter_signal_list"] for x in instances if "inter_signal_list" in x
]
for m in intermodule_instances:
if m["name"] == struct and "package" in m:
return m["package"]
return ""
def get_signal_by_name(module, name):
"""Return the signal struct with the type input/output/inout
"""
result = None
for s in module["available_input_list"] + module[
"available_output_list"] + module["available_inout_list"]:
if s["name"] == name:
result = s
break
return result
def add_module_prefix_to_signal(signal, module):
"""Add module prefix to module signal format { name: "sig_name", width: NN }
"""
result = deepcopy(signal)
if "name" not in signal:
raise SystemExit("signal {} doesn't have name field".format(signal))
result["name"] = module + "_" + signal["name"]
result["module_name"] = module
return result
def get_ms_name(name):
"""Split module_name.signal_name to module_name , signal_name
"""
tokens = name.split('.')
if len(tokens) == 0:
raise SystemExit("This to be catched in validate.py")
module = tokens[0]
signal = None
if len(tokens) == 2:
signal = tokens[1]
return module, signal
def parse_pad_field(padstr):
"""Parse PadName[NN...NN] or PadName[NN] or just PadName
"""
match = re.match(r'^([A-Za-z0-9_]+)(\[([0-9]+)(\.\.([0-9]+))?\]|)', padstr)
return match.group(1), match.group(3), match.group(5)
def get_pad_list(padstr):
pads = []
pad, first, last = parse_pad_field(padstr)
if first is None:
first = 0
last = 0
elif last is None:
last = first
first = int(first, 0)
last = int(last, 0)
# width = first - last + 1
for p in range(first, last + 1):
pads.append(OrderedDict([("name", pad), ("index", p)]))
return pads
# Template functions
def ljust(x, width):
return "{:<{width}}".format(x, width=width)
def bitarray(d, width):
"""Print Systemverilog bit array
@param d the bit width of the signal
@param width max character width of the signal group
For instance, if width is 4, the max d value in the signal group could be
9999. If d is 2, then this function pads 3 spaces at the end of the bit
slice.
"[1:0] " <- d:=2, width=4
"[9999:0]" <- max d-1 value
If d is 1, it means array slice isn't necessary. So it returns empty spaces
"""
if d <= 0:
log.error("lib.bitarray: Given value {} is smaller than 1".format(d))
raise ValueError
if d == 1:
return " " * (width + 4) # [x:0] needs 4 more space than char_width
out = "[{}:0]".format(d - 1)
return out + (" " * (width - len(str(d))))
def parameterize(text):
"""Return the value wrapping with quote if not integer nor bits
"""
if re.match(r'(\d+\'[hdb]\s*[0-9a-f_A-F]+|[0-9]+)', text) is None:
return "\"{}\"".format(text)
return text
def index(i: int) -> str:
"""Return index if it is not -1
"""
return "[{}]".format(i) if i != -1 else ""
def get_clk_name(clk):
"""Return the appropriate clk name
"""
if clk == 'main':
return 'clk_i'
else:
return "clk_{}_i".format(clk)
def get_reset_path(reset, domain, reset_cfg):
"""Return the appropriate reset path given name
"""
# find matching node for reset
node_match = [node for node in reset_cfg['nodes'] if node['name'] == reset]
assert len(node_match) == 1
reset_type = node_match[0]['type']
# find matching path
hier_path = ""
if reset_type == "int":
log.debug("{} used as internal reset".format(reset["name"]))
else:
hier_path = reset_cfg['hier_paths'][reset_type]
# find domain selection
domain_sel = ''
if reset_type not in ["ext", "int"]:
domain_sel = "[rstmgr_pkg::Domain{}Sel]".format(domain)
reset_path = ""
if reset_type == "ext":
reset_path = reset
else:
reset_path = "{}rst_{}_n{}".format(hier_path, reset, domain_sel)
return reset_path
def get_unused_resets(top):
"""Return dict of unused resets and associated domain
"""
unused_resets = OrderedDict()
unused_resets = {
reset['name']: domain
for reset in top['resets']['nodes']
for domain in top['power']['domains']
if reset['type'] == 'top' and domain not in reset['domains']
}
log.debug("Unused resets are {}".format(unused_resets))
return unused_resets
def is_templated(module):
"""Returns an indication where a particular module is templated
"""
if "attr" not in module:
return False
elif module["attr"] in ["templated"]:
return True
else:
return False
def is_top_reggen(module):
"""Returns an indication where a particular module is NOT templated
and requires top level specific reggen
"""
if "attr" not in module:
return False
elif module["attr"] in ["reggen_top", "reggen_only"]:
return True
else:
return False
def is_inst(module):
"""Returns an indication where a particular module should be instantiated
in the top level
"""
top_level_module = False
top_level_mem = False
if "attr" not in module:
top_level_module = True
elif module["attr"] in ["normal", "templated", "reggen_top"]:
top_level_module = True
elif module["attr"] in ["reggen_only"]:
top_level_module = False
else:
raise ValueError('Attribute {} in {} is not valid'
.format(module['attr'], module['name']))
if module['type'] in ['rom', 'ram_1p_scr', 'eflash']:
top_level_mem = True
return top_level_mem or top_level_module
def get_base_and_size(name_to_block: Dict[str, IpBlock],
inst: Dict[str, object],
ifname: Optional[str]) -> Tuple[int, int]:
min_device_spacing = 0x1000
block = name_to_block.get(inst['type'])
if block is None:
# If inst isn't the instantiation of a block, it came from some memory.
# Memories have their sizes defined, so we can just look it up there.
bytes_used = int(inst['size'], 0)
# Memories don't have multiple or named interfaces, so this will only
# work if ifname is None.
assert ifname is None
base_addr = inst['base_addr']
else:
# If inst is the instantiation of some block, find the register block
# that corresponds to ifname
rb = block.reg_blocks.get(ifname)
if rb is None:
log.error('Cannot connect to non-existent {} device interface '
'on {!r} (an instance of the {!r} block)'
.format('default' if ifname is None else repr(ifname),
inst['name'], block.name))
bytes_used = 0
else:
bytes_used = 1 << rb.get_addr_width()
base_addr = inst['base_addrs'][ifname]
# Round up to min_device_spacing if necessary
size_byte = max(bytes_used, min_device_spacing)
if isinstance(base_addr, str):
base_addr = int(base_addr, 0)
else:
assert isinstance(base_addr, int)
return (base_addr, size_byte)