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,3 @@
1
+ from .exporter import BusDecoderExporter
2
+
3
+ __all__ = ["BusDecoderExporter"]
@@ -0,0 +1,136 @@
1
+ import functools
2
+ from typing import TYPE_CHECKING, Any
3
+
4
+ from peakrdl.config import schema
5
+ from peakrdl.plugins.entry_points import get_entry_points
6
+ from peakrdl.plugins.exporter import ExporterSubcommandPlugin
7
+
8
+ from .cpuif import BaseCpuif, apb3, apb4, axi4lite
9
+ from .exporter import BusDecoderExporter
10
+ from .udps import ALL_UDPS
11
+
12
+ if TYPE_CHECKING:
13
+ import argparse
14
+
15
+ from systemrdl.node import AddrmapNode
16
+
17
+
18
+ @functools.lru_cache
19
+ def get_cpuifs(config: list[tuple[str, Any]]) -> dict[str, type[BaseCpuif]]:
20
+ # All built-in CPUIFs
21
+ cpuifs: dict[str, type[BaseCpuif]] = {
22
+ # "passthrough": passthrough.PassthroughCpuif,
23
+ "apb3": apb3.APB3Cpuif,
24
+ "apb3-flat": apb3.APB3CpuifFlat,
25
+ "apb4": apb4.APB4Cpuif,
26
+ "apb4-flat": apb4.APB4CpuifFlat,
27
+ "axi4-lite": axi4lite.AXI4LiteCpuif,
28
+ "axi4-lite-flat": axi4lite.AXI4LiteCpuifFlat,
29
+ }
30
+
31
+ # Load any cpuifs specified via entry points
32
+ for ep, _ in get_entry_points("peakrdl_busdecoder.cpuif"):
33
+ name = ep.name
34
+ cpuif = ep.load()
35
+ if name in cpuifs:
36
+ raise RuntimeError(
37
+ f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
38
+ )
39
+ if not issubclass(cpuif, BaseCpuif):
40
+ raise RuntimeError(
41
+ f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a BaseCpuif class"
42
+ )
43
+ cpuifs[name] = cpuif
44
+
45
+ # Load any CPUIFs via config import
46
+ for name, cpuif in config:
47
+ if name in cpuifs:
48
+ raise RuntimeError(
49
+ f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
50
+ )
51
+ if not issubclass(cpuif, BaseCpuif):
52
+ raise RuntimeError(
53
+ f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a BaseCpuif class"
54
+ )
55
+ cpuifs[name] = cpuif
56
+
57
+ return cpuifs
58
+
59
+
60
+ class Exporter(ExporterSubcommandPlugin):
61
+ short_desc = "Generate a SystemVerilog bus decoder for splitting CPU interfaces to sub-address spaces"
62
+
63
+ udp_definitions = ALL_UDPS
64
+
65
+ cfg_schema = { # noqa: RUF012
66
+ "cpuifs": {"*": schema.PythonObjectImport()},
67
+ }
68
+
69
+ def get_cpuifs(self) -> dict[str, type[BaseCpuif]]:
70
+ return get_cpuifs(map(tuple, self.cfg["cpuifs"].items()))
71
+
72
+ def add_exporter_arguments(self, arg_group: "argparse.ArgumentParser") -> None: # type: ignore
73
+ cpuifs = self.get_cpuifs()
74
+
75
+ arg_group.add_argument(
76
+ "--cpuif",
77
+ choices=cpuifs.keys(),
78
+ default="apb4",
79
+ help="Select the CPU interface protocol to use [apb3]",
80
+ )
81
+
82
+ arg_group.add_argument(
83
+ "--module-name",
84
+ metavar="NAME",
85
+ default=None,
86
+ help="Override the SystemVerilog module name",
87
+ )
88
+
89
+ arg_group.add_argument(
90
+ "--package-name",
91
+ metavar="NAME",
92
+ default=None,
93
+ help="Override the SystemVerilog package name",
94
+ )
95
+
96
+ arg_group.add_argument(
97
+ "--addr-width",
98
+ type=int,
99
+ default=None,
100
+ help="""Override the CPU interface's address width. By default,
101
+ address width is sized to the contents of the busdecoder.
102
+ """,
103
+ )
104
+
105
+ arg_group.add_argument(
106
+ "--unroll",
107
+ action="store_true",
108
+ default=False,
109
+ help="""Unroll arrayed addressable nodes into separate instances in
110
+ the CPU interface. By default, arrayed nodes are kept as arrays.
111
+ """,
112
+ )
113
+
114
+ arg_group.add_argument(
115
+ "--max-decode-depth",
116
+ type=int,
117
+ default=1,
118
+ help="""Maximum depth for address decoder to descend into nested
119
+ addressable components. Default is 1.
120
+ """,
121
+ )
122
+
123
+ def do_export(self, top_node: "AddrmapNode", options: "argparse.Namespace") -> None:
124
+ cpuifs = self.get_cpuifs()
125
+
126
+ x = BusDecoderExporter()
127
+ x.export(
128
+ top_node,
129
+ options.output,
130
+ cpuif_cls=cpuifs[options.cpuif],
131
+ module_name=options.module_name,
132
+ package_name=options.package_name,
133
+ address_width=options.addr_width,
134
+ cpuif_unroll=options.unroll,
135
+ max_decode_depth=options.max_decode_depth,
136
+ )
@@ -0,0 +1,14 @@
1
+ from .body import Body, SupportsStr
2
+ from .combinational_body import CombinationalBody
3
+ from .for_loop_body import ForLoopBody
4
+ from .if_body import IfBody
5
+ from .struct_body import StructBody
6
+
7
+ __all__ = [
8
+ "Body",
9
+ "CombinationalBody",
10
+ "ForLoopBody",
11
+ "IfBody",
12
+ "StructBody",
13
+ "SupportsStr",
14
+ ]
@@ -0,0 +1,22 @@
1
+ from typing import Protocol
2
+
3
+ from typing_extensions import Self
4
+
5
+
6
+ class SupportsStr(Protocol):
7
+ def __str__(self) -> str: ...
8
+
9
+
10
+ class Body:
11
+ def __init__(self) -> None:
12
+ self.lines: list[SupportsStr] = []
13
+
14
+ def __str__(self) -> str:
15
+ return "\n".join(map(str, self.lines))
16
+
17
+ def __add__(self, other: SupportsStr) -> Self:
18
+ self.lines.append(other)
19
+ return self
20
+
21
+ def __bool__(self) -> bool:
22
+ return bool(self.lines)
@@ -0,0 +1,10 @@
1
+ from textwrap import indent
2
+
3
+ from .body import Body
4
+
5
+
6
+ class CombinationalBody(Body):
7
+ def __str__(self) -> str:
8
+ return f"""always_comb begin
9
+ {indent(super().__str__(), " ")}
10
+ end"""
@@ -0,0 +1,16 @@
1
+ from textwrap import indent
2
+
3
+ from .body import Body
4
+
5
+
6
+ class ForLoopBody(Body):
7
+ def __init__(self, type: str, iterator: str, dim: int) -> None:
8
+ super().__init__()
9
+ self._type = type
10
+ self._iterator = iterator
11
+ self._dim = dim
12
+
13
+ def __str__(self) -> str:
14
+ return f"""for ({self._type} {self._iterator} = 0; {self._iterator} < {self._dim}; {self._iterator}++) begin
15
+ {indent(super().__str__(), " ")}
16
+ end"""
@@ -0,0 +1,91 @@
1
+ from textwrap import indent
2
+ from types import EllipsisType
3
+
4
+ from typing_extensions import Self
5
+
6
+ from .body import Body, SupportsStr
7
+
8
+
9
+ class IfBody(Body):
10
+ def __init__(self) -> None:
11
+ super().__init__()
12
+ # (None means 'else')
13
+ self._branches: list[tuple[SupportsStr | None, Body]] = []
14
+ self._has_else = False
15
+
16
+ # --- Item access: if/else-if via condition; else via Ellipsis/None ---
17
+ def __getitem__(self, condition: SupportsStr | EllipsisType | None) -> Body:
18
+ if self._has_else:
19
+ raise RuntimeError("Cannot add branches after an 'else' branch.")
20
+ if condition is Ellipsis or condition is None:
21
+ if self._has_else:
22
+ raise RuntimeError("Only one 'else' branch is allowed.")
23
+ self._has_else = True
24
+ b = Body()
25
+ self._branches.append((None, b))
26
+ return b
27
+ # conditional branch
28
+ b = Body()
29
+ self._branches.append((condition, b))
30
+ return b
31
+
32
+ # --- In-place or: if/else-if via (cond, Body); else via Body ---
33
+ def __ior__(self, other: tuple[SupportsStr, Body] | Body) -> Self:
34
+ if isinstance(other, Body):
35
+ if self._has_else:
36
+ raise RuntimeError("Only one 'else' branch is allowed.")
37
+ if self._has_else or (self._branches and self._branches[-1][0] is None):
38
+ raise RuntimeError("Cannot add branches after an 'else' branch.")
39
+ self._branches.append((None, other))
40
+ self._has_else = True
41
+ return self
42
+
43
+ cond, body = other
44
+ if self._has_else:
45
+ raise RuntimeError("Cannot add branches after an 'else' branch.")
46
+ self._branches.append((cond, body))
47
+ return self
48
+
49
+ # --- Context manager for a branch ---
50
+ class _BranchCtx:
51
+ def __init__(self, outer: "IfBody", condition: SupportsStr | None) -> None:
52
+ self._outer = outer
53
+ # route through __getitem__ to reuse validation logic
54
+ self._body = outer[Ellipsis if condition is None else condition]
55
+
56
+ def __enter__(self) -> Body:
57
+ return self._body
58
+
59
+ def __exit__(
60
+ self,
61
+ exc_type: type[BaseException] | None,
62
+ exc: BaseException | None,
63
+ tb: object | None,
64
+ ) -> bool:
65
+ return False
66
+
67
+ def cm(self, condition: SupportsStr | None) -> "IfBody._BranchCtx":
68
+ """Use with: with ifb.cm('cond') as b: ... ; use None for else."""
69
+ return IfBody._BranchCtx(self, condition)
70
+
71
+ # --- Rendering ---
72
+ def __str__(self) -> str:
73
+ out: list[str] = []
74
+ for i, (cond, body) in enumerate(self._branches):
75
+ if i == 0 and cond is not None:
76
+ out.append(f"if ({cond}) begin")
77
+ elif cond is not None:
78
+ out.append(f"else if ({cond}) begin")
79
+ else:
80
+ out.append("else begin")
81
+ body_str = str(body)
82
+ if body_str:
83
+ out.extend(indent(ln, " ") for ln in body_str.splitlines())
84
+ out.append("end")
85
+ return "\n".join(out)
86
+
87
+ def __len__(self) -> int:
88
+ return len(self._branches)
89
+
90
+ def __bool__(self) -> bool:
91
+ return bool(self._branches)
@@ -0,0 +1,25 @@
1
+ from textwrap import indent
2
+
3
+ from .body import Body
4
+
5
+
6
+ class StructBody(Body):
7
+ def __init__(self, name: str, typedef: bool = False, packed: bool = False) -> None:
8
+ super().__init__()
9
+ self._name = name
10
+ self._typedef = typedef
11
+ self._packed = packed
12
+
13
+ @property
14
+ def name(self) -> str:
15
+ return self._name
16
+
17
+ def __str__(self) -> str:
18
+ if self._typedef:
19
+ return f"""typedef struct {"packed " if self._packed else ""}{{
20
+ {indent(super().__str__(), " ")}
21
+ }} {self._name};"""
22
+
23
+ return f"""struct {{
24
+ {indent(super().__str__(), " ")}
25
+ }} {self._name};"""
@@ -0,0 +1,3 @@
1
+ from .base_cpuif import BaseCpuif
2
+
3
+ __all__ = ["BaseCpuif"]
@@ -0,0 +1,4 @@
1
+ from .apb3_cpuif import APB3Cpuif
2
+ from .apb3_cpuif_flat import APB3CpuifFlat
3
+
4
+ __all__ = ["APB3Cpuif", "APB3CpuifFlat"]
@@ -0,0 +1,67 @@
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 .apb3_interface import APB3SVInterface
8
+
9
+ if TYPE_CHECKING:
10
+ from ...exporter import BusDecoderExporter
11
+
12
+
13
+ class APB3Cpuif(BaseCpuif):
14
+ template_path = "apb3_tmpl.sv"
15
+
16
+ def __init__(self, exp: "BusDecoderExporter") -> None:
17
+ super().__init__(exp)
18
+ self._interface = APB3SVInterface(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
+ @overload
29
+ def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
30
+ @overload
31
+ def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
32
+ def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
33
+ return self._interface.signal(signal, node, indexer)
34
+
35
+ def fanout(self, node: AddressableNode) -> str:
36
+ fanout: dict[str, str] = {}
37
+ fanout[self.signal("PSEL", node, "gi")] = (
38
+ 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')}"
39
+ )
40
+ fanout[self.signal("PENABLE", node, "gi")] = self.signal("PENABLE")
41
+ fanout[self.signal("PWRITE", node, "gi")] = (
42
+ f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
43
+ )
44
+ fanout[self.signal("PADDR", node, "gi")] = self.signal("PADDR")
45
+ fanout[self.signal("PWDATA", node, "gi")] = "cpuif_wr_data"
46
+
47
+ return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items()))
48
+
49
+ def fanin(self, node: AddressableNode | None = None) -> str:
50
+ fanin: dict[str, str] = {}
51
+ if node is None:
52
+ fanin["cpuif_rd_ack"] = "'0"
53
+ fanin["cpuif_rd_err"] = "'0"
54
+ else:
55
+ fanin["cpuif_rd_ack"] = self.signal("PREADY", node, "i")
56
+ fanin["cpuif_rd_err"] = self.signal("PSLVERR", node, "i")
57
+
58
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
59
+
60
+ def readback(self, node: AddressableNode | None = None) -> str:
61
+ fanin: dict[str, str] = {}
62
+ if node is None:
63
+ fanin["cpuif_rd_data"] = "'0"
64
+ else:
65
+ fanin["cpuif_rd_data"] = self.signal("PRDATA", node, "i")
66
+
67
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
@@ -0,0 +1,68 @@
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 .apb3_interface import APB3FlatInterface
8
+
9
+ if TYPE_CHECKING:
10
+ from ...exporter import BusDecoderExporter
11
+
12
+
13
+ class APB3CpuifFlat(BaseCpuif):
14
+ template_path = "apb3_tmpl.sv"
15
+
16
+ def __init__(self, exp: "BusDecoderExporter") -> None:
17
+ super().__init__(exp)
18
+ self._interface = APB3FlatInterface(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("PWDATA", node)] = "cpuif_wr_data"
47
+
48
+ return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items()))
49
+
50
+ def fanin(self, node: AddressableNode | None = None) -> str:
51
+ fanin: dict[str, str] = {}
52
+ if node is None:
53
+ fanin["cpuif_rd_ack"] = "'0"
54
+ fanin["cpuif_rd_err"] = "'0"
55
+ else:
56
+ fanin["cpuif_rd_ack"] = self.signal("PREADY", node)
57
+ fanin["cpuif_rd_err"] = self.signal("PSLVERR", node)
58
+
59
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
60
+
61
+ def readback(self, node: AddressableNode | None = None) -> str:
62
+ fanin: dict[str, str] = {}
63
+ if node is None:
64
+ fanin["cpuif_rd_data"] = "'0"
65
+ else:
66
+ fanin["cpuif_rd_data"] = self.signal("PRDATA", node)
67
+
68
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
@@ -0,0 +1,56 @@
1
+ """APB3-specific interface implementations."""
2
+
3
+ from systemrdl.node import AddressableNode
4
+
5
+ from ..interface import FlatInterface, SVInterface
6
+
7
+
8
+ class APB3SVInterface(SVInterface):
9
+ """APB3 SystemVerilog interface."""
10
+
11
+ def get_interface_type(self) -> str:
12
+ return "apb3_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 APB3FlatInterface(FlatInterface):
22
+ """APB3 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 [{self.cpuif.data_width - 1}:0] {slave_prefix}PWDATA",
39
+ f"output logic [{self.cpuif.data_width - 1}:0] {slave_prefix}PRDATA",
40
+ f"output logic {slave_prefix}PREADY",
41
+ f"output logic {slave_prefix}PSLVERR",
42
+ ]
43
+
44
+ def _get_master_port_declarations(self, child: AddressableNode, master_prefix: str) -> list[str]:
45
+ return [
46
+ f"output logic {self.signal('PCLK', child)}",
47
+ f"output logic {self.signal('PRESETn', child)}",
48
+ f"output logic {self.signal('PSEL', child)}",
49
+ f"output logic {self.signal('PENABLE', child)}",
50
+ f"output logic {self.signal('PWRITE', child)}",
51
+ f"output logic [{self.cpuif.addr_width - 1}:0] {self.signal('PADDR', child)}",
52
+ f"output logic [{self.cpuif.data_width - 1}:0] {self.signal('PWDATA', child)}",
53
+ f"input logic [{self.cpuif.data_width - 1}:0] {self.signal('PRDATA', child)}",
54
+ f"input logic {self.signal('PREADY', child)}",
55
+ f"input logic {self.signal('PSLVERR', child)}",
56
+ ]
@@ -0,0 +1,33 @@
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
+ `endif
10
+ {% endif -%}
11
+
12
+ assign cpuif_req = {{cpuif.signal("PSEL")}};
13
+ assign cpuif_wr_en = {{cpuif.signal("PWRITE")}};
14
+ assign cpuif_rd_en = !{{cpuif.signal("PWRITE")}};
15
+
16
+ assign cpuif_wr_addr = {{cpuif.signal("PADDR")}};
17
+ assign cpuif_rd_addr = {{cpuif.signal("PADDR")}};
18
+
19
+ assign cpuif_wr_data = {{cpuif.signal("PWDATA")}};
20
+
21
+ assign {{cpuif.signal("PRDATA")}} = cpuif_rd_data;
22
+ assign {{cpuif.signal("PREADY")}} = cpuif_rd_ack;
23
+ assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpuif_wr_sel.cpuif_err;
24
+
25
+ //--------------------------------------------------------------------------
26
+ // Fanout CPU Bus interface signals
27
+ //--------------------------------------------------------------------------
28
+ {{fanout|walk(cpuif=cpuif)}}
29
+
30
+ //--------------------------------------------------------------------------
31
+ // Fanin CPU Bus interface signals
32
+ //--------------------------------------------------------------------------
33
+ {{fanin|walk(cpuif=cpuif)}}
@@ -0,0 +1,4 @@
1
+ from .apb4_cpuif import APB4Cpuif
2
+ from .apb4_cpuif_flat import APB4CpuifFlat
3
+
4
+ __all__ = ["APB4Cpuif", "APB4CpuifFlat"]
@@ -0,0 +1,70 @@
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 .apb4_interface import APB4SVInterface
8
+
9
+ if TYPE_CHECKING:
10
+ from ...exporter import BusDecoderExporter
11
+
12
+
13
+ class APB4Cpuif(BaseCpuif):
14
+ template_path = "apb4_tmpl.sv"
15
+
16
+ def __init__(self, exp: "BusDecoderExporter") -> None:
17
+ super().__init__(exp)
18
+ self._interface = APB4SVInterface(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 APB4 interface."""
27
+ return self._interface.get_port_declaration("s_apb", "m_apb_")
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) -> 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
+ fanout[self.signal("PSEL", node, "gi")] = (
39
+ 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')}"
40
+ )
41
+ fanout[self.signal("PENABLE", node, "gi")] = self.signal("PENABLE")
42
+ fanout[self.signal("PWRITE", node, "gi")] = (
43
+ f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
44
+ )
45
+ fanout[self.signal("PADDR", node, "gi")] = self.signal("PADDR")
46
+ fanout[self.signal("PPROT", node, "gi")] = self.signal("PPROT")
47
+ fanout[self.signal("PWDATA", node, "gi")] = "cpuif_wr_data"
48
+ fanout[self.signal("PSTRB", node, "gi")] = "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, "i")
59
+ fanin["cpuif_rd_err"] = self.signal("PSLVERR", node, "i")
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, "i")
69
+
70
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))