"""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()}