From 3380e68b9bc5309ac1bccba5c181e9dd236f55d5 Mon Sep 17 00:00:00 2001 From: Arnav Sacheti <36746504+arnavsacheti@users.noreply.github.com> Date: Thu, 23 Oct 2025 22:38:53 -0700 Subject: [PATCH] export axi4 lite cpuif --- src/peakrdl_busdecoder/__peakrdl__.py | 6 +- .../cpuif/axi4lite/__init__.py | 4 + .../cpuif/axi4lite/axi4_lite_cpuif_flat.py | 94 +++++++++++++++++++ 3 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 src/peakrdl_busdecoder/cpuif/axi4lite/__init__.py create mode 100644 src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif_flat.py diff --git a/src/peakrdl_busdecoder/__peakrdl__.py b/src/peakrdl_busdecoder/__peakrdl__.py index f78efdd..8cf700d 100644 --- a/src/peakrdl_busdecoder/__peakrdl__.py +++ b/src/peakrdl_busdecoder/__peakrdl__.py @@ -5,7 +5,7 @@ from peakrdl.config import schema from peakrdl.plugins.entry_points import get_entry_points from peakrdl.plugins.exporter import ExporterSubcommandPlugin -from .cpuif import BaseCpuif, apb3, apb4 +from .cpuif import BaseCpuif, apb3, apb4, axi4lite from .exporter import BusDecoderExporter from .udps import ALL_UDPS @@ -24,8 +24,8 @@ def get_cpuifs(config: list[tuple[str, Any]]) -> dict[str, type[BaseCpuif]]: "apb3-flat": apb3.APB3CpuifFlat, "apb4": apb4.APB4Cpuif, "apb4-flat": apb4.APB4CpuifFlat, - # "axi4-lite": axi4lite.AXI4Lite_Cpuif, - # "axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened, + "axi4-lite": axi4lite.AXI4LiteCpuif, + "axi4-lite-flat": axi4lite.AXI4LiteCpuifFlat, } # Load any cpuifs specified via entry points diff --git a/src/peakrdl_busdecoder/cpuif/axi4lite/__init__.py b/src/peakrdl_busdecoder/cpuif/axi4lite/__init__.py new file mode 100644 index 0000000..2420e83 --- /dev/null +++ b/src/peakrdl_busdecoder/cpuif/axi4lite/__init__.py @@ -0,0 +1,4 @@ +from .axi4_lite_cpuif import AXI4LiteCpuif +from .axi4_lite_cpuif_flat import AXI4LiteCpuifFlat + +__all__ = ["AXI4LiteCpuif", "AXI4LiteCpuifFlat"] diff --git a/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif_flat.py b/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif_flat.py new file mode 100644 index 0000000..85e69f5 --- /dev/null +++ b/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif_flat.py @@ -0,0 +1,94 @@ +from typing import overload + +from systemrdl.node import AddressableNode + +from ...utils import get_indexed_path +from ..base_cpuif import BaseCpuif + + +class AXI4LiteCpuifFlat(BaseCpuif): + template_path = "axi4lite_tmpl.sv" + is_interface = True + + def _port_declaration(self, child: AddressableNode) -> str: + base = f"axi4lite_intf.master m_axil_{child.inst_name}" + + # When unrolled, current_idx is set - append it to the name + if child.current_idx is not None: + base = f"{base}_{'_'.join(map(str, child.current_idx))}" + + # Only add array dimensions if this should be treated as an array + if self.check_is_array(child): + return f"{base} {''.join(f'[{dim}]' for dim in child.array_dimensions)}" + + return base + + @property + def port_declaration(self) -> str: + """Returns the port declaration for the AXI4-Lite interface.""" + slave_ports: list[str] = ["axi4lite_intf.slave s_axil"] + master_ports: list[str] = list(map(self._port_declaration, self.addressable_children)) + + return ",\n".join(slave_ports + master_ports) + + @overload + def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ... + @overload + def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ... + def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str: + if node is None or indexer is None: + # Node is none, so this is a slave signal + return f"s_axil.{signal}" + + # Master signal + return f"m_axil_{get_indexed_path(node.parent, node, indexer, skip_kw_filter=True)}.{signal}" + + def fanout(self, node: AddressableNode) -> str: + fanout: dict[str, str] = {} + + wr_sel = f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}" + rd_sel = f"cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}" + + # Write address channel + fanout[self.signal("AWVALID", node, "gi")] = wr_sel + fanout[self.signal("AWADDR", node, "gi")] = self.signal("AWADDR") + fanout[self.signal("AWPROT", node, "gi")] = self.signal("AWPROT") + + # Write data channel + fanout[self.signal("WVALID", node, "gi")] = wr_sel + fanout[self.signal("WDATA", node, "gi")] = "cpuif_wr_data" + fanout[self.signal("WSTRB", node, "gi")] = "cpuif_wr_byte_en" + + # Write response channel (master -> slave) + fanout[self.signal("BREADY", node, "gi")] = self.signal("BREADY") + + # Read address channel + fanout[self.signal("ARVALID", node, "gi")] = rd_sel + fanout[self.signal("ARADDR", node, "gi")] = self.signal("ARADDR") + fanout[self.signal("ARPROT", node, "gi")] = self.signal("ARPROT") + + # Read data channel (master -> slave) + fanout[self.signal("RREADY", node, "gi")] = self.signal("RREADY") + + return "\n".join(f"assign {lhs} = {rhs};" for lhs, rhs in fanout.items()) + + def fanin(self, node: AddressableNode | None = None) -> str: + fanin: dict[str, str] = {} + if node is None: + fanin["cpuif_rd_ack"] = "'0" + fanin["cpuif_rd_err"] = "'0" + else: + # Read side: ack comes from RVALID; err if RRESP[1] is set (SLVERR/DECERR) + fanin["cpuif_rd_ack"] = self.signal("RVALID", node, "i") + fanin["cpuif_rd_err"] = f"{self.signal('RRESP', node, 'i')}[1]" + + return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items()) + + def readback(self, node: AddressableNode | None = None) -> str: + fanin: dict[str, str] = {} + if node is None: + fanin["cpuif_rd_data"] = "'0" + else: + fanin["cpuif_rd_data"] = self.signal("RDATA", node, "i") + + return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())