[topgen] Convert Xbar connection to inter-module signal
Purpose: Make Xbar to be comportable IP
This is 2nd modification to make Xbar being comportable IP.
Now topgen reads comportable Xbar hjson object and amends to xbar object
in top cfg. Then the `inter_signal_list` is utilized while connecting
the modules.
Remained Items:
- Defining the connection in `inter_module.connect` is still hand-made.
need to be automated
- memory port is better to use inter_module connection rather than
current manual connection
- `corei`, `cored`, `dm_sba`, `debug_mem` are still manual. Ibex, RV_DM
should have *virtual* comportable IP hjson to make inter-module
connect-able
This commit addresses #3031 partially.
Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/util/topgen.py b/util/topgen.py
index 08f5e74..b2034c5 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -11,6 +11,7 @@
from collections import OrderedDict
from io import StringIO
from pathlib import Path
+from copy import deepcopy
import hjson
from mako import exceptions
@@ -20,6 +21,7 @@
from reggen import gen_dv, gen_rtl, validate
from topgen import (amend_clocks, get_hjsonobj_xbars, merge_top, search_ips,
validate_top)
+from topgen.intermodule import elab_intermodule
# Common header for generated files
genhdr = '''// Copyright lowRISC contributors.
@@ -81,6 +83,21 @@
# generate testbench for xbar
tlgen.generate_tb(xbar, dv_path, "top_" + top["name"])
+ # Read back the comportable IP and amend to Xbar
+ xbar_ipfile = ip_path / ("data/autogen/xbar_%s.hjson" % obj["name"])
+ with xbar_ipfile.open() as fxbar:
+ xbar_ipobj = hjson.load(fxbar,
+ use_decimal=True,
+ object_pairs_hook=OrderedDict)
+
+ # Deepcopy of the inter_signal_list.
+ # As of writing the code, it is not expected to write-back the
+ # read xbar objects into files. Still, as `inter_signal_list` is
+ # modified in the `elab_intermodule()` stage, it is better to keep
+ # the original content.
+ obj["inter_signal_list"] = deepcopy(
+ xbar_ipobj["inter_signal_list"])
+
def generate_alert_handler(top, out_path):
# default values
@@ -837,20 +854,22 @@
completecfg = merge_top(topcfg, ip_objs, xbar_objs)
+ if args.top_ral:
+ generate_top_ral(completecfg, ip_objs, out_path)
+
+ if args.hjson_only:
+ hjson_dir = Path(args.topcfg).parent
genhjson_path = hjson_dir / ("autogen/top_%s.gen.hjson" %
completecfg["name"])
gencmd = (
"// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson --hjson-only "
"-o hw/top_{topname}/\n".format(topname=topname))
- if args.top_ral:
- generate_top_ral(completecfg, ip_objs, out_path)
- else:
- genhjson_path.write_text(genhdr + gencmd +
- hjson.dumps(completecfg, for_json=True))
+ genhjson_path.write_text(genhdr + gencmd +
+ hjson.dumps(completecfg, for_json=True))
- if args.hjson_only:
- log.info("hjson is generated. Exiting...")
+ log.info("hjson is generated. "
+ "Content is absent of inter-module signal. Exiting...")
sys.exit()
if args.no_gen_hjson:
@@ -887,8 +906,23 @@
if not args.no_xbar or args.xbar_only:
generate_xbars(completecfg, out_path)
+ # All IPs are generated. Connect phase now
+ elab_intermodule(completecfg)
+
top_name = completecfg["name"]
+ # Generate top.gen.hjson right before rendering
+ if not args.no_gen_hjson:
+ hjson_dir = Path(args.topcfg).parent
+ genhjson_path = hjson_dir / ("autogen/top_%s.gen.hjson" %
+ completecfg["name"])
+ gencmd = (
+ "// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson --hjson-only "
+ "-o hw/top_{topname}/\n".format(topname=topname))
+
+ genhjson_path.write_text(genhdr + gencmd +
+ hjson.dumps(completecfg, for_json=True))
+
if not args.no_top or args.top_only:
tpl_path = Path(args.tpl)
@@ -951,7 +985,7 @@
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True,
- cwd=str(SRCTREE_TOP))
+ cwd=str(SRCTREE_TOP)) # yapf: disable
# generate chip level xbar TB
tb_files = ["xbar_env_pkg__params.sv", "tb__xbar_connect.sv"]
diff --git a/util/topgen/intermodule.py b/util/topgen/intermodule.py
index 2390457..b179926 100644
--- a/util/topgen/intermodule.py
+++ b/util/topgen/intermodule.py
@@ -58,6 +58,17 @@
return result
+def get_suffixes(ims: OrderedDict) -> (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 elab_intermodule(topcfg: OrderedDict):
"""Check the connection of inter-module and categorize them
@@ -73,7 +84,7 @@
topcfg["inter_signal"] = OrderedDict()
# Gather the inter_signal_list
- instances = topcfg["module"] + topcfg["memory"]
+ instances = topcfg["module"] + topcfg["memory"] + topcfg["xbar"]
intermodule_instances = [x for x in instances if "inter_signal_list" in x]
@@ -144,17 +155,18 @@
# Add to definition
if req_struct["type"] == "req_rsp":
+ req_suffix, rsp_suffix = get_suffixes(req_struct)
# Add two definitions
definitions.append(
OrderedDict([('package', package),
- ('struct', req_struct["struct"] + "_req"),
+ ('struct', req_struct["struct"] + req_suffix),
('signame', sig_name + "_req"),
('width', req_struct["width"]),
('type', req_struct["type"]),
('default', req_struct["default"])]))
definitions.append(
OrderedDict([('package', package),
- ('struct', req_struct["struct"] + "_rsp"),
+ ('struct', req_struct["struct"] + rsp_suffix),
('signame', sig_name + "_rsp"),
('width', req_struct["width"]),
('type', req_struct["type"]),
@@ -218,16 +230,17 @@
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"),
+ ('struct', sig["struct"] + req_suffix),
('signame', sig_name + "_req"),
('width', sig["width"]), ('type', sig["type"]),
('default', sig["default"])]))
definitions.append(
OrderedDict([('package', sig["package"]),
- ('struct', sig["struct"] + "_rsp"),
+ ('struct', sig["struct"] + rsp_suffix),
('signame', sig_name + "_rsp"),
('width', sig["width"]), ('type', sig["type"]),
('default', sig["default"])]))
@@ -258,9 +271,10 @@
# TODO: Handle the suffix `_i`, `_o` correctly.
# For now, external doesn't create _i, _o
if sig["type"] == "req_rsp":
+ req_suffix, rsp_suffix = get_suffixes(sig)
topcfg["inter_signal"]["external"].append(
OrderedDict([('package', sig["package"]),
- ('struct', sig["struct"] + "_req"),
+ ('struct', sig["struct"] + req_suffix),
('signame', sig_name + "_req"),
('width', sig["width"]), ('type', sig["type"]),
('default', sig["default"]),
@@ -268,7 +282,7 @@
'out' if sig['act'] == "req" else 'in')]))
topcfg["inter_signal"]["external"].append(
OrderedDict([('package', sig["package"]),
- ('struct', sig["struct"] + "_rsp"),
+ ('struct', sig["struct"] + rsp_suffix),
('signame', sig_name + "_rsp"),
('width', sig["width"]), ('type', sig["type"]),
('default', sig["default"]),
@@ -628,12 +642,18 @@
# 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["package"] == "tlul_pkg" and obj["struct"] == "tl":
+ return "{package}::{struct}_H2D_DEFAULT".format(
+ package=obj["package"], struct=obj["struct"].upper())
return "{package}::{struct}_REQ_DEFAULT".format(
package=obj["package"], struct=obj["struct"].upper())
if obj["act"] == "rcv" and suffix == "" and obj["struct"] == "logic":
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index f2e8442..f54cb0e 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -8,7 +8,6 @@
from collections import OrderedDict
from topgen import lib
-from .intermodule import elab_intermodule
def amend_ip(top, ip):
@@ -747,9 +746,6 @@
# Combine the wakeups
amend_wkup(gencfg)
- # Inter-module signals
- elab_intermodule(gencfg)
-
# Combine the interrupt (should be processed prior to xbar)
amend_interrupt(gencfg)