1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
"""CSB / TRANSCOM message-window seam (B3 landing point).
Evidence kind: **hardware register/field header + observed access pattern**
(once the firmware reaches the message loop). At the time of writing the
570.144 boot has not been observed polling the GSP message-queue CSB window,
so this module deliberately models **no register semantics by default**: a
window is only served when a profile declares it *and* lists the individual
registers with their widths, reset values and semantics (``rw``, ``w1c``,
``set``, ``clear``). Undeclared offsets stay unhandled and are logged.
The CSB/TRANSCOM window carries the host<->GSP message queues and doorbells
(head/tail pointers, interrupt set/clear); B3 requires the first such poll to
return the documented read-back, which will be added here together with the
observed access evidence and a test.
"""
from __future__ import annotations
from .base import Device
#: Per-register semantics accepted by a profile.
SEMANTICS = ("rw", "ro", "wo", "w1c", "set", "clear")
class CsbWindow(Device):
"""Profile-declared CSB/mailbox register window (empty by default)."""
name = "csb"
def __init__(self, *, base: int = 0, size: int = 0x1000, registers=None):
super().__init__(base=base, size=size)
# registers: {offset: {"width": 4, "value": int, "semantics": "rw"}}
self.registers: dict[int, dict] = {}
for off, spec in (registers or {}).items():
self.declare(int(off), spec)
self.reset()
def declare(self, offset: int, spec: dict) -> None:
semantics = spec.get("semantics", "rw")
if semantics not in SEMANTICS:
raise ValueError(f"unknown CSB semantics {semantics!r}")
width = int(spec.get("width", 4))
if width not in (1, 2, 4, 8):
raise ValueError(f"unsupported CSB register width {width}")
self.registers[offset] = {
"width": width,
"reset": int(spec.get("value", 0)),
"semantics": semantics,
}
def reset(self) -> None:
self.values = {off: spec["reset"] for off, spec in self.registers.items()}
def _spec(self, offset: int, size: int) -> dict | None:
spec = self.registers.get(offset)
if spec is None or spec["width"] != size:
return None
return spec
def read(self, offset: int, size: int) -> int | None:
spec = self._spec(offset, size)
if spec is None or spec["semantics"] == "wo":
return None
return self.values.get(offset, 0)
def write(self, offset: int, size: int, value: int) -> bool:
spec = self._spec(offset, size)
if spec is None or spec["semantics"] == "ro":
return False
mask = (1 << (8 * size)) - 1
value &= mask
sem = spec["semantics"]
if sem == "w1c":
self.values[offset] = self.values.get(offset, 0) & ~value & mask
elif sem == "set":
self.values[offset] = (self.values.get(offset, 0) | value) & mask
elif sem == "clear":
self.values[offset] = (self.values.get(offset, 0) & ~value) & mask
else:
self.values[offset] = value
return True
def state(self) -> dict:
return {"values": {str(k): v for k, v in sorted(self.values.items())},
"declared": sorted(self.registers)}
def load_state(self, data: dict) -> None:
values = {int(k): int(v) for k, v in data.get("values", {}).items()}
self.values = {off: values.get(off, spec["reset"])
for off, spec in self.registers.items()}
|