Compare commits
4 Commits
b0db1f60e4
...
b2fd006f7f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2fd006f7f
|
||
|
|
59724105c5
|
||
|
|
217ff15431
|
||
|
|
35d66db1b8
|
@@ -4,9 +4,9 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "peakrdl-busdecoder"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = ["jinja2>=3.1.6", "systemrdl-compiler~=1.30.1"]
|
||||
dependencies = ["jinja2>=3.1.6", "systemrdl-compiler~=1.31"]
|
||||
|
||||
authors = [{ name = "Arnav Sacheti" }]
|
||||
description = "Generate a SystemVerilog bus decoder from SystemRDL for splitting CPU interfaces to multiple sub-address spaces"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
from ...utils import get_indexed_path
|
||||
from ...utils import clog2, get_indexed_path
|
||||
from ..base_cpuif import BaseCpuif
|
||||
from .apb4_interface import APB4FlatInterface
|
||||
|
||||
@@ -42,7 +42,7 @@ class APB4CpuifFlat(BaseCpuif):
|
||||
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("PADDR", node, "gi")] = f"{self.signal('PADDR')}[{clog2(node.size) - 1}:0]"
|
||||
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"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
from ...utils import clog2
|
||||
from ..interface import FlatInterface, SVInterface
|
||||
|
||||
|
||||
@@ -50,7 +51,7 @@ class APB4FlatInterface(FlatInterface):
|
||||
f"output logic {self.signal('PSEL', child)}",
|
||||
f"output logic {self.signal('PENABLE', child)}",
|
||||
f"output logic {self.signal('PWRITE', child)}",
|
||||
f"output logic [{self.cpuif.addr_width - 1}:0] {self.signal('PADDR', child)}",
|
||||
f"output logic [{clog2(child.size) - 1}:0] {self.signal('PADDR', child)}",
|
||||
f"output logic [2:0] {self.signal('PPROT', child)}",
|
||||
f"output logic [{self.cpuif.data_width - 1}:0] {self.signal('PWDATA', child)}",
|
||||
f"output logic [{self.cpuif.data_width // 8 - 1}:0] {self.signal('PSTRB', child)}",
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
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 %}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
$bits({{cpuif.signal("WDATA")}}), {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH);
|
||||
end
|
||||
|
||||
`ifdef PEAKRDL_ASSERTIONS
|
||||
// Simple handshake sanity (one-cycle implication; relax/adjust as needed)
|
||||
assert_rd_resp_enc: assert property (@(posedge {{cpuif.signal("ACLK")}})
|
||||
{{cpuif.signal("RVALID")}} |-> (^{{cpuif.signal("RRESP")}} !== 1'bx))
|
||||
@@ -23,6 +24,7 @@
|
||||
assert_wr_resp_enc: assert property (@(posedge {{cpuif.signal("ACLK")}})
|
||||
{{cpuif.signal("BVALID")}} |-> (^{{cpuif.signal("BRESP")}} !== 1'bx))
|
||||
else $error("BRESP must be a legal AXI response when BVALID is high");
|
||||
`endif
|
||||
`endif
|
||||
{% endif -%}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
3
src/peakrdl_busdecoder/cpuif/taxi_apb/__init__.py
Normal file
3
src/peakrdl_busdecoder/cpuif/taxi_apb/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .taxi_apb_cpuif import TaxiAPBCpuif
|
||||
|
||||
__all__ = ["TaxiAPBCpuif"]
|
||||
99
src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_cpuif.py
Normal file
99
src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_cpuif.py
Normal file
@@ -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;",
|
||||
]
|
||||
16
src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_interface.py
Normal file
16
src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_interface.py
Normal file
@@ -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_"
|
||||
45
src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_tmpl.sv
Normal file
45
src/peakrdl_busdecoder/cpuif/taxi_apb/taxi_apb_tmpl.sv
Normal file
@@ -0,0 +1,45 @@
|
||||
{%- 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;
|
||||
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)}}
|
||||
Reference in New Issue
Block a user