"""RISC-V local (PRISCV) register block. Evidence kind: **hardware register/field header + observed access pattern.** Reached through the PRIV aperture at ``PRIV + 0x111000`` and through the libos kernel's private mapping at ``0x3_0000_1000`` (the firmware reads the trace and error-status registers there during boot). Modelled registers: ================================ ====== ===== ============================== register offset access semantics ================================ ====== ===== ============================== ``RISCV_IRQMSET`` 0x2ac W ``irq_mask |= value`` ``RISCV_IRQMCLR`` 0x2b0 W ``irq_mask &= ~value`` ``RISCV_IRQMASK`` 0x2b4 R ``irq_mask`` ``RISCV_IRQDEST`` 0x2b8 RW storage ``RISCV_IRQTYPE`` 0x2bc RW storage ``RISCV_TRACECTL`` 0x344 RW storage ``RISCV_TRACE_RDIDX`` 0x348 RW storage ``RISCV_TRACE_WTIDX`` 0x34c RW storage ``RISCV_PRIV_ERR_STAT`` 0x360 RW storage ``RISCV_HUB_ERR_STAT`` 0x36c RW storage ================================ ====== ===== ============================== The IRQ mask semantics (set/clear/read) follow the header access types and the observed boot reads; core interrupt *delivery* (mtvec/mip) is not part of this device and remains unmodelled (documented unknown). """ from __future__ import annotations from .base import Device IRQMSET = 0x2AC IRQMCLR = 0x2B0 IRQMASK = 0x2B4 IRQDEST = 0x2B8 IRQTYPE = 0x2BC TRACECTL = 0x344 TRACE_RDIDX = 0x348 TRACE_WTIDX = 0x34C PRIV_ERR_STAT = 0x360 PRIV_ERR_INFO = 0x364 PRIV_ERR_ADDR = 0x368 HUB_ERR_STAT = 0x36C _RW = {IRQDEST, IRQTYPE, TRACECTL, TRACE_RDIDX, TRACE_WTIDX, PRIV_ERR_STAT, HUB_ERR_STAT} #: Read-only error capture registers (header R-I4R); the boot panic path #: reads them after a trap (observed reads at PC 0x40106BC). _RO = {PRIV_ERR_INFO, PRIV_ERR_ADDR} class PriscvBlock(Device): """RISC-V-local control/trace/error registers.""" name = "priscv" def __init__(self, *, base: int = 0, size: int = 0x1000): super().__init__(base=base, size=size) self.reset() def reset(self) -> None: self.irq_mask = 0 self.regs: dict[int, int] = {off: 0 for off in _RW} self.error: dict[int, int] = {off: 0 for off in _RO} def read(self, offset: int, size: int) -> int | None: if size != 4: return None if offset == IRQMASK: return self.irq_mask if offset in self.regs: return self.regs[offset] if offset in self.error: return self.error[offset] return None def write(self, offset: int, size: int, value: int) -> bool: if size != 4: return False value &= 0xFFFFFFFF if offset == IRQMSET: self.irq_mask = (self.irq_mask | value) & 0xFFFFFFFF return True if offset == IRQMCLR: self.irq_mask = (self.irq_mask & ~value) & 0xFFFFFFFF return True if offset == IRQMASK: return False # read-only if offset in self.regs: self.regs[offset] = value return True return False def peek(self, offset: int, size: int) -> int | None: if size != 4: return None if offset == IRQMASK: return self.irq_mask if offset in self.error: return self.error[offset] return self.regs.get(offset) def state(self) -> dict: return {"irq_mask": self.irq_mask, "regs": {str(k): v for k, v in sorted(self.regs.items())}, "error": {str(k): v for k, v in sorted(self.error.items())}} def load_state(self, data: dict) -> None: self.irq_mask = int(data.get("irq_mask", 0)) regs = {int(k): int(v) for k, v in data.get("regs", {}).items()} self.regs = {off: regs.get(off, 0) for off in _RW} err = {int(k): int(v) for k, v in data.get("error", {}).items()} self.error = {off: err.get(off, 0) for off in _RO}