From 11b89432df0ea67cba119a6395fe4e47dc88e487 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Thu, 5 Feb 2026 08:08:28 -0800 Subject: [PATCH] Initial Commit --- .gitignore | 20 +++ MANIFEST.in | 1 + pyproject.toml | 51 ++++++++ .../cpuif/busdecoder_taxi_apb.py | 119 ++++++++++++++++++ .../cpuif/busdecoder_taxi_apb_tmpl.sv | 44 +++++++ .../cpuif/regblock_taxi_apb.py | 12 ++ .../cpuif/regblock_taxi_apb_tmpl.sv | 51 ++++++++ 7 files changed, 298 insertions(+) create mode 100644 .gitignore create mode 100644 MANIFEST.in create mode 100644 pyproject.toml create mode 100644 src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb.py create mode 100644 src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb_tmpl.sv create mode 100644 src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb.py create mode 100644 src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb_tmpl.sv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2bfac04 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +**/__pycache__ +**/.vscode +**/.venv +**/.coverage +**/*.rpt +**/.pytest_cache +**/_build +**/*.out +**/transcript +**/htmlcov +**/*.log +**/*.pb +**/.Xil +**/.coverage.* +coverage.xml + +build/ +dist/ +*.egg-info/ +.eggs/ diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..c2de9c9 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +recursive-include src/taxi_peakrdl_extensions *.sv diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0e67845 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,51 @@ +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "taxi-peakrdl-extensions" +version = "0.0.1" +requires-python = ">=3.10" +dependencies = [ + "jinja2~=3.1", + "systemrdl-compiler~=1.30", +] + +authors = [{ name = "Arnav Sacheti" }] +description = "Add Taxi interfaces to peakrdl tools" +readme = "README.md" +license = { text = "LGPLv3" } +keywords = [ + "SystemRDL", + "PeakRDL", + "hierarchical addressing", + "compiler", + "tool", + "registers", + "generator", + "Verilog", + "SystemVerilog", + "register abstraction layer", + "FPGA", + "ASIC", +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", + "Operating System :: OS Independent", + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", +] + +[project.optional-dependencies] +cli = ["peakrdl-cli >= 1.2.3"] + +[project.entry-points."peakrdl_regblock.cpuif"] +taxi-apb = "taxi_peakrdl_extensions.cpuif.regblock_taxi_apb:TaxiAPBCpuif" + +[project.entry-points."peakrdl_busdecoder.cpuif"] +taxi-apb = "taxi_peakrdl_extensions.cpuif.busdecoder_taxi_apb:TaxiAPBCpuif" + diff --git a/src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb.py b/src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb.py new file mode 100644 index 0000000..e58bd7e --- /dev/null +++ b/src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb.py @@ -0,0 +1,119 @@ +from collections import deque +from typing import TYPE_CHECKING, overload + +from systemrdl.node import AddressableNode + +from peakrdl_busdecoder.utils import get_indexed_path +from peakrdl_busdecoder.cpuif.base_cpuif import BaseCpuif +from peakrdl_busdecoder.cpuif.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_" + + + +class TaxiAPBCpuif(BaseCpuif): + template_path = "busdecoder_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, array_stack: deque[int]) -> 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_wr(self, node: AddressableNode | None = None, *, error: bool = False) -> 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 fanin_rd(self, node: AddressableNode | None = None, *, error: bool = False) -> str: + fanin: dict[str, str] = {} + if node is None: + fanin["cpuif_rd_ack"] = "'0" + fanin["cpuif_rd_err"] = "'0" + fanin["cpuif_rd_data"] = "'0" + if error: + fanin["cpuif_rd_ack"] = "'1" + fanin["cpuif_rd_err"] = "cpuif_rd_sel.cpuif_err" + 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}" + fanin["cpuif_rd_data"] = f"{node.inst_name}_fanin_data{array_idx}" + else: + fanin["cpuif_rd_ack"] = self.signal("prdata", node, "i") + fanin["cpuif_rd_err"] = self.signal("pslverr", node, "i") + 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/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb_tmpl.sv b/src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb_tmpl.sv new file mode 100644 index 0000000..4fe842c --- /dev/null +++ b/src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb_tmpl.sv @@ -0,0 +1,44 @@ +{%- 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 + `ifdef PEAKRDL_ASSERTIONS + 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 +{%- 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 | cpuif_wr_ack; +assign {{cpuif.signal("pslverr")}} = cpuif_rd_err | cpuif_wr_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 diff --git a/src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb.py b/src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb.py new file mode 100644 index 0000000..be470a3 --- /dev/null +++ b/src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb.py @@ -0,0 +1,12 @@ +from peakrdl_regblock.cpuif.base import CpuifBase + +class TaxiAPBCpuif(CpuifBase): + template_path = "regblock_taxi_apb_tmpl.sv" + is_interface = True + + @property + def port_declaration(self) -> str: + return "taxi_apb_if.slv s_apb" + + def signal(self, name:str) -> str: + return "s_apb." + name diff --git a/src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb_tmpl.sv b/src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb_tmpl.sv new file mode 100644 index 0000000..4293bec --- /dev/null +++ b/src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb_tmpl.sv @@ -0,0 +1,51 @@ +{%- 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 +`endif + +{% endif -%} + +// Request +logic is_active; +always_ff {{get_always_ff_event(cpuif.reset)}} begin + if({{get_resetsignal(cpuif.reset)}}) begin + is_active <= '0; + cpuif_req <= '0; + cpuif_req_is_wr <= '0; + cpuif_addr <= '0; + cpuif_wr_data <= '0; + cpuif_wr_biten <= '0; + end else begin + if(~is_active) begin + if({{cpuif.signal("psel")}}) begin + is_active <= '1; + cpuif_req <= '1; + cpuif_req_is_wr <= {{cpuif.signal("pwrite")}}; + {%- if cpuif.data_width_bytes == 1 %} + cpuif_addr <= {{cpuif.signal("paddr")}}[{{cpuif.addr_width-1}}:0]; + {%- else %} + cpuif_addr <= { {{-cpuif.signal("paddr")}}[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width_bytes)}}], {{clog2(cpuif.data_width_bytes)}}'b0}; + {%- endif %} + cpuif_wr_data <= {{cpuif.signal("pwdata")}}; + for(int i=0; i<{{cpuif.data_width_bytes}}; i++) begin + cpuif_wr_biten[i*8 +: 8] <= {8{ {{-cpuif.signal("pstrb")}}[i]}}; + end + end + end else begin + cpuif_req <= '0; + if(cpuif_rd_ack || cpuif_wr_ack) begin + is_active <= '0; + end + end + end +end + +// Response +assign {{cpuif.signal("pready")}} = cpuif_rd_ack | cpuif_wr_ack; +assign {{cpuif.signal("prdata")}} = cpuif_rd_data; +assign {{cpuif.signal("pslverr")}} = cpuif_rd_err | cpuif_wr_err;