Skip to content

Commit

Permalink
[topgen] Improve support for multiple address spaces
Browse files Browse the repository at this point in the history
Individually generate C and Rust collateral for all address spaces

Signed-off-by: Robert Schilling <[email protected]>
  • Loading branch information
Razer6 committed Oct 30, 2024
1 parent 6581ef8 commit 32e1390
Show file tree
Hide file tree
Showing 15 changed files with 215 additions and 118 deletions.
1 change: 1 addition & 0 deletions hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@
{
name: hart
desc: The main address space, shared between the CPU and DM
default: true
subspaces:
[
{
Expand Down
1 change: 1 addition & 0 deletions hw/top_earlgrey/data/top_earlgrey.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -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: '''
Expand Down
205 changes: 109 additions & 96 deletions util/topgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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 \\
Expand All @@ -1221,81 +1226,101 @@ 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"]:
target_name = target["name"]
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 = [
Expand All @@ -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)
Expand All @@ -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()
4 changes: 2 additions & 2 deletions util/topgen/c.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 12 additions & 4 deletions util/topgen/c_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']:
Expand Down Expand Up @@ -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"]:
Expand All @@ -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:
Expand Down
Loading

0 comments on commit 32e1390

Please sign in to comment.