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,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,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,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,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()))
|