peakrdl-busdecoder 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- peakrdl_busdecoder/__init__.py +3 -0
- peakrdl_busdecoder/__peakrdl__.py +136 -0
- peakrdl_busdecoder/body/__init__.py +14 -0
- peakrdl_busdecoder/body/body.py +22 -0
- peakrdl_busdecoder/body/combinational_body.py +10 -0
- peakrdl_busdecoder/body/for_loop_body.py +16 -0
- peakrdl_busdecoder/body/if_body.py +91 -0
- peakrdl_busdecoder/body/struct_body.py +25 -0
- peakrdl_busdecoder/cpuif/__init__.py +3 -0
- peakrdl_busdecoder/cpuif/apb3/__init__.py +4 -0
- peakrdl_busdecoder/cpuif/apb3/apb3_cpuif.py +67 -0
- peakrdl_busdecoder/cpuif/apb3/apb3_cpuif_flat.py +68 -0
- peakrdl_busdecoder/cpuif/apb3/apb3_interface.py +56 -0
- peakrdl_busdecoder/cpuif/apb3/apb3_tmpl.sv +33 -0
- peakrdl_busdecoder/cpuif/apb4/__init__.py +4 -0
- peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py +70 -0
- peakrdl_busdecoder/cpuif/apb4/apb4_cpuif_flat.py +70 -0
- peakrdl_busdecoder/cpuif/apb4/apb4_interface.py +60 -0
- peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv +36 -0
- peakrdl_busdecoder/cpuif/axi4lite/__init__.py +4 -0
- peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif.py +84 -0
- peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif_flat.py +86 -0
- peakrdl_busdecoder/cpuif/axi4lite/axi4lite_interface.py +84 -0
- peakrdl_busdecoder/cpuif/axi4lite/axi4lite_tmpl.sv +60 -0
- peakrdl_busdecoder/cpuif/base_cpuif.py +118 -0
- peakrdl_busdecoder/cpuif/fanin_gen.py +64 -0
- peakrdl_busdecoder/cpuif/fanout_gen.py +50 -0
- peakrdl_busdecoder/cpuif/interface.py +190 -0
- peakrdl_busdecoder/decode_logic_gen.py +152 -0
- peakrdl_busdecoder/design_scanner.py +46 -0
- peakrdl_busdecoder/design_state.py +74 -0
- peakrdl_busdecoder/exporter.py +142 -0
- peakrdl_busdecoder/identifier_filter.py +263 -0
- peakrdl_busdecoder/listener.py +58 -0
- peakrdl_busdecoder/module_tmpl.sv +79 -0
- peakrdl_busdecoder/package_tmpl.sv +21 -0
- peakrdl_busdecoder/py.typed +0 -0
- peakrdl_busdecoder/struct_gen.py +57 -0
- peakrdl_busdecoder/sv_int.py +21 -0
- peakrdl_busdecoder/udps/__init__.py +5 -0
- peakrdl_busdecoder/utils.py +80 -0
- peakrdl_busdecoder/validate_design.py +185 -0
- peakrdl_busdecoder-0.2.0.dist-info/METADATA +40 -0
- peakrdl_busdecoder-0.2.0.dist-info/RECORD +48 -0
- peakrdl_busdecoder-0.2.0.dist-info/WHEEL +5 -0
- peakrdl_busdecoder-0.2.0.dist-info/entry_points.txt +2 -0
- peakrdl_busdecoder-0.2.0.dist-info/licenses/LICENSE +165 -0
- peakrdl_busdecoder-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from systemrdl.node import AddressableNode
|
|
4
|
+
|
|
5
|
+
from ...utils import get_indexed_path
|
|
6
|
+
from ..base_cpuif import BaseCpuif
|
|
7
|
+
from .apb4_interface import APB4FlatInterface
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ...exporter import BusDecoderExporter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class APB4CpuifFlat(BaseCpuif):
|
|
14
|
+
template_path = "apb4_tmpl.sv"
|
|
15
|
+
|
|
16
|
+
def __init__(self, exp: "BusDecoderExporter") -> None:
|
|
17
|
+
super().__init__(exp)
|
|
18
|
+
self._interface = APB4FlatInterface(self)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def is_interface(self) -> bool:
|
|
22
|
+
return self._interface.is_interface
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def port_declaration(self) -> str:
|
|
26
|
+
return self._interface.get_port_declaration("s_apb_", "m_apb_")
|
|
27
|
+
|
|
28
|
+
def signal(
|
|
29
|
+
self,
|
|
30
|
+
signal: str,
|
|
31
|
+
node: AddressableNode | None = None,
|
|
32
|
+
idx: str | int | None = None,
|
|
33
|
+
) -> str:
|
|
34
|
+
return self._interface.signal(signal, node, idx)
|
|
35
|
+
|
|
36
|
+
def fanout(self, node: AddressableNode) -> str:
|
|
37
|
+
fanout: dict[str, str] = {}
|
|
38
|
+
fanout[self.signal("PSEL", node)] = (
|
|
39
|
+
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'i')}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'i')}"
|
|
40
|
+
)
|
|
41
|
+
fanout[self.signal("PENABLE", node)] = self.signal("PENABLE")
|
|
42
|
+
fanout[self.signal("PWRITE", node)] = (
|
|
43
|
+
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'i')}"
|
|
44
|
+
)
|
|
45
|
+
fanout[self.signal("PADDR", node)] = self.signal("PADDR")
|
|
46
|
+
fanout[self.signal("PPROT", node)] = self.signal("PPROT")
|
|
47
|
+
fanout[self.signal("PWDATA", node)] = "cpuif_wr_data"
|
|
48
|
+
fanout[self.signal("PSTRB", node)] = "cpuif_wr_byte_en"
|
|
49
|
+
|
|
50
|
+
return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items()))
|
|
51
|
+
|
|
52
|
+
def fanin(self, node: AddressableNode | None = None) -> str:
|
|
53
|
+
fanin: dict[str, str] = {}
|
|
54
|
+
if node is None:
|
|
55
|
+
fanin["cpuif_rd_ack"] = "'0"
|
|
56
|
+
fanin["cpuif_rd_err"] = "'0"
|
|
57
|
+
else:
|
|
58
|
+
fanin["cpuif_rd_ack"] = self.signal("PREADY", node)
|
|
59
|
+
fanin["cpuif_rd_err"] = self.signal("PSLVERR", node)
|
|
60
|
+
|
|
61
|
+
return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
|
|
62
|
+
|
|
63
|
+
def readback(self, node: AddressableNode | None = None) -> str:
|
|
64
|
+
fanin: dict[str, str] = {}
|
|
65
|
+
if node is None:
|
|
66
|
+
fanin["cpuif_rd_data"] = "'0"
|
|
67
|
+
else:
|
|
68
|
+
fanin["cpuif_rd_data"] = self.signal("PRDATA", node)
|
|
69
|
+
|
|
70
|
+
return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""APB4-specific interface implementations."""
|
|
2
|
+
|
|
3
|
+
from systemrdl.node import AddressableNode
|
|
4
|
+
|
|
5
|
+
from ..interface import FlatInterface, SVInterface
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class APB4SVInterface(SVInterface):
|
|
9
|
+
"""APB4 SystemVerilog interface."""
|
|
10
|
+
|
|
11
|
+
def get_interface_type(self) -> str:
|
|
12
|
+
return "apb4_intf"
|
|
13
|
+
|
|
14
|
+
def get_slave_name(self) -> str:
|
|
15
|
+
return "s_apb"
|
|
16
|
+
|
|
17
|
+
def get_master_prefix(self) -> str:
|
|
18
|
+
return "m_apb_"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class APB4FlatInterface(FlatInterface):
|
|
22
|
+
"""APB4 flat signal interface."""
|
|
23
|
+
|
|
24
|
+
def get_slave_prefix(self) -> str:
|
|
25
|
+
return "s_apb_"
|
|
26
|
+
|
|
27
|
+
def get_master_prefix(self) -> str:
|
|
28
|
+
return "m_apb_"
|
|
29
|
+
|
|
30
|
+
def _get_slave_port_declarations(self, slave_prefix: str) -> list[str]:
|
|
31
|
+
return [
|
|
32
|
+
f"input logic {slave_prefix}PCLK",
|
|
33
|
+
f"input logic {slave_prefix}PRESETn",
|
|
34
|
+
f"input logic {slave_prefix}PSEL",
|
|
35
|
+
f"input logic {slave_prefix}PENABLE",
|
|
36
|
+
f"input logic {slave_prefix}PWRITE",
|
|
37
|
+
f"input logic [{self.cpuif.addr_width - 1}:0] {slave_prefix}PADDR",
|
|
38
|
+
f"input logic [2:0] {slave_prefix}PPROT",
|
|
39
|
+
f"input logic [{self.cpuif.data_width - 1}:0] {slave_prefix}PWDATA",
|
|
40
|
+
f"input logic [{self.cpuif.data_width // 8 - 1}:0] {slave_prefix}PSTRB",
|
|
41
|
+
f"output logic [{self.cpuif.data_width - 1}:0] {slave_prefix}PRDATA",
|
|
42
|
+
f"output logic {slave_prefix}PREADY",
|
|
43
|
+
f"output logic {slave_prefix}PSLVERR",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
def _get_master_port_declarations(self, child: AddressableNode, master_prefix: str) -> list[str]:
|
|
47
|
+
return [
|
|
48
|
+
f"output logic {self.signal('PCLK', child)}",
|
|
49
|
+
f"output logic {self.signal('PRESETn', child)}",
|
|
50
|
+
f"output logic {self.signal('PSEL', child)}",
|
|
51
|
+
f"output logic {self.signal('PENABLE', child)}",
|
|
52
|
+
f"output logic {self.signal('PWRITE', child)}",
|
|
53
|
+
f"output logic [{self.cpuif.addr_width - 1}:0] {self.signal('PADDR', child)}",
|
|
54
|
+
f"output logic [2:0] {self.signal('PPROT', child)}",
|
|
55
|
+
f"output logic [{self.cpuif.data_width - 1}:0] {self.signal('PWDATA', child)}",
|
|
56
|
+
f"output logic [{self.cpuif.data_width // 8 - 1}:0] {self.signal('PSTRB', child)}",
|
|
57
|
+
f"input logic [{self.cpuif.data_width - 1}:0] {self.signal('PRDATA', child)}",
|
|
58
|
+
f"input logic {self.signal('PREADY', child)}",
|
|
59
|
+
f"input logic {self.signal('PSLVERR', child)}",
|
|
60
|
+
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{%- if cpuif.is_interface %}
|
|
2
|
+
`ifndef SYNTHESIS
|
|
3
|
+
initial begin
|
|
4
|
+
assert_bad_addr_width: assert($bits({{cpuif.signal("PADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH)
|
|
5
|
+
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);
|
|
6
|
+
assert_bad_data_width: assert($bits({{cpuif.signal("PWDATA")}}) == {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH)
|
|
7
|
+
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);
|
|
8
|
+
end
|
|
9
|
+
assert_wr_sel: assert property (@(posedge {{cpuif.signal("PCLK")}}) {{cpuif.signal("PSEL")}} && {{cpuif.signal("PWRITE")}} |-> ##1 ({{cpuif.signal("PREADY")}} || {{cpuif.signal("PSLVERR")}}))
|
|
10
|
+
else $error("APB4 Slave port SEL implies that cpuif_wr_sel must be one-hot encoded");
|
|
11
|
+
`endif
|
|
12
|
+
{%- endif %}
|
|
13
|
+
|
|
14
|
+
assign cpuif_req = {{cpuif.signal("PSEL")}};
|
|
15
|
+
assign cpuif_wr_en = {{cpuif.signal("PWRITE")}};
|
|
16
|
+
assign cpuif_rd_en = !{{cpuif.signal("PWRITE")}};
|
|
17
|
+
|
|
18
|
+
assign cpuif_wr_addr = {{cpuif.signal("PADDR")}};
|
|
19
|
+
assign cpuif_rd_addr = {{cpuif.signal("PADDR")}};
|
|
20
|
+
|
|
21
|
+
assign cpuif_wr_data = {{cpuif.signal("PWDATA")}};
|
|
22
|
+
assign cpuif_wr_byte_en = {{cpuif.signal("PSTRB")}};
|
|
23
|
+
|
|
24
|
+
assign {{cpuif.signal("PRDATA")}} = cpuif_rd_data;
|
|
25
|
+
assign {{cpuif.signal("PREADY")}} = cpuif_rd_ack;
|
|
26
|
+
assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpuif_wr_sel.cpuif_err;
|
|
27
|
+
|
|
28
|
+
//--------------------------------------------------------------------------
|
|
29
|
+
// Fanout CPU Bus interface signals
|
|
30
|
+
//--------------------------------------------------------------------------
|
|
31
|
+
{{fanout|walk(cpuif=cpuif)}}
|
|
32
|
+
|
|
33
|
+
//--------------------------------------------------------------------------
|
|
34
|
+
// Fanin CPU Bus interface signals
|
|
35
|
+
//--------------------------------------------------------------------------
|
|
36
|
+
{{fanin|walk(cpuif=cpuif)}}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, overload
|
|
2
|
+
|
|
3
|
+
from systemrdl.node import AddressableNode
|
|
4
|
+
|
|
5
|
+
from ...utils import get_indexed_path
|
|
6
|
+
from ..base_cpuif import BaseCpuif
|
|
7
|
+
from .axi4lite_interface import AXI4LiteSVInterface
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ...exporter import BusDecoderExporter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AXI4LiteCpuif(BaseCpuif):
|
|
14
|
+
template_path = "axi4lite_tmpl.sv"
|
|
15
|
+
|
|
16
|
+
def __init__(self, exp: "BusDecoderExporter") -> None:
|
|
17
|
+
super().__init__(exp)
|
|
18
|
+
self._interface = AXI4LiteSVInterface(self)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def is_interface(self) -> bool:
|
|
22
|
+
return self._interface.is_interface
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def port_declaration(self) -> str:
|
|
26
|
+
"""Returns the port declaration for the AXI4-Lite interface."""
|
|
27
|
+
return self._interface.get_port_declaration("s_axil", "m_axil_")
|
|
28
|
+
|
|
29
|
+
@overload
|
|
30
|
+
def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
|
|
31
|
+
@overload
|
|
32
|
+
def signal(self, signal: str, node: AddressableNode, indexer: str | None = None) -> str: ...
|
|
33
|
+
def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
|
|
34
|
+
return self._interface.signal(signal, node, indexer)
|
|
35
|
+
|
|
36
|
+
def fanout(self, node: AddressableNode) -> str:
|
|
37
|
+
fanout: dict[str, str] = {}
|
|
38
|
+
|
|
39
|
+
wr_sel = f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
|
40
|
+
rd_sel = f"cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
|
41
|
+
|
|
42
|
+
# Write address channel
|
|
43
|
+
fanout[self.signal("AWVALID", node, "gi")] = wr_sel
|
|
44
|
+
fanout[self.signal("AWADDR", node, "gi")] = self.signal("AWADDR")
|
|
45
|
+
fanout[self.signal("AWPROT", node, "gi")] = self.signal("AWPROT")
|
|
46
|
+
|
|
47
|
+
# Write data channel
|
|
48
|
+
fanout[self.signal("WVALID", node, "gi")] = wr_sel
|
|
49
|
+
fanout[self.signal("WDATA", node, "gi")] = "cpuif_wr_data"
|
|
50
|
+
fanout[self.signal("WSTRB", node, "gi")] = "cpuif_wr_byte_en"
|
|
51
|
+
|
|
52
|
+
# Write response channel (master -> slave)
|
|
53
|
+
fanout[self.signal("BREADY", node, "gi")] = self.signal("BREADY")
|
|
54
|
+
|
|
55
|
+
# Read address channel
|
|
56
|
+
fanout[self.signal("ARVALID", node, "gi")] = rd_sel
|
|
57
|
+
fanout[self.signal("ARADDR", node, "gi")] = self.signal("ARADDR")
|
|
58
|
+
fanout[self.signal("ARPROT", node, "gi")] = self.signal("ARPROT")
|
|
59
|
+
|
|
60
|
+
# Read data channel (master -> slave)
|
|
61
|
+
fanout[self.signal("RREADY", node, "gi")] = self.signal("RREADY")
|
|
62
|
+
|
|
63
|
+
return "\n".join(f"assign {lhs} = {rhs};" for lhs, rhs in fanout.items())
|
|
64
|
+
|
|
65
|
+
def fanin(self, node: AddressableNode | None = None) -> str:
|
|
66
|
+
fanin: dict[str, str] = {}
|
|
67
|
+
if node is None:
|
|
68
|
+
fanin["cpuif_rd_ack"] = "'0"
|
|
69
|
+
fanin["cpuif_rd_err"] = "'0"
|
|
70
|
+
else:
|
|
71
|
+
# Read side: ack comes from RVALID; err if RRESP[1] is set (SLVERR/DECERR)
|
|
72
|
+
fanin["cpuif_rd_ack"] = self.signal("RVALID", node, "i")
|
|
73
|
+
fanin["cpuif_rd_err"] = f"{self.signal('RRESP', node, 'i')}[1]"
|
|
74
|
+
|
|
75
|
+
return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
|
|
76
|
+
|
|
77
|
+
def readback(self, node: AddressableNode | None = None) -> str:
|
|
78
|
+
fanin: dict[str, str] = {}
|
|
79
|
+
if node is None:
|
|
80
|
+
fanin["cpuif_rd_data"] = "'0"
|
|
81
|
+
else:
|
|
82
|
+
fanin["cpuif_rd_data"] = self.signal("RDATA", node, "i")
|
|
83
|
+
|
|
84
|
+
return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, overload
|
|
2
|
+
|
|
3
|
+
from systemrdl.node import AddressableNode
|
|
4
|
+
|
|
5
|
+
from ...utils import get_indexed_path
|
|
6
|
+
from ..base_cpuif import BaseCpuif
|
|
7
|
+
from .axi4lite_interface import AXI4LiteFlatInterface
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ...exporter import BusDecoderExporter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AXI4LiteCpuifFlat(BaseCpuif):
|
|
14
|
+
"""Verilator-friendly variant that flattens the AXI4-Lite interface ports."""
|
|
15
|
+
|
|
16
|
+
template_path = "axi4lite_tmpl.sv"
|
|
17
|
+
|
|
18
|
+
def __init__(self, exp: "BusDecoderExporter") -> None:
|
|
19
|
+
super().__init__(exp)
|
|
20
|
+
self._interface = AXI4LiteFlatInterface(self)
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def is_interface(self) -> bool:
|
|
24
|
+
return self._interface.is_interface
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def port_declaration(self) -> str:
|
|
28
|
+
"""Returns the port declaration for the AXI4-Lite interface."""
|
|
29
|
+
return self._interface.get_port_declaration("s_axil_", "m_axil_")
|
|
30
|
+
|
|
31
|
+
@overload
|
|
32
|
+
def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
|
|
33
|
+
@overload
|
|
34
|
+
def signal(self, signal: str, node: AddressableNode, indexer: str | None = None) -> str: ...
|
|
35
|
+
def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
|
|
36
|
+
return self._interface.signal(signal, node, indexer)
|
|
37
|
+
|
|
38
|
+
def fanout(self, node: AddressableNode) -> str:
|
|
39
|
+
fanout: dict[str, str] = {}
|
|
40
|
+
|
|
41
|
+
wr_sel = f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
|
42
|
+
rd_sel = f"cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
|
43
|
+
|
|
44
|
+
# Write address channel
|
|
45
|
+
fanout[self.signal("AWVALID", node, "gi")] = wr_sel
|
|
46
|
+
fanout[self.signal("AWADDR", node, "gi")] = self.signal("AWADDR")
|
|
47
|
+
fanout[self.signal("AWPROT", node, "gi")] = self.signal("AWPROT")
|
|
48
|
+
|
|
49
|
+
# Write data channel
|
|
50
|
+
fanout[self.signal("WVALID", node, "gi")] = wr_sel
|
|
51
|
+
fanout[self.signal("WDATA", node, "gi")] = "cpuif_wr_data"
|
|
52
|
+
fanout[self.signal("WSTRB", node, "gi")] = "cpuif_wr_byte_en"
|
|
53
|
+
|
|
54
|
+
# Write response channel (master -> slave)
|
|
55
|
+
fanout[self.signal("BREADY", node, "gi")] = self.signal("BREADY")
|
|
56
|
+
|
|
57
|
+
# Read address channel
|
|
58
|
+
fanout[self.signal("ARVALID", node, "gi")] = rd_sel
|
|
59
|
+
fanout[self.signal("ARADDR", node, "gi")] = self.signal("ARADDR")
|
|
60
|
+
fanout[self.signal("ARPROT", node, "gi")] = self.signal("ARPROT")
|
|
61
|
+
|
|
62
|
+
# Read data channel (master -> slave)
|
|
63
|
+
fanout[self.signal("RREADY", node, "gi")] = self.signal("RREADY")
|
|
64
|
+
|
|
65
|
+
return "\n".join(f"assign {lhs} = {rhs};" for lhs, rhs in fanout.items())
|
|
66
|
+
|
|
67
|
+
def fanin(self, node: AddressableNode | None = None) -> str:
|
|
68
|
+
fanin: dict[str, str] = {}
|
|
69
|
+
if node is None:
|
|
70
|
+
fanin["cpuif_rd_ack"] = "'0"
|
|
71
|
+
fanin["cpuif_rd_err"] = "'0"
|
|
72
|
+
else:
|
|
73
|
+
# Read side: ack comes from RVALID; err if RRESP[1] is set (SLVERR/DECERR)
|
|
74
|
+
fanin["cpuif_rd_ack"] = self.signal("RVALID", node, "i")
|
|
75
|
+
fanin["cpuif_rd_err"] = f"{self.signal('RRESP', node, 'i')}[1]"
|
|
76
|
+
|
|
77
|
+
return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
|
|
78
|
+
|
|
79
|
+
def readback(self, node: AddressableNode | None = None) -> str:
|
|
80
|
+
fanin: dict[str, str] = {}
|
|
81
|
+
if node is None:
|
|
82
|
+
fanin["cpuif_rd_data"] = "'0"
|
|
83
|
+
else:
|
|
84
|
+
fanin["cpuif_rd_data"] = self.signal("RDATA", node, "i")
|
|
85
|
+
|
|
86
|
+
return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""AXI4-Lite-specific interface implementations."""
|
|
2
|
+
|
|
3
|
+
from systemrdl.node import AddressableNode
|
|
4
|
+
|
|
5
|
+
from ..interface import FlatInterface, SVInterface
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AXI4LiteSVInterface(SVInterface):
|
|
9
|
+
"""AXI4-Lite SystemVerilog interface."""
|
|
10
|
+
|
|
11
|
+
def get_interface_type(self) -> str:
|
|
12
|
+
return "axi4lite_intf"
|
|
13
|
+
|
|
14
|
+
def get_slave_name(self) -> str:
|
|
15
|
+
return "s_axil"
|
|
16
|
+
|
|
17
|
+
def get_master_prefix(self) -> str:
|
|
18
|
+
return "m_axil_"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AXI4LiteFlatInterface(FlatInterface):
|
|
22
|
+
"""AXI4-Lite flat signal interface."""
|
|
23
|
+
|
|
24
|
+
def get_slave_prefix(self) -> str:
|
|
25
|
+
return "s_axil_"
|
|
26
|
+
|
|
27
|
+
def get_master_prefix(self) -> str:
|
|
28
|
+
return "m_axil_"
|
|
29
|
+
|
|
30
|
+
def _get_slave_port_declarations(self, slave_prefix: str) -> list[str]:
|
|
31
|
+
return [
|
|
32
|
+
# Write address channel
|
|
33
|
+
f"input logic {slave_prefix}AWVALID",
|
|
34
|
+
f"output logic {slave_prefix}AWREADY",
|
|
35
|
+
f"input logic [{self.cpuif.addr_width - 1}:0] {slave_prefix}AWADDR",
|
|
36
|
+
f"input logic [2:0] {slave_prefix}AWPROT",
|
|
37
|
+
# Write data channel
|
|
38
|
+
f"input logic {slave_prefix}WVALID",
|
|
39
|
+
f"output logic {slave_prefix}WREADY",
|
|
40
|
+
f"input logic [{self.cpuif.data_width - 1}:0] {slave_prefix}WDATA",
|
|
41
|
+
f"input logic [{self.cpuif.data_width // 8 - 1}:0] {slave_prefix}WSTRB",
|
|
42
|
+
# Write response channel
|
|
43
|
+
f"output logic {slave_prefix}BVALID",
|
|
44
|
+
f"input logic {slave_prefix}BREADY",
|
|
45
|
+
f"output logic [1:0] {slave_prefix}BRESP",
|
|
46
|
+
# Read address channel
|
|
47
|
+
f"input logic {slave_prefix}ARVALID",
|
|
48
|
+
f"output logic {slave_prefix}ARREADY",
|
|
49
|
+
f"input logic [{self.cpuif.addr_width - 1}:0] {slave_prefix}ARADDR",
|
|
50
|
+
f"input logic [2:0] {slave_prefix}ARPROT",
|
|
51
|
+
# Read data channel
|
|
52
|
+
f"output logic {slave_prefix}RVALID",
|
|
53
|
+
f"input logic {slave_prefix}RREADY",
|
|
54
|
+
f"output logic [{self.cpuif.data_width - 1}:0] {slave_prefix}RDATA",
|
|
55
|
+
f"output logic [1:0] {slave_prefix}RRESP",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
def _get_master_port_declarations(self, child: AddressableNode, master_prefix: str) -> list[str]:
|
|
59
|
+
return [
|
|
60
|
+
# Write address channel
|
|
61
|
+
f"output logic {self.signal('AWVALID', child)}",
|
|
62
|
+
f"input logic {self.signal('AWREADY', child)}",
|
|
63
|
+
f"output logic [{self.cpuif.addr_width - 1}:0] {self.signal('AWADDR', child)}",
|
|
64
|
+
f"output logic [2:0] {self.signal('AWPROT', child)}",
|
|
65
|
+
# Write data channel
|
|
66
|
+
f"output logic {self.signal('WVALID', child)}",
|
|
67
|
+
f"input logic {self.signal('WREADY', child)}",
|
|
68
|
+
f"output logic [{self.cpuif.data_width - 1}:0] {self.signal('WDATA', child)}",
|
|
69
|
+
f"output logic [{self.cpuif.data_width // 8 - 1}:0] {self.signal('WSTRB', child)}",
|
|
70
|
+
# Write response channel
|
|
71
|
+
f"input logic {self.signal('BVALID', child)}",
|
|
72
|
+
f"output logic {self.signal('BREADY', child)}",
|
|
73
|
+
f"input logic [1:0] {self.signal('BRESP', child)}",
|
|
74
|
+
# Read address channel
|
|
75
|
+
f"output logic {self.signal('ARVALID', child)}",
|
|
76
|
+
f"input logic {self.signal('ARREADY', child)}",
|
|
77
|
+
f"output logic [{self.cpuif.addr_width - 1}:0] {self.signal('ARADDR', child)}",
|
|
78
|
+
f"output logic [2:0] {self.signal('ARPROT', child)}",
|
|
79
|
+
# Read data channel
|
|
80
|
+
f"input logic {self.signal('RVALID', child)}",
|
|
81
|
+
f"output logic {self.signal('RREADY', child)}",
|
|
82
|
+
f"input logic [{self.cpuif.data_width - 1}:0] {self.signal('RDATA', child)}",
|
|
83
|
+
f"input logic [1:0] {self.signal('RRESP', child)}",
|
|
84
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{%- if cpuif.is_interface -%}
|
|
2
|
+
`ifndef SYNTHESIS
|
|
3
|
+
initial begin
|
|
4
|
+
// Width checks (AXI4-Lite)
|
|
5
|
+
assert_bad_awaddr_width: assert($bits({{cpuif.signal("AWADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH)
|
|
6
|
+
else $error("AWADDR width %0d < MIN_ADDR_WIDTH %0d",
|
|
7
|
+
$bits({{cpuif.signal("AWADDR")}}), {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH);
|
|
8
|
+
|
|
9
|
+
assert_bad_araddr_width: assert($bits({{cpuif.signal("ARADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH)
|
|
10
|
+
else $error("ARADDR width %0d < MIN_ADDR_WIDTH %0d",
|
|
11
|
+
$bits({{cpuif.signal("ARADDR")}}), {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH);
|
|
12
|
+
|
|
13
|
+
assert_bad_data_width: assert($bits({{cpuif.signal("WDATA")}}) == {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH)
|
|
14
|
+
else $error("WDATA width %0d != DATA_WIDTH %0d",
|
|
15
|
+
$bits({{cpuif.signal("WDATA")}}), {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH);
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
// Simple handshake sanity (one-cycle implication; relax/adjust as needed)
|
|
19
|
+
assert_rd_resp_enc: assert property (@(posedge {{cpuif.signal("ACLK")}})
|
|
20
|
+
{{cpuif.signal("RVALID")}} |-> (^{{cpuif.signal("RRESP")}} !== 1'bx))
|
|
21
|
+
else $error("RRESP must be a legal AXI response when RVALID is high");
|
|
22
|
+
|
|
23
|
+
assert_wr_resp_enc: assert property (@(posedge {{cpuif.signal("ACLK")}})
|
|
24
|
+
{{cpuif.signal("BVALID")}} |-> (^{{cpuif.signal("BRESP")}} !== 1'bx))
|
|
25
|
+
else $error("BRESP must be a legal AXI response when BVALID is high");
|
|
26
|
+
`endif
|
|
27
|
+
{% endif -%}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
assign cpuif_req = {{cpuif.signal("AWVALID")}} | {{cpuif.signal("ARVALID")}};
|
|
31
|
+
assign cpuif_wr_en = {{cpuif.signal("AWVALID")}} & {{cpuif.signal("WVALID")}};
|
|
32
|
+
assign cpuif_rd_en = {{cpuif.signal("ARVALID")}};
|
|
33
|
+
|
|
34
|
+
assign cpuif_wr_addr = {{cpuif.signal("AWADDR")}};
|
|
35
|
+
assign cpuif_rd_addr = {{cpuif.signal("ARADDR")}};
|
|
36
|
+
|
|
37
|
+
assign cpuif_wr_data = {{cpuif.signal("WDATA")}};
|
|
38
|
+
assign cpuif_wr_byte_en = {{cpuif.signal("WSTRB")}};
|
|
39
|
+
|
|
40
|
+
//
|
|
41
|
+
// Return paths back to AXI master from generic cpuif_*
|
|
42
|
+
// Read: ack=RVALID, err=RRESP[1] (SLVERR/DECERR), data=RDATA
|
|
43
|
+
//
|
|
44
|
+
assign {{cpuif.signal("RDATA")}} = cpuif_rd_data;
|
|
45
|
+
assign {{cpuif.signal("RVALID")}} = cpuif_rd_ack;
|
|
46
|
+
assign {{cpuif.signal("RRESP")}} = (cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpuif_wr_sel.cpuif_err) ? 2'b10 : 2'b00;
|
|
47
|
+
|
|
48
|
+
// Write: ack=BVALID, err=BRESP[1]
|
|
49
|
+
assign {{cpuif.signal("BVALID")}} = cpuif_wr_ack;
|
|
50
|
+
assign {{cpuif.signal("BRESP")}} = (cpuif_wr_err | cpuif_wr_sel.cpuif_err | cpuif_rd_sel.cpuif_err) ? 2'b10 : 2'b00;
|
|
51
|
+
|
|
52
|
+
//--------------------------------------------------------------------------
|
|
53
|
+
// Fanout CPU Bus interface signals
|
|
54
|
+
//--------------------------------------------------------------------------
|
|
55
|
+
{{fanout|walk(cpuif=cpuif)}}
|
|
56
|
+
|
|
57
|
+
//--------------------------------------------------------------------------
|
|
58
|
+
// Fanin CPU Bus interface signals
|
|
59
|
+
//--------------------------------------------------------------------------
|
|
60
|
+
{{fanin|walk(cpuif=cpuif)}}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import os
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import jinja2 as jj
|
|
6
|
+
from systemrdl.node import AddressableNode
|
|
7
|
+
|
|
8
|
+
from ..utils import clog2, get_indexed_path, is_pow2, roundup_pow2
|
|
9
|
+
from .fanin_gen import FaninGenerator
|
|
10
|
+
from .fanout_gen import FanoutGenerator
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from ..exporter import BusDecoderExporter
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseCpuif:
|
|
17
|
+
# Path is relative to the location of the class that assigns this variable
|
|
18
|
+
template_path = ""
|
|
19
|
+
|
|
20
|
+
def __init__(self, exp: "BusDecoderExporter") -> None:
|
|
21
|
+
self.exp = exp
|
|
22
|
+
self.reset = exp.ds.top_node.cpuif_reset
|
|
23
|
+
self.unroll = exp.ds.cpuif_unroll
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def addressable_children(self) -> list[AddressableNode]:
|
|
27
|
+
return [
|
|
28
|
+
child
|
|
29
|
+
for child in self.exp.ds.top_node.children(unroll=self.unroll)
|
|
30
|
+
if isinstance(child, AddressableNode)
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def addr_width(self) -> int:
|
|
35
|
+
return self.exp.ds.addr_width
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def data_width(self) -> int:
|
|
39
|
+
return self.exp.ds.cpuif_data_width
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def data_width_bytes(self) -> int:
|
|
43
|
+
return self.data_width // 8
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def port_declaration(self) -> str:
|
|
47
|
+
raise NotImplementedError()
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def parameters(self) -> list[str]:
|
|
51
|
+
"""
|
|
52
|
+
Optional list of additional parameters this CPU interface provides to
|
|
53
|
+
the module's definition
|
|
54
|
+
"""
|
|
55
|
+
array_parameters = [
|
|
56
|
+
f"localparam N_{child.inst_name.upper()}S = {child.n_elements}"
|
|
57
|
+
for child in self.addressable_children
|
|
58
|
+
if self.check_is_array(child)
|
|
59
|
+
]
|
|
60
|
+
return array_parameters
|
|
61
|
+
|
|
62
|
+
def _get_template_path_class_dir(self) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Traverse up the MRO and find the first class that explicitly assigns
|
|
65
|
+
template_path. Returns the directory that contains the class definition.
|
|
66
|
+
"""
|
|
67
|
+
for cls in inspect.getmro(self.__class__):
|
|
68
|
+
if "template_path" in cls.__dict__:
|
|
69
|
+
class_dir = os.path.dirname(inspect.getfile(cls))
|
|
70
|
+
return class_dir
|
|
71
|
+
raise RuntimeError
|
|
72
|
+
|
|
73
|
+
def check_is_array(self, node: AddressableNode) -> bool:
|
|
74
|
+
# When unrolling is enabled, children(unroll=True) returns individual
|
|
75
|
+
# array elements with current_idx set. These should NOT be treated as arrays.
|
|
76
|
+
if self.unroll and hasattr(node, "current_idx") and node.current_idx is not None:
|
|
77
|
+
return False
|
|
78
|
+
return node.is_array
|
|
79
|
+
|
|
80
|
+
def get_implementation(self) -> str:
|
|
81
|
+
class_dir = self._get_template_path_class_dir()
|
|
82
|
+
loader = jj.FileSystemLoader(class_dir)
|
|
83
|
+
jj_env = jj.Environment(
|
|
84
|
+
loader=loader,
|
|
85
|
+
undefined=jj.StrictUndefined,
|
|
86
|
+
)
|
|
87
|
+
jj_env.tests["array"] = self.check_is_array # type: ignore
|
|
88
|
+
jj_env.filters["clog2"] = clog2 # type: ignore
|
|
89
|
+
jj_env.filters["is_pow2"] = is_pow2 # type: ignore
|
|
90
|
+
jj_env.filters["roundup_pow2"] = roundup_pow2 # type: ignore
|
|
91
|
+
jj_env.filters["address_slice"] = self.get_address_slice # type: ignore
|
|
92
|
+
jj_env.filters["get_path"] = lambda x: get_indexed_path(self.exp.ds.top_node, x, "i") # type: ignore
|
|
93
|
+
jj_env.filters["walk"] = self.exp.walk # type: ignore
|
|
94
|
+
|
|
95
|
+
context = { # type: ignore
|
|
96
|
+
"cpuif": self,
|
|
97
|
+
"ds": self.exp.ds,
|
|
98
|
+
"fanout": FanoutGenerator,
|
|
99
|
+
"fanin": FaninGenerator,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
template = jj_env.get_template(self.template_path)
|
|
103
|
+
return template.render(context)
|
|
104
|
+
|
|
105
|
+
def get_address_slice(self, node: AddressableNode, cpuif_addr: str = "cpuif_addr") -> str:
|
|
106
|
+
addr = node.raw_absolute_address - self.exp.ds.top_node.raw_absolute_address
|
|
107
|
+
size = node.size
|
|
108
|
+
|
|
109
|
+
return f"({cpuif_addr} - 'h{addr:x})[{clog2(size) - 1}:0]"
|
|
110
|
+
|
|
111
|
+
def fanout(self, node: AddressableNode) -> str:
|
|
112
|
+
raise NotImplementedError
|
|
113
|
+
|
|
114
|
+
def fanin(self, node: AddressableNode | None = None) -> str:
|
|
115
|
+
raise NotImplementedError
|
|
116
|
+
|
|
117
|
+
def readback(self, node: AddressableNode | None = None) -> str:
|
|
118
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from collections import deque
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from systemrdl.node import AddressableNode
|
|
5
|
+
from systemrdl.walker import WalkerAction
|
|
6
|
+
|
|
7
|
+
from ..body import Body, CombinationalBody, ForLoopBody, IfBody
|
|
8
|
+
from ..design_state import DesignState
|
|
9
|
+
from ..listener import BusDecoderListener
|
|
10
|
+
from ..utils import get_indexed_path
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from .base_cpuif import BaseCpuif
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FaninGenerator(BusDecoderListener):
|
|
17
|
+
def __init__(self, ds: DesignState, cpuif: "BaseCpuif") -> None:
|
|
18
|
+
super().__init__(ds)
|
|
19
|
+
self._cpuif = cpuif
|
|
20
|
+
|
|
21
|
+
self._stack: deque[Body] = deque()
|
|
22
|
+
cb = CombinationalBody()
|
|
23
|
+
cb += cpuif.fanin()
|
|
24
|
+
cb += cpuif.readback()
|
|
25
|
+
self._stack.append(cb)
|
|
26
|
+
|
|
27
|
+
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
|
|
28
|
+
action = super().enter_AddressableComponent(node)
|
|
29
|
+
|
|
30
|
+
if node.array_dimensions:
|
|
31
|
+
for i, dim in enumerate(node.array_dimensions):
|
|
32
|
+
fb = ForLoopBody(
|
|
33
|
+
"int",
|
|
34
|
+
f"i{i}",
|
|
35
|
+
dim,
|
|
36
|
+
)
|
|
37
|
+
self._stack.append(fb)
|
|
38
|
+
|
|
39
|
+
ifb = IfBody()
|
|
40
|
+
with ifb.cm(
|
|
41
|
+
f"cpuif_rd_sel.{get_indexed_path(self._cpuif.exp.ds.top_node, node)} || cpuif_wr_sel.{get_indexed_path(self._cpuif.exp.ds.top_node, node)}"
|
|
42
|
+
) as b:
|
|
43
|
+
b += self._cpuif.fanin(node)
|
|
44
|
+
self._stack[-1] += ifb
|
|
45
|
+
|
|
46
|
+
ifb = IfBody()
|
|
47
|
+
with ifb.cm(f"cpuif_rd_sel.{get_indexed_path(self._cpuif.exp.ds.top_node, node)}") as b:
|
|
48
|
+
b += self._cpuif.readback(node)
|
|
49
|
+
self._stack[-1] += ifb
|
|
50
|
+
|
|
51
|
+
return action
|
|
52
|
+
|
|
53
|
+
def exit_AddressableComponent(self, node: AddressableNode) -> None:
|
|
54
|
+
if node.array_dimensions:
|
|
55
|
+
for _ in node.array_dimensions:
|
|
56
|
+
b = self._stack.pop()
|
|
57
|
+
if not b:
|
|
58
|
+
continue
|
|
59
|
+
self._stack[-1] += b
|
|
60
|
+
|
|
61
|
+
super().exit_AddressableComponent(node)
|
|
62
|
+
|
|
63
|
+
def __str__(self) -> str:
|
|
64
|
+
return "\n".join(map(str, self._stack))
|