From 59724105c53027c0938900ca02f56b9a36a61aec Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sat, 22 Nov 2025 15:55:04 -0800 Subject: [PATCH] Add taxi apb interface --- src/peakrdl_busdecoder/__peakrdl__.py | 3 +- src/peakrdl_busdecoder/cpuif/interface.py | 7 +- .../cpuif/taxi_apb/__init__.py | 3 + .../cpuif/taxi_apb/taxi_apb_cpuif.py | 99 +++++++++++++++++++ .../cpuif/taxi_apb/taxi_apb_interface.py | 16 +++ .../cpuif/taxi_apb/taxi_apb_tmpl.sv | 43 ++++++++ 6 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 src/peakrdl_busdecoder/cpuif/taxi_apb/__init__.py create mode 100644 src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_cpuif.py create mode 100644 src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_interface.py create mode 100644 src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_tmpl.sv diff --git a/src/peakrdl_busdecoder/__peakrdl__.py b/src/peakrdl_busdecoder/__peakrdl__.py index 2adeb85..0a80251 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, axi4lite +from .cpuif import BaseCpuif, apb3, apb4, axi4lite, taxi_apb from .exporter import BusDecoderExporter from .udps import ALL_UDPS @@ -24,6 +24,7 @@ def get_cpuifs(config: list[tuple[str, Any]]) -> dict[str, type[BaseCpuif]]: "apb3-flat": apb3.APB3CpuifFlat, "apb4": apb4.APB4Cpuif, "apb4-flat": apb4.APB4CpuifFlat, + "taxi-apb": taxi_apb.TaxiAPBCpuif, "axi4-lite": axi4lite.AXI4LiteCpuif, "axi4-lite-flat": axi4lite.AXI4LiteCpuifFlat, } diff --git a/src/peakrdl_busdecoder/cpuif/interface.py b/src/peakrdl_busdecoder/cpuif/interface.py index 6c27451..c331489 100644 --- a/src/peakrdl_busdecoder/cpuif/interface.py +++ b/src/peakrdl_busdecoder/cpuif/interface.py @@ -64,17 +64,20 @@ class Interface(ABC): class SVInterface(Interface): """SystemVerilog interface-based signal handling.""" + slave_modport_name = "slave" + master_modport_name = "master" + @property def is_interface(self) -> bool: return True def get_port_declaration(self, slave_name: str, master_prefix: str) -> str: """Generate SystemVerilog interface port declarations.""" - slave_ports: list[str] = [f"{self.get_interface_type()}.slave {slave_name}"] + slave_ports: list[str] = [f"{self.get_interface_type()}.{self.slave_modport_name} {slave_name}"] master_ports: list[str] = [] for child in self.cpuif.addressable_children: - base = f"{self.get_interface_type()}.master {master_prefix}{child.inst_name}" + base = f"{self.get_interface_type()}.{self.master_modport_name} {master_prefix}{child.inst_name}" # When unrolled, current_idx is set - append it to the name if child.current_idx is not None: diff --git a/src/peakrdl_busdecoder/cpuif/taxi_apb/__init__.py b/src/peakrdl_busdecoder/cpuif/taxi_apb/__init__.py new file mode 100644 index 0000000..665a4e6 --- /dev/null +++ b/src/peakrdl_busdecoder/cpuif/taxi_apb/__init__.py @@ -0,0 +1,3 @@ +from .taxi_apb_cpuif import TaxiAPBCpuif + +__all__ = ["TaxiAPBCpuif"] diff --git a/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_cpuif.py b/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_cpuif.py new file mode 100644 index 0000000..4d74bb9 --- /dev/null +++ b/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_cpuif.py @@ -0,0 +1,99 @@ +from typing import TYPE_CHECKING, overload + +from systemrdl.node import AddressableNode + +from ...utils import get_indexed_path +from ..base_cpuif import BaseCpuif +from .taxi_apb_interface import TaxiAPBSVInterface + +if TYPE_CHECKING: + from ...exporter import BusDecoderExporter + + +class TaxiAPBCpuif(BaseCpuif): + template_path = "taxi_apb_tmpl.sv" + + def __init__(self, exp: "BusDecoderExporter") -> None: + super().__init__(exp) + self._interface = TaxiAPBSVInterface(self) + self._interface.master_modport_name = "mst" + self._interface.slave_modport_name = "slv" + + @property + def is_interface(self) -> bool: + return self._interface.is_interface + + @property + def port_declaration(self) -> str: + """Returns the port declaration for the APB4 interface.""" + return self._interface.get_port_declaration("s_apb", "m_apb_") + + @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: + return self._interface.signal(signal, node, indexer) + + def fanout(self, node: AddressableNode) -> str: + fanout: dict[str, str] = {} + fanout[self.signal("psel", node, "gi")] = ( + f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}" + ) + fanout[self.signal("penable", node, "gi")] = self.signal("penable") + fanout[self.signal("pwrite", node, "gi")] = ( + f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}" + ) + fanout[self.signal("paddr", node, "gi")] = self.signal("paddr") + fanout[self.signal("pprot", node, "gi")] = self.signal("pprot") + fanout[self.signal("pwdata", node, "gi")] = "cpuif_wr_data" + fanout[self.signal("pstrb", node, "gi")] = "cpuif_wr_byte_en" + # no user? + + return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", 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: + # Use intermediate signals for interface arrays to avoid + # non-constant indexing of interface arrays in procedural blocks + if self.is_interface and node.is_array and node.array_dimensions: + # Generate array index string [i0][i1]... for the intermediate signal + array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions))) + fanin["cpuif_rd_ack"] = f"{node.inst_name}_fanin_ready{array_idx}" + fanin["cpuif_rd_err"] = f"{node.inst_name}_fanin_err{array_idx}" + else: + fanin["cpuif_rd_ack"] = self.signal("pready", node, "i") + fanin["cpuif_rd_err"] = self.signal("pslverr", node, "i") + + # no user? + return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items())) + + def readback(self, node: AddressableNode | None = None) -> str: + fanin: dict[str, str] = {} + if node is None: + fanin["cpuif_rd_data"] = "'0" + else: + # Use intermediate signals for interface arrays to avoid + # non-constant indexing of interface arrays in procedural blocks + if self.is_interface and node.is_array and node.array_dimensions: + # Generate array index string [i0][i1]... for the intermediate signal + array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions))) + fanin["cpuif_rd_data"] = f"{node.inst_name}_fanin_data{array_idx}" + else: + fanin["cpuif_rd_data"] = self.signal("prdata", node, "i") + + return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items())) + + def fanin_intermediate_assignments( + self, node: AddressableNode, inst_name: str, array_idx: str, master_prefix: str, indexed_path: str + ) -> list[str]: + """Generate intermediate signal assignments for APB4 interface arrays.""" + return [ + f"assign {inst_name}_fanin_ready{array_idx} = {master_prefix}{indexed_path}.pready;", + f"assign {inst_name}_fanin_err{array_idx} = {master_prefix}{indexed_path}.pslverr;", + f"assign {inst_name}_fanin_data{array_idx} = {master_prefix}{indexed_path}.prdata;", + ] diff --git a/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_interface.py b/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_interface.py new file mode 100644 index 0000000..84388fc --- /dev/null +++ b/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_interface.py @@ -0,0 +1,16 @@ +"""Taxi APB-specific interface implementations.""" + +from ..interface import SVInterface + + +class TaxiAPBSVInterface(SVInterface): + """Taxi APB SystemVerilog interface.""" + + def get_interface_type(self) -> str: + return "taxi_apb_if" + + def get_slave_name(self) -> str: + return "s_apb" + + def get_master_prefix(self) -> str: + return "m_apb_" diff --git a/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_tmpl.sv b/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_tmpl.sv new file mode 100644 index 0000000..c1b1d9f --- /dev/null +++ b/src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_tmpl.sv @@ -0,0 +1,43 @@ +{%- if cpuif.is_interface %} +`ifndef SYNTHESIS + initial begin + assert_bad_addr_width: assert($bits({{cpuif.signal("paddr")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH) + else $error("Interface address width of %0d is too small. Shall be at least %0d bits", $bits({{cpuif.signal("paddr")}}), {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH); + assert_bad_data_width: assert($bits({{cpuif.signal("pwdata")}}) == {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH) + else $error("Interface data width of %0d is incorrect. Shall be %0d bits", $bits({{cpuif.signal("pwdata")}}), {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH); + end + assert_wr_sel: assert property (@(posedge {{cpuif.signal("PCLK")}}) {{cpuif.signal("psel")}} && {{cpuif.signal("pwrite")}} |-> ##1 ({{cpuif.signal("pready")}} || {{cpuif.signal("pslverr")}})) + else $error("APB4 Slave port SEL implies that cpuif_wr_sel must be one-hot encoded"); +`endif +{%- endif %} + +assign cpuif_req = {{cpuif.signal("psel")}}; +assign cpuif_wr_en = {{cpuif.signal("pwrite")}}; +assign cpuif_rd_en = !{{cpuif.signal("pwrite")}}; + +assign cpuif_wr_addr = {{cpuif.signal("paddr")}}; +assign cpuif_rd_addr = {{cpuif.signal("paddr")}}; + +assign cpuif_wr_data = {{cpuif.signal("pwdata")}}; +assign cpuif_wr_byte_en = {{cpuif.signal("pstrb")}}; + +assign {{cpuif.signal("prdata")}} = cpuif_rd_data; +assign {{cpuif.signal("pready")}} = cpuif_rd_ack; +assign {{cpuif.signal("pslverr")}} = cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpuif_wr_sel.cpuif_err; + +//-------------------------------------------------------------------------- +// Fanout CPU Bus interface signals +//-------------------------------------------------------------------------- +{{fanout|walk(cpuif=cpuif)}} +{%- if cpuif.is_interface %} + +//-------------------------------------------------------------------------- +// Intermediate signals for interface array fanin +//-------------------------------------------------------------------------- +{{fanin_intermediate|walk(cpuif=cpuif)}} +{%- endif %} + +//-------------------------------------------------------------------------- +// Fanin CPU Bus interface signals +//-------------------------------------------------------------------------- +{{fanin|walk(cpuif=cpuif)}} \ No newline at end of file