Initial Commit
This commit is contained in:
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal 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
1
MANIFEST.in
Normal file
@@ -0,0 +1 @@
|
||||
recursive-include src/taxi_peakrdl_extensions *.sv
|
||||
51
pyproject.toml
Normal file
51
pyproject.toml
Normal 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"
|
||||
|
||||
119
src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb.py
Normal file
119
src/taxi_peakrdl_extensions/cpuif/busdecoder_taxi_apb.py
Normal 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;",
|
||||
]
|
||||
@@ -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)}}
|
||||
12
src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb.py
Normal file
12
src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb.py
Normal 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
|
||||
51
src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb_tmpl.sv
Normal file
51
src/taxi_peakrdl_extensions/cpuif/regblock_taxi_apb_tmpl.sv
Normal 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;
|
||||
Reference in New Issue
Block a user