Initial Commit

This commit is contained in:
2026-02-05 08:08:28 -08:00
commit 11b89432df
7 changed files with 298 additions and 0 deletions

20
.gitignore vendored Normal file
View File

@@ -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/

1
MANIFEST.in Normal file
View File

@@ -0,0 +1 @@
recursive-include src/taxi_peakrdl_extensions *.sv

51
pyproject.toml Normal file
View File

@@ -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"

View File

@@ -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;",
]

View File

@@ -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)}}

View File

@@ -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

View File

@@ -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;