From 32e13907d21849211d094434bfde4e426f0f13e6 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 30 Oct 2024 14:32:07 +0100 Subject: [PATCH] [topgen] Improve support for multiple address spaces Individually generate C and Rust collateral for all address spaces Signed-off-by: Robert Schilling --- .../data/autogen/top_earlgrey.gen.hjson | 1 + hw/top_earlgrey/data/top_earlgrey.hjson | 1 + util/topgen.py | 205 ++++++++++-------- util/topgen/c.py | 4 +- util/topgen/c_test.py | 16 +- util/topgen/lib.py | 48 +++- util/topgen/rust.py | 4 +- util/topgen/templates/toplevel.c.tpl | 4 +- util/topgen/templates/toplevel.h.tpl | 13 +- util/topgen/templates/toplevel.rs.tpl | 2 + util/topgen/templates/toplevel_memory.h.tpl | 11 +- util/topgen/templates/toplevel_memory.ld.tpl | 2 + util/topgen/templates/toplevel_mod.rs.tpl | 14 +- util/topgen/templates/toplevel_pkg.sv.tpl | 6 +- util/topgen/validate.py | 2 +- 15 files changed, 215 insertions(+), 118 deletions(-) diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index 12eef250a34d2c..02a759826313ff 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson @@ -523,6 +523,7 @@ { name: hart desc: The main address space, shared between the CPU and DM + default: true subspaces: [ { diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson index 67b39de117ec7c..bb185077e60bd9 100644 --- a/hw/top_earlgrey/data/top_earlgrey.hjson +++ b/hw/top_earlgrey/data/top_earlgrey.hjson @@ -173,6 +173,7 @@ addr_spaces: [ { name: "hart", desc: "The main address space, shared between the CPU and DM" + default: true subspaces: [ { name: "mmio", desc: ''' diff --git a/util/topgen.py b/util/topgen.py index 91d20f4e5adcce..1d7aef656bcf9a 100755 --- a/util/topgen.py +++ b/util/topgen.py @@ -641,29 +641,34 @@ def render_template(template_path: str, rendered_path: Path, **other_info): with rendered_path.open(mode="w", encoding="UTF-8") as fout: fout.write(template_contents) - # The Rust file needs some complex information, so we initialize this - # object to store it. - rs_helper = TopGenRust(completecfg, name_to_block, version_stamp) - - rust_files = [("toplevel_mod.rs.tpl", "mod.rs"), - ("toplevel.rs.tpl", f"top_{topname}.rs")] - # Create Rust output directory rsformat_dir = out_path / "sw/autogen/chip/" rsformat_dir.mkdir(parents=True, exist_ok=True) - # Generating Rust device description for external sw usage - for (template, source) in rust_files: - render_template(topgen_template_path / template, - rsformat_dir / source, - helper=rs_helper) + for addr_space in completecfg['addr_spaces']: + default_addr_space = addr_space.get('default', False) + addr_space_suffix = lib.get_addr_space_suffix(addr_space) - # Generate Rust host-side files - rsformat_dir = src_tree_top / "sw/host/opentitanlib/src/chip/autogen" - rsformat_dir.mkdir(parents=True, exist_ok=True) - render_template(topgen_template_path / "host_toplevel.rs.tpl", - rsformat_dir / f"{topname}.rs", - helper=rs_helper) + # The Rust file needs some complex information, so we initialize this + # object to store it. + rs_helper = TopGenRust(completecfg, name_to_block, addr_space['name'], version_stamp) + + rust_files = [("toplevel_mod.rs.tpl", "mod.rs"), + ("toplevel.rs.tpl", f"top_{topname}{addr_space_suffix}.rs")] + + # Generating Rust device description for external sw usage + for (template, source) in rust_files: + render_template(topgen_template_path / template, + rsformat_dir / source, + helper=rs_helper) + + if default_addr_space: + # Generate Rust host-side files + rsformat_dir = src_tree_top / "sw/host/opentitanlib/src/chip/autogen" + rsformat_dir.mkdir(parents=True, exist_ok=True) + render_template(topgen_template_path / "host_toplevel.rs.tpl", + rsformat_dir / f"{topname}.rs", + helper=rs_helper) def _process_top( @@ -1207,7 +1212,7 @@ def render_template(template_path: str, rendered_path: Path, fout.write(template_contents) # Header for SV files - gencmd = warnhdr + """// + gencmd_sv = warnhdr + """// // util/topgen.py -t hw/{top_name}/data/{top_name}.hjson \\ // -o hw/{top_name}/ \\ // --rnd_cnst_seed \\ @@ -1221,7 +1226,7 @@ def render_template(template_path: str, rendered_path: Path, # "toplevel.sv.tpl" -> "rtl/autogen/{top_name}.sv" render_template(top_template_path / "toplevel.sv.tpl", out_path / "rtl" / "autogen" / f"{top_name}.sv", - gencmd=gencmd) + gencmd=gencmd_sv) # Multiple chip-levels (ASIC, FPGA, Verilator, etc) for target in topcfg["targets"]: @@ -1229,73 +1234,93 @@ def render_template(template_path: str, rendered_path: Path, render_template(top_template_path / "chiplevel.sv.tpl", out_path / f"rtl/autogen/chip_{topname}_{target_name}.sv", - gencmd=gencmd, + gencmd=gencmd_sv, target=target) - - # The C / SV file needs some complex information, so we initialize this - # object to store it. - c_helper = TopGenCTest(completecfg, name_to_block) - - # "toplevel_pkg.sv.tpl" -> "rtl/autogen/{top_name}_pkg.sv" - render_template(TOPGEN_TEMPLATE_PATH / "toplevel_pkg.sv.tpl", - out_path / "rtl" / "autogen" / f"{top_name}_pkg.sv", - helper=c_helper, - gencmd=gencmd) - + # compile-time random netlist constants render_template(TOPGEN_TEMPLATE_PATH / "toplevel_rnd_cnst_pkg.sv.tpl", out_path / f"rtl/autogen/{top_name}_rnd_cnst_pkg.sv", - gencmd=gencmd) - - # Since SW does not use FuseSoC and instead expects those files always - # to be in hw/top_{topname}/sw/autogen, we currently create these files - # twice: - # - Once under out_path/sw/autogen - # - Once under hw/top_{topname}/sw/autogen - root_paths = [out_path.resolve(), SRCTREE_TOP] - out_paths = [ - out_path.resolve(), (SRCTREE_TOP / "hw" / top_name).resolve() - ] - for idx, path in enumerate(out_paths): - # C Header + C File + Clang-format file - - # "clang-format" -> "sw/autogen/.clang-format" - cformat_tplpath = TOPGEN_TEMPLATE_PATH / "clang-format" - cformat_dir = path / "sw" / "autogen" - cformat_dir.mkdir(parents=True, exist_ok=True) - cformat_path = cformat_dir / ".clang-format" - cformat_path.write_text(cformat_tplpath.read_text()) - - # Save the header macro prefix into `c_helper` - rel_header_dir = cformat_dir.relative_to(root_paths[idx]) - c_helper.header_macro_prefix = ( - "OPENTITAN_" + str(rel_header_dir).replace("/", "_").upper()) - - # "{top_name}.h.tpl" -> "sw/autogen/{top_name}.h" - cheader_path = cformat_dir / f"{top_name}.h" - render_template(TOPGEN_TEMPLATE_PATH / "toplevel.h.tpl", - cheader_path, - helper=c_helper) - - # Save the relative header path into `c_helper` - rel_header_path = cheader_path.relative_to(root_paths[idx]) - c_helper.header_path = str(rel_header_path) - - # "toplevel.c.tpl" -> "sw/autogen/{top_name}.c" - render_template(TOPGEN_TEMPLATE_PATH / "toplevel.c.tpl", - cformat_dir / f"{top_name}.c", - helper=c_helper) - - # "toplevel_memory.ld.tpl" -> "sw/autogen/{top_name}_memory.ld" - render_template(TOPGEN_TEMPLATE_PATH / "toplevel_memory.ld.tpl", - cformat_dir / f"{top_name}_memory.ld", - helper=c_helper) - - # "toplevel_memory.h.tpl" -> "sw/autogen/{top_name}_memory.h" - memory_cheader_path = cformat_dir / f"{top_name}_memory.h" - render_template(TOPGEN_TEMPLATE_PATH / "toplevel_memory.h.tpl", - memory_cheader_path, - helper=c_helper) + gencmd=gencmd_sv) + + for addr_space in topcfg['addr_spaces']: + default_addr_space = addr_space.get('default', False) + addr_space_suffix = lib.get_addr_space_suffix(addr_space) + + # The C / SV file needs some complex information, so we initialize this + # object to store it. + c_helper = TopGenCTest(completecfg, name_to_block, addr_space['name']) + + # "toplevel_pkg.sv.tpl" -> "rtl/autogen/{top_name}{addr_space_suffix}_pkg.sv" + render_template(TOPGEN_TEMPLATE_PATH / "toplevel_pkg.sv.tpl", + out_path / "rtl" / "autogen" / f"{top_name}{addr_space_suffix}_pkg.sv", + helper=c_helper, + gencmd=gencmd_sv) + + + + # Since SW does not use FuseSoC and instead expects those files always + # to be in hw/top_{topname}/sw/autogen, we currently create these files + # twice: + # - Once under out_path/sw/autogen + # - Once under hw/top_{topname}/sw/autogen + root_paths = [out_path.resolve(), SRCTREE_TOP] + out_paths = [ + out_path.resolve(), (SRCTREE_TOP / "hw" / top_name).resolve() + ] + for idx, path in enumerate(out_paths): + # C Header + C File + Clang-format file + + # "clang-format" -> "sw/autogen/.clang-format" + cformat_tplpath = TOPGEN_TEMPLATE_PATH / "clang-format" + cformat_dir = path / "sw" / "autogen" + cformat_dir.mkdir(parents=True, exist_ok=True) + cformat_path = cformat_dir / ".clang-format" + cformat_path.write_text(cformat_tplpath.read_text()) + + # Save the header macro prefix into `c_helper` + rel_header_dir = cformat_dir.relative_to(root_paths[idx]) + c_helper.header_macro_prefix = ( + "OPENTITAN_" + str(rel_header_dir).replace("/", "_").upper()) + + # "{top_name}.h.tpl" -> "sw/autogen/{top_name}{addr_space_suffix}.h" + cheader_path = cformat_dir / f"{top_name}{addr_space_suffix}.h" + render_template(TOPGEN_TEMPLATE_PATH / "toplevel.h.tpl", + cheader_path, + helper=c_helper) + + # Save the relative header path into `c_helper` + rel_header_path = cheader_path.relative_to(root_paths[idx]) + c_helper.header_path = str(rel_header_path) + + # "toplevel.c.tpl" -> "sw/autogen/{top_name}{addr_space_suffix}.c" + render_template(TOPGEN_TEMPLATE_PATH / "toplevel.c.tpl", + cformat_dir / f"{top_name}{addr_space_suffix}.c", + helper=c_helper) + + # "toplevel_memory.h.tpl" -> "sw/autogen/{top_name}{addr_space_suffix}_memory.h" + memory_cheader_path = cformat_dir / f"{top_name}{addr_space_suffix}_memory.h" + render_template(TOPGEN_TEMPLATE_PATH / "toplevel_memory.h.tpl", + memory_cheader_path, + helper=c_helper) + + if default_addr_space: + # "toplevel_memory.ld.tpl" -> + # "sw/autogen/{top_name}{addr_space_suffix}_memory.ld" + render_template(TOPGEN_TEMPLATE_PATH / "toplevel_memory.ld.tpl", + cformat_dir / f"{top_name}{addr_space_suffix}_memory.ld", + helper=c_helper) + + # Auto-generate tests in "sw/device/tests/autogen" area. + gencmd = warnhdr + GENCMD.format(top_name=top_name) + for fname in ["plic_all_irqs_test.c", "alert_test.c", "BUILD"]: + outfile = SRCTREE_TOP / "sw/device/tests/autogen" / fname + render_template(TOPGEN_TEMPLATE_PATH / f"{fname}.tpl", + outfile, + helper=c_helper, + gencmd=gencmd) + + # generate documentation for toplevel + gen_top_docs(completecfg, c_helper, out_path) # generate chip level xbar and alert_handler TB tb_files = [ @@ -1309,7 +1334,7 @@ def render_template(template_path: str, rendered_path: Path, template_contents = generate_top(completecfg, name_to_block, str(xbar_chip_data_path), - gencmd=gencmd) + gencmd=gencmd_sv) rendered_dir = out_path / "dv/autogen" rendered_dir.mkdir(parents=True, exist_ok=True) @@ -1331,18 +1356,6 @@ def render_template(template_path: str, rendered_path: Path, with rendered_path.open(mode="w", encoding="UTF-8") as fout: fout.write(template_contents) - # generate documentation for toplevel - gen_top_docs(completecfg, c_helper, out_path) - - # Auto-generate tests in "sw/device/tests/autogen" area. - gencmd = warnhdr + GENCMD.format(top_name=top_name) - for fname in ["plic_all_irqs_test.c", "alert_test.c", "BUILD"]: - outfile = SRCTREE_TOP / "sw/device/tests/autogen" / fname - render_template(TOPGEN_TEMPLATE_PATH / f"{fname}.tpl", - outfile, - helper=c_helper, - gencmd=gencmd) - if __name__ == "__main__": main() diff --git a/util/topgen/c.py b/util/topgen/c.py index 9fcbcdd5027821..d97869e47c3377 100644 --- a/util/topgen/c.py +++ b/util/topgen/c.py @@ -12,7 +12,7 @@ class TopGenC(TopGen): - def __init__(self, top_info, name_to_block: Dict[str, IpBlock]): - super().__init__(top_info, name_to_block, CEnum, CArrayMapping) + def __init__(self, top_info, name_to_block: Dict[str, IpBlock], addr_space: str): + super().__init__(top_info, name_to_block, addr_space, CEnum, CArrayMapping) # The .c file needs the .h file's relative path, store it here self.header_path = None diff --git a/util/topgen/c_test.py b/util/topgen/c_test.py index ab58d51c67c3c1..2593e66a27cd78 100644 --- a/util/topgen/c_test.py +++ b/util/topgen/c_test.py @@ -63,13 +63,17 @@ def __init__(self, name: str, inst_name: str, base_addr_name: str, class TopGenCTest(TopGenC): - def __init__(self, top_info, name_to_block: Dict[str, IpBlock]): - super().__init__(top_info, name_to_block) + def __init__(self, top_info, name_to_block: Dict[str, IpBlock], addr_space: str): + super().__init__(top_info, name_to_block, addr_space) self.irq_peripherals = self._get_irq_peripherals() self.alert_peripherals = self._get_alert_peripherals() def _get_irq_peripherals(self): + # TODO: Topgen should support interrupt domains in different address spaces + if self.addr_space != self.default_addr_space: + return [] + irq_peripherals = [] self.devices() for entry in self.top['module']: @@ -139,8 +143,12 @@ def _get_irq_peripherals(self): return irq_peripherals def _get_alert_peripherals(self): + # Alert peripherals are only considered in the default address space + if self.addr_space != self.default_addr_space: + return [] + alert_peripherals = [] - self.devices() + device_regions = self.all_device_regions() for entry in self.top['module']: inst_name = entry['name'] if inst_name not in self.top["alert_module"]: @@ -153,7 +161,7 @@ def _get_alert_peripherals(self): if item['name'] == inst_name: name = item['type'] - regions = self.device_regions[inst_name] + regions = device_regions[inst_name] if "core" in regions: if_name = "core" elif "regs" in regions: diff --git a/util/topgen/lib.py b/util/topgen/lib.py index 8235ee5024f67a..4c68b77db1f440 100644 --- a/util/topgen/lib.py +++ b/util/topgen/lib.py @@ -844,8 +844,18 @@ def get_device_ranges(devices, device_name): return ranges +def get_addr_space_suffix(addr_space): + default_addr_space = addr_space.get('default', False) + + addr_space_suffix = "" + if not default_addr_space: + addr_space_suffix = "_" + addr_space['name'] + return addr_space_suffix + + class TopGen: - def __init__(self, top_info, name_to_block: Dict[str, IpBlock], enum_type, array_mapping_type): + def __init__(self, top_info, name_to_block: Dict[str, IpBlock], addr_space: str, + enum_type, array_mapping_type): self.top = top_info self._top_name = Name(["top"]) + Name.from_snake_case(top_info["name"]) self._name_to_block = name_to_block @@ -857,8 +867,15 @@ def __init__(self, top_info, name_to_block: Dict[str, IpBlock], enum_type, array self._enum_type = enum_type self._array_mapping_type = array_mapping_type - # TODO: Don't hardcode the address space used for software. - self.addr_space = "hart" + self.addr_space = addr_space + + # Determine default address space from the top config + self.default_addr_space = None + for addr_space in top_info['addr_spaces']: + default = addr_space.get('default', False) + if default: + self.default_addr_space = addr_space['name'] + assert self.default_addr_space is not None, "Missing default addr_space" self._init_plic_targets() self._init_plic_mapping() @@ -871,8 +888,30 @@ def __init__(self, top_info, name_to_block: Dict[str, IpBlock], enum_type, array self._init_clkmgr_clocks() self._init_subranges() + def all_device_regions(self) -> Dict[str, MemoryRegion]: + '''Return a list of MemoryRegion objects for all devices on the bus. + ''' + device_regions = defaultdict(dict) + for inst in self.top['module']: + block = self._name_to_block[inst['type']] + for if_name, rb in block.reg_blocks.items(): + full_if = (inst['name'], if_name) + full_if_name = Name.from_snake_case(full_if[0]) + if if_name is not None: + full_if_name += Name.from_snake_case(if_name) + + name = full_if_name + base, size = get_base_and_size(self._name_to_block, + inst, if_name) + + base_address_int = list(base.values())[0] + region = MemoryRegion(self._top_name, name, base_address_int, size) + device_regions[inst['name']].update({if_name: region}) + + return device_regions + def devices(self) -> List[Tuple[Tuple[str, Optional[str]], MemoryRegion]]: - '''Return a list of MemoryRegion objects for devices on the bus + '''Return a list of MemoryRegion objects for devices on the bus. The list returned is pairs (full_if, region) where full_if is itself a pair (inst_name, if_name). inst_name is the name of some IP block @@ -896,6 +935,7 @@ def devices(self) -> List[Tuple[Tuple[str, Optional[str]], MemoryRegion]]: name = full_if_name base, size = get_base_and_size(self._name_to_block, inst, if_name) + if self.addr_space not in base: continue diff --git a/util/topgen/rust.py b/util/topgen/rust.py index a547b6f0234b12..4b3d7ad826f5c1 100644 --- a/util/topgen/rust.py +++ b/util/topgen/rust.py @@ -11,7 +11,7 @@ class TopGenRust(TopGen): - def __init__(self, top_info, name_to_block: Dict[str, IpBlock], + def __init__(self, top_info, name_to_block: Dict[str, IpBlock], addr_space: str, version_stamp: VersionInformation): - super().__init__(top_info, name_to_block, RustEnum, RustArrayMapping) + super().__init__(top_info, name_to_block, addr_space, RustEnum, RustArrayMapping) self.file_header = RustFileHeader(version_stamp) diff --git a/util/topgen/templates/toplevel.c.tpl b/util/topgen/templates/toplevel.c.tpl index 49a6af47a2b21e..bba96278b69949 100644 --- a/util/topgen/templates/toplevel.c.tpl +++ b/util/topgen/templates/toplevel.c.tpl @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "${helper.header_path}" +% if helper.addr_space == helper.default_addr_space: /** * PLIC Interrupt Source to Peripheral Map @@ -18,4 +19,5 @@ ${helper.plic_mapping.render_definition()} * This array is a mapping from `${helper.alert_alerts.name.as_c_type()}` to * `${helper.alert_sources.name.as_c_type()}`. */ -${helper.alert_mapping.render_definition()} \ No newline at end of file +${helper.alert_mapping.render_definition()}\ +% endif \ No newline at end of file diff --git a/util/topgen/templates/toplevel.h.tpl b/util/topgen/templates/toplevel.h.tpl index 703045da069ef2..0e9c57679b5760 100644 --- a/util/topgen/templates/toplevel.h.tpl +++ b/util/topgen/templates/toplevel.h.tpl @@ -3,10 +3,15 @@ // SPDX-License-Identifier: Apache-2.0 <% import textwrap + +if helper.addr_space == helper.default_addr_space: + header_suffix = top["name"].upper() +else: + header_suffix = "_".join([top["name"], helper.addr_space]).upper() %>\ -#ifndef ${helper.header_macro_prefix}_TOP_${top["name"].upper()}_H_ -#define ${helper.header_macro_prefix}_TOP_${top["name"].upper()}_H_ +#ifndef ${helper.header_macro_prefix}_TOP_${header_suffix}_H_ +#define ${helper.header_macro_prefix}_TOP_${header_suffix}_H_ /** * @file @@ -78,6 +83,7 @@ extern "C" { #define ${size_bytes_name} ${hex_size_bytes} % endfor +% if helper.addr_space == helper.default_addr_space: /** * PLIC Interrupt Source Peripheral. @@ -205,6 +211,7 @@ ${helper.clkmgr_gateable_clocks.render()} * but the clock manager is in control of whether the clock actually is stopped. */ ${helper.clkmgr_hintable_clocks.render()} +% endif % for (subspace_name, description, subspace_range) in helper.subranges: /** @@ -223,4 +230,4 @@ ${helper.clkmgr_hintable_clocks.render()} } // extern "C" #endif -#endif // ${helper.header_macro_prefix}_TOP_${top["name"].upper()}_H_ +#endif // ${helper.header_macro_prefix}_TOP_${header_suffix}_H_ diff --git a/util/topgen/templates/toplevel.rs.tpl b/util/topgen/templates/toplevel.rs.tpl index 0d7fc3d332438e..6dfe9acf587c10 100644 --- a/util/topgen/templates/toplevel.rs.tpl +++ b/util/topgen/templates/toplevel.rs.tpl @@ -65,6 +65,7 @@ pub const ${base_addr_name}: usize = ${hex_base_addr}; pub const ${size_bytes_name}: usize = ${hex_size_bytes}; % endfor +% if helper.addr_space == helper.default_addr_space: /// PLIC Interrupt Source Peripheral. /// /// Enumeration used to determine which peripheral asserted the corresponding @@ -155,6 +156,7 @@ ${helper.clkmgr_gateable_clocks.render()} /// but the clock manager is in control of whether the clock actually is stopped. ${helper.clkmgr_hintable_clocks.render()} +% endif % for (subspace_name, description, subspace_range) in helper.subranges: /// ${subspace_name.upper()} Region /// diff --git a/util/topgen/templates/toplevel_memory.h.tpl b/util/topgen/templates/toplevel_memory.h.tpl index f0ab7e2b1547ac..47238291a537ca 100644 --- a/util/topgen/templates/toplevel_memory.h.tpl +++ b/util/topgen/templates/toplevel_memory.h.tpl @@ -3,10 +3,15 @@ // SPDX-License-Identifier: Apache-2.0 <% import textwrap + +if helper.addr_space == helper.default_addr_space: + header_suffix = top["name"].upper() +else: + header_suffix = "_".join([top["name"], helper.addr_space]).upper() %>\ -#ifndef ${helper.header_macro_prefix}_TOP_${top["name"].upper()}_MEMORY_H_ -#define ${helper.header_macro_prefix}_TOP_${top["name"].upper()}_MEMORY_H_ +#ifndef ${helper.header_macro_prefix}_TOP_${header_suffix}_MEMORY_H_ +#define ${helper.header_macro_prefix}_TOP_${header_suffix}_MEMORY_H_ /** * @file @@ -96,4 +101,4 @@ import textwrap #endif // __ASSEMBLER__ -#endif // ${helper.header_macro_prefix}_TOP_${top["name"].upper()}_MEMORY_H_ +#endif // ${helper.header_macro_prefix}_TOP_${header_suffix}_MEMORY_H_ diff --git a/util/topgen/templates/toplevel_memory.ld.tpl b/util/topgen/templates/toplevel_memory.ld.tpl index b5e5d6d8adff50..0810a5ba35c9e8 100644 --- a/util/topgen/templates/toplevel_memory.ld.tpl +++ b/util/topgen/templates/toplevel_memory.ld.tpl @@ -52,7 +52,9 @@ MEMORY { % for m in top["module"]: % if "memory" in m: % for key, mem in m["memory"].items(): + % if helper.addr_space in m["base_addrs"][key]: ${mem["label"]}(${flags(mem)}) : ORIGIN = ${m["base_addrs"][key][helper.addr_space]}, LENGTH = ${mem["size"]} + % endif % endfor % endif % endfor diff --git a/util/topgen/templates/toplevel_mod.rs.tpl b/util/topgen/templates/toplevel_mod.rs.tpl index 6e21a65734e24a..115035150427f3 100644 --- a/util/topgen/templates/toplevel_mod.rs.tpl +++ b/util/topgen/templates/toplevel_mod.rs.tpl @@ -2,4 +2,16 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 -pub mod top_${top["name"]}; +<% +top_name = top["name"] +modules = [] +for addr_space in top["addr_spaces"]: + if addr_space.get('default', False): + modules.append(f"top_{top_name}") + else: + modules.append(f"top_{top_name}_{addr_space['name']}") +modules.sort() +%>\ +% for module in modules: +pub mod ${module}; +% endfor diff --git a/util/topgen/templates/toplevel_pkg.sv.tpl b/util/topgen/templates/toplevel_pkg.sv.tpl index b737ba9fdd8046..458461196ff62c 100644 --- a/util/topgen/templates/toplevel_pkg.sv.tpl +++ b/util/topgen/templates/toplevel_pkg.sv.tpl @@ -4,8 +4,10 @@ ${gencmd} <% import topgen.lib as lib + +addr_space_suffix = "" if helper.addr_space == helper.default_addr_space else "_" + helper.addr_space %>\ -package top_${top["name"]}_pkg; +package top_${top["name"]}${addr_space_suffix}_pkg; % for (inst_name, if_name), region in helper.devices(): <% if_desc = inst_name if if_name is None else '{} device on {}'.format(if_name, inst_name) @@ -39,6 +41,7 @@ package top_${top["name"]}_pkg; parameter int unsigned ${region.size_bytes_name().as_c_define()} = ${hex_size_bytes}; % endfor +% if helper.addr_space == helper.default_addr_space: // Enumeration of alert modules typedef enum int unsigned { @@ -150,5 +153,6 @@ package top_${top["name"]}_pkg; `define INOUT_AI inout `define INOUT_AO inout `endif +% endif endpackage diff --git a/util/topgen/validate.py b/util/topgen/validate.py index 31653adb1ef695..e33d2276a6c50b 100644 --- a/util/topgen/validate.py +++ b/util/topgen/validate.py @@ -41,7 +41,7 @@ 'type': ['s', 'type of hjson. Shall be "top" always'], 'clocks': ['g', 'group of clock properties'], 'resets': ['l', 'list of resets'], - 'addr_spaces': ['g', 'list of address spaces'], + 'addr_spaces': ['l', 'list of address spaces'], 'module': ['l', 'list of modules to instantiate'], 'memory': ['l', 'list of memories. At least one memory ' 'is needed to run the software'],