diff options
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/sim/devices/priscv.py')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/sim/devices/priscv.py | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/sim/devices/priscv.py b/debug/risc-v-gsp-rm-vm/sim/devices/priscv.py new file mode 100644 index 0000000..62a8fea --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/sim/devices/priscv.py @@ -0,0 +1,113 @@ +"""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} |
