summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/sim/devices/priscv.py
blob: 62a8feadbb6514bf73b25ad2cd88a342148b60bb (plain)
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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}