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.
Files changed (48) hide show
  1. peakrdl_busdecoder/__init__.py +3 -0
  2. peakrdl_busdecoder/__peakrdl__.py +136 -0
  3. peakrdl_busdecoder/body/__init__.py +14 -0
  4. peakrdl_busdecoder/body/body.py +22 -0
  5. peakrdl_busdecoder/body/combinational_body.py +10 -0
  6. peakrdl_busdecoder/body/for_loop_body.py +16 -0
  7. peakrdl_busdecoder/body/if_body.py +91 -0
  8. peakrdl_busdecoder/body/struct_body.py +25 -0
  9. peakrdl_busdecoder/cpuif/__init__.py +3 -0
  10. peakrdl_busdecoder/cpuif/apb3/__init__.py +4 -0
  11. peakrdl_busdecoder/cpuif/apb3/apb3_cpuif.py +67 -0
  12. peakrdl_busdecoder/cpuif/apb3/apb3_cpuif_flat.py +68 -0
  13. peakrdl_busdecoder/cpuif/apb3/apb3_interface.py +56 -0
  14. peakrdl_busdecoder/cpuif/apb3/apb3_tmpl.sv +33 -0
  15. peakrdl_busdecoder/cpuif/apb4/__init__.py +4 -0
  16. peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py +70 -0
  17. peakrdl_busdecoder/cpuif/apb4/apb4_cpuif_flat.py +70 -0
  18. peakrdl_busdecoder/cpuif/apb4/apb4_interface.py +60 -0
  19. peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv +36 -0
  20. peakrdl_busdecoder/cpuif/axi4lite/__init__.py +4 -0
  21. peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif.py +84 -0
  22. peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif_flat.py +86 -0
  23. peakrdl_busdecoder/cpuif/axi4lite/axi4lite_interface.py +84 -0
  24. peakrdl_busdecoder/cpuif/axi4lite/axi4lite_tmpl.sv +60 -0
  25. peakrdl_busdecoder/cpuif/base_cpuif.py +118 -0
  26. peakrdl_busdecoder/cpuif/fanin_gen.py +64 -0
  27. peakrdl_busdecoder/cpuif/fanout_gen.py +50 -0
  28. peakrdl_busdecoder/cpuif/interface.py +190 -0
  29. peakrdl_busdecoder/decode_logic_gen.py +152 -0
  30. peakrdl_busdecoder/design_scanner.py +46 -0
  31. peakrdl_busdecoder/design_state.py +74 -0
  32. peakrdl_busdecoder/exporter.py +142 -0
  33. peakrdl_busdecoder/identifier_filter.py +263 -0
  34. peakrdl_busdecoder/listener.py +58 -0
  35. peakrdl_busdecoder/module_tmpl.sv +79 -0
  36. peakrdl_busdecoder/package_tmpl.sv +21 -0
  37. peakrdl_busdecoder/py.typed +0 -0
  38. peakrdl_busdecoder/struct_gen.py +57 -0
  39. peakrdl_busdecoder/sv_int.py +21 -0
  40. peakrdl_busdecoder/udps/__init__.py +5 -0
  41. peakrdl_busdecoder/utils.py +80 -0
  42. peakrdl_busdecoder/validate_design.py +185 -0
  43. peakrdl_busdecoder-0.2.0.dist-info/METADATA +40 -0
  44. peakrdl_busdecoder-0.2.0.dist-info/RECORD +48 -0
  45. peakrdl_busdecoder-0.2.0.dist-info/WHEEL +5 -0
  46. peakrdl_busdecoder-0.2.0.dist-info/entry_points.txt +2 -0
  47. peakrdl_busdecoder-0.2.0.dist-info/licenses/LICENSE +165 -0
  48. 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,4 @@
1
+ from .axi4_lite_cpuif import AXI4LiteCpuif
2
+ from .axi4_lite_cpuif_flat import AXI4LiteCpuifFlat
3
+
4
+ __all__ = ["AXI4LiteCpuif", "AXI4LiteCpuifFlat"]
@@ -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))