[topgen/tlgen] Multi-tiered Xbar
The script is to support multi-tiered crossbars on top_earlgrey.
Now any crossbar can have connections to other crossbars. It is allowed
only one connection between two crossbars at this time.
If `xbar: "true"` is defined in the node, the tool does below:
- Recognize it as a crossbar connection
- Gather downstream addresses and calculate the common address range (*)
- If device is a port to another crossbar, the address steering compares
list of address range not a single `address_from`, `address_to`
Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/ip/tlul/rtl/tlul_fifo_sync.sv b/hw/ip/tlul/rtl/tlul_fifo_sync.sv
index bc772d3..34aced7 100644
--- a/hw/ip/tlul/rtl/tlul_fifo_sync.sv
+++ b/hw/ip/tlul/rtl/tlul_fifo_sync.sv
@@ -72,7 +72,7 @@
tl_d_i.d_size ,
tl_d_i.d_source,
tl_d_i.d_sink ,
- tl_d_i.d_data ,
+ (tl_d_i.d_opcode == tlul_pkg::AccessAckData) ? tl_d_i.d_data : '0 ,
tl_d_i.d_user ,
tl_d_i.d_error ,
spare_rsp_i}),
diff --git a/hw/top_earlgrey/data/top_earlgrey.sv.tpl b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
index c0f3207..f41c7a1 100644
--- a/hw/top_earlgrey/data/top_earlgrey.sv.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
@@ -102,6 +102,22 @@
tl_d2h_t tl_${m["name"]}_d_d2h;
% endfor
+## Xbar connection
+% for xbar in top["xbar"]:
+<%
+ xbar_devices = [x for x in xbar["nodes"] if x["type"] == "device" and x["xbar"]]
+%>\
+ % for node in xbar_devices:
+ tl_h2d_t tl_${xbar["name"]}_h_h2d;
+ tl_d2h_t tl_${xbar["name"]}_h_d2h;
+ tl_h2d_t tl_${node["name"]}_d_h2d;
+ tl_d2h_t tl_${node["name"]}_d_d2h;
+
+ assign tl_${xbar["name"]}_h_h2d = tl_${node["name"]}_d_h2d;
+ assign tl_${node["name"]}_d_d2h = tl_${xbar["name"]}_h_d2h;
+ % endfor
+% endfor
+
//reset wires declaration
% for reset in top['resets']:
logic ${reset['name']}_rst_n;
@@ -622,8 +638,8 @@
% endfor
.scanmode_i
-% endfor
);
+% endfor
% if "pinmux" in top:
// Pinmux connections
diff --git a/util/tlgen/elaborate.py b/util/tlgen/elaborate.py
index 494eec6..5ae5227 100644
--- a/util/tlgen/elaborate.py
+++ b/util/tlgen/elaborate.py
@@ -166,7 +166,7 @@
continue
if unode.node_type == NodeType.SOCKET_1N:
- idx = unode.ds.index(device.us)
+ idx = unode.ds.index(device.us[0])
unode.dpass = unode.dpass ^ (
1 << idx) if no_bypass else unode.dpass
diff --git a/util/tlgen/item.py b/util/tlgen/item.py
index 841edfe..1c43fa7 100644
--- a/util/tlgen/item.py
+++ b/util/tlgen/item.py
@@ -11,7 +11,6 @@
a Node can be a host port, output of async_fifo, port in a socket,
or a device port.
"""
-
def __init__(self, us, ds):
self.us = us
self.ds = ds
@@ -45,12 +44,12 @@
resets = [] # Resets # resets of the node
# e.g. async_fifo in : clk_core , out : clk_main
-
# If NodeType is Socket out from 1:N then address steering is used
# But this value is also propagated up to a Host from multiple Devices
# Device Node should have address_from, address_to
- address_from = 0 #: int
- address_to = 0 #: int
+ #address_from = 0 #: int
+ #address_to = 0 #: int
+ addr_range = []
us = [] # Edges # Number of Ports depends on the NodeType
# 1 for Host, Device, 2 for Async FIFO, N for Sockets
@@ -72,3 +71,4 @@
self.resets = [reset]
self.us = []
self.ds = []
+ self.addr_range = []
diff --git a/util/tlgen/lib.py b/util/tlgen/lib.py
new file mode 100644
index 0000000..528de26
--- /dev/null
+++ b/util/tlgen/lib.py
@@ -0,0 +1,21 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+import math
+import logging as log
+
+
+def is_pow2(v):
+ """Return true if value is power of two
+ """
+ if not isinstance(v, int):
+ log.warning("is_pow2 received non-integer value {}".format(v))
+ return False
+ t = 1
+ while t <= v:
+ if t == v:
+ return True
+ t = t * 2
+
+ return False
diff --git a/util/tlgen/validate.py b/util/tlgen/validate.py
index 11b6f31..9cf2813 100644
--- a/util/tlgen/validate.py
+++ b/util/tlgen/validate.py
@@ -46,13 +46,17 @@
},
'optional': {
'clock': ['s', 'main clock of the port'],
+ 'reset': ['s', 'main reset of the port'],
'base_addr': ['d', 'Base address of the device'\
' It is required for the device'],
'size_byte': ['d', 'Memory space of the device'\
' It is required for the device'],
'pipeline': ['pb', 'If true, pipeline is added in front of the port'],
'pipeline_byp': ['pb', 'Pipeline bypass. If true, '\
- 'request/response are not latched']
+ 'request/response are not latched'],
+ 'inst_type': ['s', 'Instance type'],
+ 'xbar': ['pb', 'If true, the node is connected to another Xbar'],
+ 'xbar_addr': ['l', 'Xbar address. List type']
},
'added': {}
}
@@ -65,12 +69,14 @@
'name': ['s', 'Name of the crossbar'],
'clock': ['s', 'Main clock. Internal components use this clock.'\
' If not specified, it is assumed to be in main clock domain'],
+ 'reset': ['s', 'Main reset'],
'connections':
['g', "List of edge. Key is host, entry in value list is device"],
+ 'clock_connections': ['g', 'list of clocks'],
'nodes': ['lg', node]
},
'optional': {
- 'type': ['s', 'Indicate Hjson type. "xbar" always if exist']
+ 'type': ['s', 'Indicate Hjson type. "xbar" always if exist'],
},
'added': {
'reset_connections': ['g', "Generated by topgen. Key is the reset signal inside IP"\
@@ -145,6 +151,9 @@
if err:
error += 1
+ elif checker[0] == 'l':
+ if not isinstance(obj[k], list):
+ error += 1
else:
log.error(prefix_name +
" is not supported in this configuration format")
@@ -164,6 +173,7 @@
elif t == "socket_m1":
return NodeType.SOCKET_M1
+ log.error("Cannot process type {}".format(t))
raise
@@ -227,13 +237,14 @@
clock=clock,
reset=reset)
- if node.node_type == NodeType.DEVICE:
+ if node.node_type == NodeType.DEVICE and nodeobj["xbar"] == False:
# Add address obj["base_addr"], obj["size"])
- node.address_from = int(nodeobj["base_addr"], 0)
+ node.xbar = False
+ address_from = int(nodeobj["base_addr"], 0)
size = int(nodeobj["size_byte"], 0)
- node.address_to = node.address_from + size - 1
+ address_to = address_from + size - 1
- addr = (node.address_from, node.address_to)
+ addr = (address_from, address_to)
if checkAddressOverlap(addr, addr_ranges):
log.error(
@@ -242,6 +253,27 @@
raise SystemExit("Address overlapping error occurred")
addr_ranges.append(addr)
+ node.addr_range = [addr]
+
+ if node.node_type == NodeType.DEVICE and nodeobj["xbar"] == True:
+ node.xbar = True
+ node.addr_range = []
+
+ for addr in nodeobj["xbar_addr"]:
+ address_from = int(addr["base_addr"], 0)
+ size = int(addr["size_byte"], 0)
+ address_to = address_from + size - 1
+
+ addr_entry = (address_from, address_to)
+
+ if checkAddressOverlap(addr_entry, addr_ranges):
+ log.error(
+ "Address is overlapping. Check the config. Addr(0x%x - 0x%x)"
+ % (addr_entry[0], addr_entry[1]))
+ raise SystemExit("Address overlapping error occurred")
+
+ addr_ranges.append(addr_entry)
+ node.addr_range.append(addr_entry)
if node.node_type in [NodeType.DEVICE, NodeType.HOST
] and "pipeline" in nodeobj:
diff --git a/util/tlgen/xbar.pkg.sv.tpl b/util/tlgen/xbar.pkg.sv.tpl
index a44d48f..7df61cc 100644
--- a/util/tlgen/xbar.pkg.sv.tpl
+++ b/util/tlgen/xbar.pkg.sv.tpl
@@ -11,13 +11,30 @@
% for device in xbar.devices:
## Address
- localparam logic [31:0] ADDR_SPACE_${device.name.upper().ljust(name_len)} = 32'h ${"%08x" % device.address_from};
+ % if device.xbar == False:
+ localparam logic [31:0] ADDR_SPACE_${device.name.upper().ljust(name_len)} = 32'h ${"%08x" % device.addr_range[0][0]};
+ % else:
+ ## Xbar device
+ localparam logic [${len(device.addr_range)-1}:0][31:0] ADDR_SPACE_${device.name.upper().ljust(name_len)} = {
+ % for addr in device.addr_range:
+ 32'h ${"%08x" % addr[0]}${"," if not loop.last else ""}
+ % endfor
+ };
+ % endif
% endfor
% for device in xbar.devices:
## Mask
- localparam logic [31:0] ADDR_MASK_${device.name.upper().ljust(name_len)} = 32'h ${"%08x" % (device.address_to -
- device.address_from)};
+ % if device.xbar == False:
+ localparam logic [31:0] ADDR_MASK_${device.name.upper().ljust(name_len)} = 32'h ${"%08x" % (device.addr_range[0][1] - device.addr_range[0][0])};
+ % else:
+ ## Xbar
+ localparam logic [${len(device.addr_range)-1}:0][31:0] ADDR_MASK_${device.name.upper().ljust(name_len)} = {
+ % for addr in device.addr_range:
+ 32'h ${"%08x" % (addr[1] - addr[0])}${"," if not loop.last else ""}
+ % endfor
+ };
+ % endif
% endfor
localparam int N_HOST = ${len(xbar.hosts)};
diff --git a/util/tlgen/xbar.rtl.sv.tpl b/util/tlgen/xbar.rtl.sv.tpl
index 44ec805..a5b59a8 100644
--- a/util/tlgen/xbar.rtl.sv.tpl
+++ b/util/tlgen/xbar.rtl.sv.tpl
@@ -6,6 +6,9 @@
// all reset signals should be generated from one reset signal to not make any deadlock
//
// Interconnect
+<%
+ import tlgen.lib as lib
+%>\
% for host in xbar.hosts:
${xbar.repr_tree(host, 0)}
% endfor
@@ -157,15 +160,34 @@
leaf = xbar.get_leaf_from_s1n(block, loop.index);
name_space = "ADDR_SPACE_" + leaf.name.upper();
name_mask = "ADDR_MASK_" + leaf.name.upper();
+ prefix = "if (" if loop.first else "end else if ("
%>\
- % if loop.first:
- if ((${addr_sig} & ~(${name_mask})) == ${name_space}) begin
- % else:
- end else if ((${addr_sig} & ~(${name_mask})) == ${name_space}) begin
- % endif
+ % if len(leaf.addr_range) == 1:
+ % if lib.is_pow2((leaf.addr_range[0][1]-leaf.addr_range[0][0])+1):
+ ${prefix}(${addr_sig} & ~(${name_mask})) == ${name_space}) begin
dev_sel_${block.name} = ${"%d'd%d" % (sel_len, loop.index)};
- % if loop.last:
- end
+ % else:
+ ((${addr_sig} <= (${name_mask} + ${name_space})) &&
+ (${addr_sig} >= ${name_space}))${" ||" if not loop.last else ""}
+ % endif
+ ${"end" if loop.last else ""}
+ % else:
+ ## Xbar device port
+<%
+ num_range = len(leaf.addr_range)
+%>\
+ ${prefix}
+ % for i in range(num_range):
+ % if lib.is_pow2(leaf.addr_range[i][1]-leaf.addr_range[0][0]+1):
+ ((${addr_sig} & ~(${name_mask}[${i}])) == ${name_space}[${i}])${" ||" if not loop.last else ""}
+ % else:
+ ((${addr_sig} <= (${name_mask}[${i}] + ${name_space}[${i}])) &&
+ (${addr_sig} >= ${name_space}[${i}]))${" ||" if not loop.last else ""}
+ % endif
+ % endfor
+ ) begin
+ dev_sel_${block.name} = ${"%d'd%d" % (sel_len, loop.index)};
+ ${"end" if loop.last else ""}
% endif
% endfor
end
diff --git a/util/topgen.py b/util/topgen.py
index 8e2dfd0..41507d2 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -42,12 +42,12 @@
def generate_xbars(top, out_path):
- xbar_path = out_path / 'ip/xbar/data/autogen'
- xbar_path.mkdir(parents=True, exist_ok=True)
gencmd = ("// util/topgen.py -t hw/top_earlgrey/data/top_earlgrey.hjson "
"-o hw/top_earlgrey/\n\n")
for obj in top["xbar"]:
+ xbar_path = out_path / 'ip/xbar_{}/data/autogen'.format(obj["name"])
+ xbar_path.mkdir(parents=True, exist_ok=True)
xbar = tlgen.validate(obj)
# Generate output of crossbar with complete fields
@@ -59,13 +59,13 @@
log.error("Elaboration failed." + repr(xbar))
try:
- out_rtl, out_pkg, out_bind = tlgen.generate(xbar)
+ out_rtl, out_pkg, out_core = tlgen.generate(xbar)
except:
log.error(exceptions.text_error_template().render())
- rtl_path = out_path / 'ip/xbar/rtl/autogen'
+ rtl_path = out_path / 'ip/xbar_{}/rtl/autogen'.format(obj["name"])
rtl_path.mkdir(parents=True, exist_ok=True)
- dv_path = out_path / 'ip/xbar/dv/autogen'
+ dv_path = out_path / 'ip/xbar_{}/dv/autogen'.format(obj["name"])
dv_path.mkdir(parents=True, exist_ok=True)
rtl_filename = "xbar_%s.sv" % (xbar.name)
@@ -78,19 +78,20 @@
with pkg_filepath.open(mode='w', encoding='UTF-8') as fout:
fout.write(out_pkg)
- bind_filename = "xbar_%s_bind.sv" % (xbar.name)
- bind_filepath = dv_path / bind_filename
- with bind_filepath.open(mode='w', encoding='UTF-8') as fout:
- fout.write(out_bind)
+ core_filename = "xbar_%s.core" % (xbar.name)
+ core_filepath = rtl_path / core_filename
+ with core_filepath.open(mode='w', encoding='UTF-8') as fout:
+ fout.write(out_core)
+
def generate_alert_handler(top, out_path):
# default values
- esc_cnt_dw=32
- accu_cnt_dw=16
- lfsr_seed=2**31-1
- async_on="'0"
+ esc_cnt_dw = 32
+ accu_cnt_dw = 16
+ lfsr_seed = 2**31 - 1
+ async_on = "'0"
# leave this constant
- n_classes=4
+ n_classes = 4
# check if there are any params to be passed through reggen and placed into
# the generated package
@@ -118,7 +119,7 @@
# set number of alerts to 1 such that the config is still valid
# that input will be tied off
n_alerts = 1
- log.warning("no alerts are defined in the system");
+ log.warning("no alerts are defined in the system")
else:
async_on = ""
for alert in top['alert']:
@@ -132,7 +133,6 @@
log.info("LfsrSeed = %d" % lfsr_seed)
log.info("AsyncOn = %s" % async_on)
-
# Define target path
rtl_path = out_path / 'ip/alert_handler/rtl/autogen'
rtl_path.mkdir(parents=True, exist_ok=True)
@@ -179,8 +179,6 @@
gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
-
-
def generate_plic(top, out_path):
# Count number of interrupts
src = sum([x["width"] if "width" in x else 1 for x in top["interrupt"]])
@@ -439,7 +437,8 @@
"'no' series options cannot be used with 'only' series options")
raise SystemExit(sys.exc_info()[1])
- if not (args.hjson_only or args.plic_only or args.alert_handler_only or args.tpl):
+ if not (args.hjson_only or args.plic_only or args.alert_handler_only or
+ args.tpl):
log.error(
"Template file can be omitted only if '--hjson-only' is true")
raise SystemExit(sys.exc_info()[1])
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index 6b7b3e0..bc3ee71 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -10,8 +10,6 @@
import hjson
-
-
def amend_ip(top, ip):
""" Amend additional information into top module
@@ -124,8 +122,25 @@
}
-def xbar_addhost(xbar, host):
- # TODO: check if host is another crossbar
+def is_xbar(top, name):
+ """Check if the given name is crossbar
+ """
+ xbars = list(filter(lambda node: node["name"] == name, top["xbar"]))
+ if len(xbars) == 0:
+ return False, None
+
+ if len(xbars) > 1:
+ log.error("Matching crossbar {} is more than one.".format(name))
+ raise SystemExit()
+
+ return True, xbars[0]
+
+
+def xbar_addhost(top, xbar, host):
+ """Add host nodes information
+
+ - xbar: bool, true if the host port is from another Xbar
+ """
# Check and fetch host if exists in nodes
obj = list(filter(lambda node: node["name"] == host, xbar["nodes"]))
if len(obj) == 0:
@@ -144,19 +159,26 @@
"pipeline_byp": "true"
}
topxbar["nodes"].append(obj)
- else:
- if 'clock' not in obj[0]:
- obj[0]["clock"] = xbar['clock']
+ return
- if 'reset' not in obj[0]:
- obj[0]["reset"] = xbar["reset"]
+ xbar_bool, xbar_h = is_xbar(top, host)
+ if xbar_bool:
+ # TODO: Handle Host XBAR port (nothing)
+ log.warning("host {} is a crossbar.".format(host))
- 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 "true"
- obj[0]["pipeline_byp"] = obj[0]["pipeline_byp"] if obj[0][
- "pipeline"] == "true" and "pipeline_byp" in obj[0] else "true"
+ obj[0]["xbar"] = xbar_bool
+
+ if 'clock' not in obj[0]:
+ obj[0]["clock"] = xbar['clock']
+
+ if 'reset' not in obj[0]:
+ obj[0]["reset"] = xbar["reset"]
+
+ 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 "true"
+ obj[0]["pipeline_byp"] = obj[0]["pipeline_byp"] if obj[0][
+ "pipeline"] == "true" and "pipeline_byp" in obj[0] else "true"
def process_pipeline_var(node):
@@ -177,6 +199,7 @@
- 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
+ - xbar: bool, true if the device port is another xbar
"""
deviceobj = list(
filter(lambda node: node["name"] == device,
@@ -185,18 +208,29 @@
xbar_list = [x["name"] for x in top["xbar"] if x["name"] != xbar["name"]]
+ # 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
+
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)
+
+ # case 1: Crossbar handling
+ if device in xbar_list:
+ log.warning(
+ "device {} in Xbar {} is connected to another Xbar".format(
+ device, xbar["name"]))
+ assert len(nodeobj) == 1
+ nodeobj[0]["xbar"] = True
+ process_pipeline_var(nodeobj[0])
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 in predefined_modules:
if device == "debug_mem":
if len(nodeobj) == 0:
# Add new debug_mem
@@ -208,6 +242,7 @@
"inst_type": predefined_modules["debug_mem"],
"base_addr": top["debug_mem_base_addr"],
"size_byte": "0x1000",
+ "xbar": False,
"pipeline" : "true",
"pipeline_byp" : "true"
}) # yapf: disable
@@ -217,12 +252,14 @@
node["inst_type"] = predefined_modules["debug_mem"]
node["base_addr"] = top["debug_mem_base_addr"]
node["size_byte"] = "0x1000"
+ node["xbar"] = False
process_pipeline_var(node)
else:
log.error("device %s shouldn't be host type" % device)
return
# case 3: not defined
else:
+ # Crossbar check
log.error(
"device %s doesn't exist in 'module', 'memory', or predefined"
% device)
@@ -240,7 +277,8 @@
"base_addr" : deviceobj[0]["base_addr"],
"size_byte": deviceobj[0]["size"],
"pipeline" : "true",
- "pipeline_byp" : "true"
+ "pipeline_byp" : "true",
+ "xbar" : True if device in xbar_list else False
}) # yapf: disable
else:
@@ -249,6 +287,7 @@
node["inst_type"] = deviceobj[0]["type"]
node["base_addr"] = deviceobj[0]["base_addr"]
node["size_byte"] = deviceobj[0]["size"]
+ node["xbar"] = True if device in xbar_list else False
process_pipeline_var(node)
@@ -286,7 +325,7 @@
device_nodes = set()
for host, devices in xbar["connections"].items():
# add host first
- xbar_addhost(topxbar, host)
+ xbar_addhost(top, topxbar, host)
# add device if doesn't exist
device_nodes.update(devices)
@@ -296,6 +335,146 @@
xbar_adddevice(top, topxbar, device)
+def xbar_cross(xbar, xbars):
+ """Check if cyclic dependency among xbars
+
+ And gather the address range for device port (to another Xbar)
+
+ @param node_name if not "", the function only search downstream
+ devices starting from the node_name
+ @param visited The nodes it visited to reach this port. If any
+ downstream port from node_name in visited, it means
+ circular path exists. It should be fatal error.
+ """
+ # Step 1: Visit devices (gather the address range)
+ log.info("Processing circular path check for {}".format(xbar["name"]))
+ addr = []
+ for node in [
+ x for x in xbar["nodes"]
+ if x["type"] == "device" and "xbar" in x and x["xbar"] == False
+ ]:
+ # TODO: Can this be simplified? (Merging contiguous into one)
+ addr.append((node["base_addr"], node["size_byte"]))
+
+ # Step 2: visit xbar device ports
+ xbar_nodes = [
+ x for x in xbar["nodes"]
+ if x["type"] == "device" and "xbar" in x and x["xbar"] == True
+ ]
+
+ # Now call function to get the device range
+ # the node["name"] is used to find the host_xbar and its connection. The
+ # assumption here is that there's only one connection from crossbar A to
+ # crossbar B.
+ #
+ # device_xbar is the crossbar has a device port with name as node["name"].
+ # host_xbar is the crossbar has a host port with name as node["name"].
+ for node in xbar_nodes:
+ xbar_addr = xbar_cross_node(node["name"], xbar, xbars, visited=[])
+ node["xbar_addr"] = xbar_addr
+
+
+def xbar_cross_node(node_name, device_xbar, xbars, visited=[]):
+ # 1. Get the connected xbar
+ host_xbars = [x for x in xbars if x["name"] == node_name]
+ assert len(host_xbars) == 1
+ host_xbar = host_xbars[0]
+
+ log.info("Processing node {} in Xbar {}.".format(node_name,
+ device_xbar["name"]))
+ result = [] # [(base_addr, size), .. ]
+ # Sweep the devices using connections and gather the address.
+ # If the device is another xbar, call recursive
+ visited.append(host_xbar["name"])
+ devices = host_xbar["connections"][device_xbar["name"]]
+
+ for node in host_xbar["nodes"]:
+ if not node["name"] in devices:
+ continue
+ if "xbar" in node and node["xbar"] == True:
+ if not "xbar_addr" in node:
+ # Deeper dive into another crossbar
+ xbar_addr = xbar_cross_node(node["name"], host_xbar, xbars,
+ visited)
+ node["xbar_addr"] = xbar_addr
+
+ result.append(node["xbar_addr"])
+ continue
+
+ # Normal device
+ result.append({
+ 'base_addr': node["base_addr"],
+ 'size_byte': node["size_byte"]
+ })
+
+ visited.pop()
+
+ # TODO: simplify the result? if contiguous, combine into one?
+ return simplify_addr(result, device_xbar)
+
+
+def simplify_addr(addrs, xbar):
+ """If any contiguous regions exist, concatenate them
+
+ For instance, 0x1000 ~ 0x1FFF , 0x2000~ 0x2FFF ==> 0x1000 ~ 0x2FFF
+
+ @param addrs List of Dict[Addr] : {'base_addr':,'size_byte':}
+ """
+
+ # Sort based on the base addr
+ newlist = sorted(addrs, key=lambda k: int(k['base_addr'], 0))
+ # check if overlap or contiguous
+ result = []
+ for e in newlist:
+ if len(result) == 0:
+ result.append(e)
+ continue
+ # if contiguous
+ if int(e["base_addr"], 0) == int(result[-1]["base_addr"], 0) + int(
+ result[-1]["size_byte"], 0):
+ # update previous entry size
+ result[-1]["size_byte"] = "0x{:x}".format(
+ int(result[-1]["size_byte"], 0) + int(e["size_byte"], 0))
+ continue
+
+ # TODO: If no other device in current xbar between the gap?
+ if no_device_in_range(xbar, result[-1], e):
+ result[-1]["size_byte"] = "0x{:x}".format(
+ int(e["base_addr"], 0) + int(e["size_byte"], 0) -
+ int(result[-1]["base_addr"], 0))
+ continue
+
+ # If overlapping (Should it be consider? TlGen will catch it)
+
+ # Normal case
+ result.append(e)
+
+ # return result
+ return result
+
+
+def no_device_in_range(xbar, f, t):
+ """Check if other devices doesn't overlap with the from <= x < to
+ """
+ from_addr = int(f["base_addr"], 0) + int(f["size_byte"], 0)
+ to_addr = int(t["base_addr"], 0)
+
+ for node in [x for x in xbar["nodes"] if x["type"] == "device"]:
+ if not "base_addr" in node:
+ # Xbar?
+ log.info("Xbar type node cannot be compared in this version.",
+ "Please use in caution")
+ continue
+ b_addr = int(node["base_addr"], 0)
+ e_addr = b_addr + int(node["size_byte"], 0)
+
+ if e_addr <= from_addr or b_addr >= to_addr:
+ # No overlap
+ continue
+ return False
+ return True
+
+
def amend_interrupt(top):
"""Check interrupt_module if exists, or just use all modules
"""
@@ -317,6 +496,7 @@
map(partial(add_prefix_to_signal, prefix=m.lower()),
ip[0]["interrupt_list"]))
+
def amend_alert(top):
"""Check interrupt_module if exists, or just use all modules
"""
@@ -329,8 +509,8 @@
for m in top["alert_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 alert_module" % m)
+ log.warning("Cannot find IP %s which is used in the alert_module" %
+ m)
continue
log.info("Adding alert from module %s" % ip[0]["name"])
@@ -338,6 +518,7 @@
map(partial(add_prefix_to_signal, prefix=m.lower()),
ip[0]["alert_list"]))
+
def amend_pinmux_io(top):
""" Check dio_modules/ mio_modules. If not exists, add all modules to mio
"""
@@ -460,7 +641,6 @@
def merge_top(topcfg, ipobjs, xbarobjs):
gencfg = deepcopy(topcfg)
-
# Combine ip cfg into topcfg
for ip in ipobjs:
amend_ip(gencfg, ip)
@@ -479,6 +659,10 @@
for xbar in xbarobjs:
amend_xbar(gencfg, xbar)
+ # 2nd phase of xbar (gathering the devices address range)
+ for xbar in gencfg["xbar"]:
+ xbar_cross(xbar, gencfg["xbar"])
+
# remove unwanted fields 'debug_mem_base_addr'
gencfg.pop('debug_mem_base_addr', None)