blob: e7bfb959d1bfad5258657fae8edf120dc0ea6aca [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
from copy import deepcopy
from pathlib import Path, PosixPath
import hjson
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('*/doc/*.hjson')
# filter only ip_name/doc/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) 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 amend_ip(top, ip):
""" Amend additional information into top module
Amended fields:
- size: register space
- clock: converted into ip_clock
- bus_device
- bus_host: none if doesn't exist
- available_input_list: empty list if doesn't exist
- available_output_list: empty list if doesn't exist
- available_inout_list: empty list if doesn't exist
- interrupt_list: empty list if doesn't exist
- (TBD) alert_list: empty list if doesn't exist
"""
ip_list_in_top = [x["name"].lower() for x in top["module"]]
ipname = ip["name"].lower()
if not ipname in ip_list_in_top:
log.info("TOP doens't use the IP %s. Skip" % ip["name"])
return
# Find index of the IP
ip_idx = ip_list_in_top.index(ipname)
ip_module = top["module"][ip_idx]
# Size
if not "size" in ip_module:
ip_module["size"] = "0x%x" % max(ip["gensize"], 0x1000)
elif ip_module["size"] < ip["gensize"]:
log.error(
"given 'size' field in IP %s is smaller than the required space" %
ip_module["name"])
# ip_clock
if "clock" in ip:
ip_module["ip_clock"] = ip["clock"]
else:
ip_module["ip_clock"] = "main"
# bus_device
ip_module["bus_device"] = ip["bus_device"]
# bus_host
if "bus_host" in ip and ip["bus_host"] != "":
ip_module["bus_host"] = ip["bus_host"]
else:
ip_module["bus_host"] = "none"
# available_input_list , available_output_list, available_inout_list
if "available_input_list" in ip:
ip_module["available_input_list"] = ip["available_input_list"]
for i in ip_module["available_input_list"]:
i.pop('desc', None)
i["width"] = int(i["width"])
else:
ip_module["available_input_list"] = []
if "available_output_list" in ip:
ip_module["available_output_list"] = ip["available_output_list"]
for i in ip_module["available_output_list"]:
i.pop('desc', None)
i["width"] = int(i["width"])
else:
ip_module["available_output_list"] = []
if "available_inout_list" in ip:
ip_module["available_inout_list"] = ip["available_inout_list"]
for i in ip_module["available_inout_list"]:
i.pop('desc', None)
i["width"] = int(i["width"])
else:
ip_module["available_inout_list"] = []
# interrupt_list
if "interrupt_list" in ip:
ip_module["interrupt_list"] = ip["interrupt_list"]
for i in ip_module["interrupt_list"]:
i.pop('desc', None)
i["width"] = int(i["width"])
else:
ip_module["interrupt_list"] = []
# (TBD) alert_list
# TODO: Replace this part to be configurable from hjson or template
predefined_modules = {
"corei": "rv_core_ibex",
"cored": "rv_core_ibex",
"dm_sba": "rv_dm",
"debug_mem": "rv_dm"
}
def xbar_addhost(xbar, host):
# TODO: check if host is another crossbar
# Check and fetch host if exists in nodes
obj = list(filter(lambda node: node["name"] == host, xbar["nodes"]))
if len(obj) == 0:
log.warning(
"host %s doesn't exist in the node list. Using default values" %
host)
obj = {
"name": host,
"clock": xbar["clock"],
"type": "host",
"inst_type": "",
"pipeline": "false"
}
topxbar["nodes"].append(obj)
else:
obj[0]["clock"] = xbar["clock"]
obj[0]["inst_type"] = predefined_modules[
host] if host in predefined_modules else ""
obj[0]["pipeline"] = obj[0]["pipeline"] if "pipeline" in obj[
0] else "false"
def xbar_adddevice(top, xbar, device):
"""Add device nodes information
- clock: comes from module if exist. use main top clock for memory as of now
- inst_type: comes from module or memory if exist.
- base_addr: comes from module or memory, or assume rv_plic?
- size_byte: comes from module or memory
"""
deviceobj = list(
filter(lambda node: node["name"] == device,
top["module"] + top["memory"]))
nodeobj = list(filter(lambda node: node["name"] == device, xbar["nodes"]))
xbar_list = [x["name"] for x in top["xbar"] if x["name"] != xbar["name"]]
if len(deviceobj) == 0:
# doesn't exist,
# case 1: another xbar --> check in xbar list
if device in xbar_list and len(nodeobj) == 0:
log.error(
"Another crossbar %s needs to be specified in the 'nodes' list"
% device)
return
# case 2: predefined_modules (debug_mem, rv_plic)
# TODO: Find configurable solution not from predefined but from object?
elif device in predefined_modules:
if device == "debug_mem":
if len(nodeobj) == 0:
# Add new debug_mem
xbar["nodes"].append({
"name": "debug_mem",
"type": "device",
"clock": "main",
"inst_type": predefined_modules["debug_mem"],
"base_addr": top["debug_mem_base_addr"],
"size_byte": "0x1000"
}) # yapf: disable
else:
# Update if exists
node = nodeobj[0]
node["inst_type"] = predefined_modules["debug_mem"]
node["base_addr"] = top["debug_mem_base_addr"]
node["size_byte"] = "0x1000"
else:
log.error("device %s shouldn't be host type" % device)
return
# case 3: not defined
else:
log.error(
"device %s doesn't exist in 'module', 'memory', or predefined"
% device)
return
# Search object from module or memory
elif len(nodeobj) == 0:
# found in module or memory but node object doesn't exist.
xbar["nodes"].append({
"name" : device,
"type" : "device",
"clock" : deviceobj[0]["clock"],
"inst_type" : deviceobj[0]["type"],
"base_addr" : deviceobj[0]["base_addr"],
"size_byte": deviceobj[0]["size"]
}) # yapf: disable
else:
# found and exist in the nodes too
node = nodeobj[0]
node["inst_type"] = deviceobj[0]["type"]
node["base_addr"] = deviceobj[0]["base_addr"]
node["size_byte"] = deviceobj[0]["size"]
def amend_xbar(top, xbar):
"""Amend crossbar informations to the top list
Amended fields
- clock: Adopt from module clock if exists
- inst_type: Module instance some module will be hard-coded
the tool searches module list and memory list then put here
- base_addr: from top["module"]
- size: from top["module"]
"""
xbar_list = [x["name"] for x in top["xbar"]]
if not xbar["name"] in xbar_list:
log.info(
"Xbar %s doesn't belong to the top %s. Check if the xbar doesn't need"
% (xbar["name"], top["name"]))
return
topxbar = list(
filter(lambda node: node["name"] == xbar["name"], top["xbar"]))[0]
topxbar["connections"] = deepcopy(xbar["connections"])
if "nodes" in xbar:
topxbar["nodes"] = deepcopy(xbar["nodes"])
else:
topxbar["nodes"] = []
# Build nodes from 'connections'
device_nodes = set()
for host, devices in xbar["connections"].items():
# add host first
xbar_addhost(topxbar, host)
# add device if doesn't exist
device_nodes.update(devices)
log.info(device_nodes)
for device in device_nodes:
xbar_adddevice(top, topxbar, device)
def prefix_module(module, interrupt_list):
result = []
for i in interrupt_list:
result.append({
"name": module.lower() + "_" + i["name"],
"width": i["width"]
})
return result
def amend_interrupt(top):
"""Check interrupt_module if exists, or just use all modules
"""
if not "interrupt_module" in top:
top["interrupt_module"] = [x["name"] for x in top["module"]]
if not "interrupt" in top or top["interrupt"] == "":
top["interrupt"] = []
for m in top["interrupt_module"]:
ip = list(filter(lambda module: module["name"] == m, top["module"]))
if len(ip) == 0:
log.warning(
"Cannot find IP %s which is used in the interrupt_module" % m)
continue
log.info("Adding interrupts from module %s" % ip[0]["name"])
top["interrupt"] += prefix_module(m, ip[0]["interrupt_list"])
def merge_top(topcfg, ipobjs, xbarobjs):
gencfg = deepcopy(topcfg)
# Combine ip cfg into topcfg
for ip in ipobjs:
amend_ip(gencfg, ip)
# Combine the interrupt (should be processed prior to xbar)
amend_interrupt(gencfg)
# Combine xbar into topcfg
for xbar in xbarobjs:
amend_xbar(gencfg, xbar)
# remove unwanted fields 'debug_mem_base_addr'
gencfg.pop('debug_mem_base_addr', None)
return gencfg