684 lines
33 KiB
Plaintext
684 lines
33 KiB
Plaintext
== CSRs
|
|
|
|
The RISC-V privileged specification affords flexibility as to which CSRs are implemented, and how they behave. This section documents the concrete behaviour of Hazard3's standard and nonstandard M-mode CSRs, as implemented.
|
|
|
|
All CSRs are 32-bit; MXLEN is fixed at 32 bits on Hazard3. All CSR addresses not listed in this section are unimplemented. Accessing an unimplemented CSR will cause an illegal instruction exception (`mcause` = 2). This includes all U-mode and S-mode CSRs.
|
|
|
|
|
|
IMPORTANT: The https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf[RISC-V Privileged Specification] should be your primary reference for writing software to run on Hazard3. This section specifies those details which are left implementation-defined by the RISC-V Privileged Specification, for sake of completeness, but portable RISC-V software should not rely on these details.
|
|
|
|
=== Standard M-mode Identification CSRs
|
|
|
|
==== mvendorid
|
|
|
|
Address: `0xf11`
|
|
|
|
Vendor identifier. Read-only, configurable constant. Should contain either all-zeroes, or a valid JEDEC JEP106 vendor ID using the encoding in the RISC-V specification.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:7 | `bank` | The number of continuation codes in the vendor JEP106 ID. _One less than the JEP106 bank number._
|
|
| 6:0 | `offset` | Vendor ID within the specified bank. LSB (parity) is not stored.
|
|
|===
|
|
|
|
==== marchid
|
|
|
|
Address: `0xf12`
|
|
|
|
Architecture identifier for Hazard3. Read-only, constant.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31 | - | 0: Open-source implementation
|
|
| 30:0 | - | 0x1b (decimal 27): the https://github.com/riscv/riscv-isa-manual/blob/master/marchid.md[registered] architecture ID for Hazard3
|
|
|===
|
|
|
|
|
|
==== mimpid
|
|
|
|
Address: `0xf13`
|
|
|
|
Implementation identifier. Read-only, configurable constant.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:0 | - | Should contain the git hash of the Hazard3 revision from which the processor was synthesised, or all-zeroes.
|
|
|===
|
|
|
|
==== mhartid
|
|
|
|
Address: `0xf14`
|
|
|
|
Hart identification register. Read-only, configurable constant.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:0 | - | Hazard3 cores possess only one hardware thread, so this is a unique per-core identifier, assigned consecutively from 0.
|
|
|===
|
|
|
|
==== mconfigptr
|
|
|
|
Address: `0xf15`
|
|
|
|
Pointer to configuration data structure. Read-only, configurable constant.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:0 | - | Either pointer to configuration data structure, containing information about the harts and system, or all-zeroes. At least 4-byte-aligned.
|
|
|===
|
|
|
|
==== misa
|
|
|
|
Address: `0x301`
|
|
|
|
Read-only, constant. Value depends on which ISA extensions Hazard3 is configured with. The table below lists the fields which are _not_ always hardwired to 0:
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:30 | `mxl` | Always `0x1`. Indicates this is a 32-bit processor.
|
|
| 23 | `x` | 1 if the core is configured to support trap-handling, otherwise 0. Hazard3 has nonstandard CSRs to enable/disable external interrupts on a per-interrupt basis, see <<reg-meiea>> and <<reg-meipa>>. The `misa.x` bit must be set to indicate their presence. Hazard3 does not implement any custom instructions.
|
|
| 20 | `u` | 1 if User mode is supported, otherwise 0.
|
|
| 12 | `m` | 1 if the M extension is present, otherwise 0.
|
|
| 2 | `c` | 1 if the C extension is present, otherwise 0.
|
|
| 0 | `a` | 1 if the A extension is present, otherwise 0.
|
|
|===
|
|
|
|
=== Standard M-mode Trap Handling CSRs
|
|
|
|
==== mstatus
|
|
|
|
Address: `0x300`
|
|
|
|
The below table lists the fields which are _not_ hardwired to 0:
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 21 | `tw` | Timeout wait. Only present if U-mode is supported. When 1, attempting to execute a WFI instruction in U-mode will instantly cause an illegal instruction exception.
|
|
| 17 | `mprv` | Modify privilege. Only present if U-mode is supported. If 1, loads and stores behave as though the current privilege level were `mpp`. This includes physical memory protection checks, and the privilege level asserted on the system bus alongside the load/store address.
|
|
| 12:11 | `mpp` | Previous privilege level. If U-mode is supported, this register can store the values 3 (M-mode) or 0 (U-mode). Otherwise, only 3 (M-mode). If another value is written, hardware rounds to the nearest supported mode.
|
|
| 7 | `mpie` | Previous interrupt enable. Readable and writable. Is set to the current value of `mstatus.mie` on trap entry. Is set to 1 on trap return.
|
|
| 3 | `mie` | Interrupt enable. Readable and writable. Is set to 0 on trap entry. Is set to the current value of `mstatus.mpie` on trap return.
|
|
|===
|
|
|
|
==== mstatush
|
|
|
|
Address: `0x310`
|
|
|
|
Hardwired to 0.
|
|
|
|
|
|
==== medeleg
|
|
|
|
Address: `0x302`
|
|
|
|
Unimplemented, as only M-mode is supported. Access will cause an illegal instruction exception.
|
|
|
|
==== mideleg
|
|
|
|
Address: `0x303`
|
|
|
|
Unimplemented, as only M-mode is supported. Access will cause an illegal instruction exception.
|
|
|
|
==== mie
|
|
|
|
Address: `0x304`
|
|
|
|
Interrupt enable register. Not to be confused with `mstatus.mie`, which is a global enable, having the final say in whether any interrupt which is both enabled in `mie` and pending in `mip` will actually cause the processor to transfer control to a handler.
|
|
|
|
The table below lists the fields which are _not_ hardwired to 0:
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
|Bits | Name | Description
|
|
| 11 | `meie` | External interrupt enable. Hazard3 has internal custom CSRs to further filter external interrupts, see <<reg-meiea>>.
|
|
| 7 | `mtie` | Timer interrupt enable. A timer interrupt is requested when `mie.mtie`, `mip.mtip` and `mstatus.mie` are all 1.
|
|
| 3 | `msie` | Software interrupt enable. A software interupt is requested when `mie.msie`, `mip.mtip` and `mstatus.mie` are all 1.
|
|
|===
|
|
|
|
NOTE: RISC-V reserves bits 16+ of `mie`/`mip` for platform use, which Hazard3 could use for external interrupt control. On RV32I this could only control 16 external interrupts, so Hazard3 instead adds nonstandard interrupt enable registers starting at <<reg-meiea>>, and keeps the upper half of `mie` reserved.
|
|
|
|
==== mip
|
|
|
|
Address: `0x344`
|
|
|
|
Interrupt pending register. Read-only.
|
|
|
|
NOTE: The RISC-V specification lists `mip` as a read-write register, but the bits which are writable correspond to lower privilege modes (S- and U-mode) which are not implemented on Hazard3, so it is documented here as read-only.
|
|
|
|
The table below lists the fields which are _not_ hardwired to 0:
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
|Bits | Name | Description
|
|
| 11 | `meip` | External interrupt pending. When 1, indicates there is at least one interrupt which is asserted (hence pending in <<reg-meipa>>) and enabled in <<reg-meiea>>.
|
|
| 7 | `mtip` | Timer interrupt pending. Level-sensitive interrupt signal from outside the core. Connected to a standard, external RISC-V 64-bit timer.
|
|
| 3 | `msip` | Software interrupt pending. In spite of the name, this is not triggered by an instruction on this core, rather it is wired to an external memory-mapped register to provide a cross-hart level-sensitive doorbell interrupt.
|
|
|===
|
|
|
|
==== mtvec
|
|
|
|
Address: `0x305`
|
|
|
|
Trap vector base address. Read-write. Exactly which bits of `mtvec` can be modified (possibly none) is configurable when instantiating the processor, but by default the entire register is writable. The reset value of `mtvec` is also configurable.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
|Bits | Name | Description
|
|
| 31:2 | `base` | Base address for trap entry. In Vectored mode, this is _OR'd_ with the trap offset to calculate the trap entry address, so the table must be aligned to its total size, rounded up to a power of 2. In Direct mode, `base` is word-aligned.
|
|
| 0 | `mode` | 0 selects Direct mode -- all traps (whether exception or interrupt) jump to `base`. 1 selects Vectored mode -- exceptions go to `base`, interrupts go to `base \| mcause << 2`.
|
|
|===
|
|
|
|
NOTE: In the RISC-V specification, `mode` is a 2-bit write-any read-legal field in bits 1:0. Hazard3 implements this by hardwiring bit 1 to 0.
|
|
|
|
==== mscratch
|
|
|
|
Address: `0x340`
|
|
|
|
Read-write 32-bit register. No specific hardware function -- available for software to swap with a register when entering a trap handler.
|
|
|
|
==== mepc
|
|
|
|
Address: `0x341`
|
|
|
|
Exception program counter. When entering a trap, the current value of the program counter is recorded here. When executing an `mret`, the processor jumps to `mepc`. Can also be read and written by software.
|
|
|
|
On Hazard3, bits 31:2 of `mepc` are capable of holding all 30-bit values. Bit 1 is writable only if the C extension is implemented, and is otherwise hardwired to 0. Bit 0 is hardwired to 0, as per the specification.
|
|
|
|
All traps on Hazard3 are precise. For example, a load/store bus error will set `mepc` to the exact address of the load/store instruction which encountered the fault.
|
|
|
|
==== mcause
|
|
|
|
Address: `0x342`
|
|
|
|
Exception cause. Set when entering a trap to indicate the reason for the trap. Readable and writable by software.
|
|
|
|
NOTE: On Hazard3, most bits of `mcause` are hardwired to 0. Only bit 31, and enough least-significant bits to index all exception and all interrupt causes (at least four bits), are backed by registers. Only these bits are writable; the RISC-V specification only requires that `mcause` be able to hold all legal cause values.
|
|
|
|
The most significant bit of `mcause` is set to 1 to indicate an interrupt cause, and 0 to indicate an exception cause. The following interrupt causes may be set by Hazard3 hardware:
|
|
|
|
[cols="10h,~", options="header"]
|
|
|===
|
|
| Cause | Description
|
|
| 3 | Software interrupt (`mip.msip`)
|
|
| 7 | Timer interrupt (`mip.mtip`)
|
|
| 11 | External interrupt (`mip.meip`)
|
|
|===
|
|
|
|
The following exception causes may be set by Hazard3 hardware:
|
|
|
|
[cols="10h,~", options="header"]
|
|
|===
|
|
| Cause | Description
|
|
| 1 | Instruction access fault
|
|
| 2 | Illegal instruction
|
|
| 3 | Breakpoint
|
|
| 4 | Load address misaligned
|
|
| 5 | Load access fault
|
|
| 6 | Store/AMO address misaligned
|
|
| 7 | Store/AMO access fault
|
|
| 11 | Environment call
|
|
|===
|
|
|
|
==== mtval
|
|
|
|
Address: `0x343`
|
|
|
|
Hardwired to 0.
|
|
|
|
==== mcounteren
|
|
|
|
Address: `0x306`
|
|
|
|
Counter enable. Control access to counters from U-mode. Not to be confused with <<reg-mcountinhibit>>.
|
|
|
|
This register only exists if U-mode is supported.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
|Bits | Name | Description
|
|
| 31:3 | - | RES0
|
|
| 2 | `ir` | If 1, U-mode is permitted to access the `instret`/`instreth` instruction retire counter CSRs. Otherwise, U-mode accesses to these CSRs will trap.
|
|
| 1 | `tm` | No hardware effect, as the `time`/`timeh` CSRs are not implemented. However, this field still exists, as M-mode software can use it to track whether it should emulate U-mode attempts to access those CSRs.
|
|
| 0 | `cy` |If 1, U-mode is permitted to access the `cycle`/`cycleh` cycle counter CSRs. Otherwise, U-mode accesses to these CSRs will trap.
|
|
|===
|
|
|
|
=== Standard Memory Protection CSRs
|
|
|
|
==== pmpcfg0...3
|
|
|
|
Address: `0x3a0` through `0x3a3`
|
|
|
|
Configuration registers for up to 16 physical memory protection regions. Only present if PMP support is configured. If so, all 4 registers are present, but some registers may be partially/completely hardwired depending on the number of PMP regions present.
|
|
|
|
By default, M-mode has full permissions (RWX) on all of memory, and U-mode has no permissions. A PMP region can be configured to alter this default within some range of addresses. For every memory location executed, loaded or stored, the processor looks up the _lowest active region_ that overlaps that memory location, and applies its permissions to determine whether this access is allowed. The full description can be found in the RISC-V privileged ISA manual.
|
|
|
|
Each `pmpcfg` register divides into four identical 8-bit chunks, each corresponding to one region, and laid out as below:
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
|Bits | Name | Description
|
|
| 7 | `L` | Lock region, and additionally enforce its permissions on M-mode as well as U-mode.
|
|
| 6:5 | - | RES0
|
|
| 4:3 | `A` | Address-matching mode. Values supported are 0 (OFF), 2 (NA4, naturally aligned 4-byte) and 3 (NAPOT, naturally aligned power-of-two). Attempting to write an unsupported value will set the region to OFF.
|
|
| 2 | `X` | Execute permission
|
|
| 1 | `W` | Write permission
|
|
| 0 | `R` | Read permission
|
|
|===
|
|
|
|
==== pmpaddr0...15
|
|
|
|
Address: `0x3b0` through `0x3bf`
|
|
|
|
Address registers for up to 16 physical memory protection regions. Only present if PMP support is configured. If so, all 16 registers are present, but some may fully/partially hardwired.
|
|
|
|
`pmpaddr` registers express addresses in units of 4 bytes, so on Hazard3 (a 32-bit processor with no virtual address support) only the lower 30 bits of each address register are implemented.
|
|
|
|
The interpretation of the `pmpaddr` bits depends on the `A` mode configured in the corresponding `pmpcfg` register field:
|
|
|
|
* For NA4, the entire 30-bit `pmpaddr` field is matched against the 30 most-significant bits of the checked address.
|
|
* FOr NAPOT, `pmpaddr` bits up to and including the least-significant zero bits are ignored, and only the remaining bits are matched against the checked address.
|
|
|
|
=== Standard M-mode Performance Counters
|
|
|
|
==== mcycle
|
|
|
|
Address: `0xb00`
|
|
|
|
Lower half of the 64-bit cycle counter. Readable and writable by software. Increments every cycle, unless `mcountinhibit.cy` is 1, or the processor is in Debug Mode (as <<reg-dcsr>>.`stopcount` is hardwired to 1).
|
|
|
|
If written with a value `n` and read on the very next cycle, the value read will be exactly `n`. The RISC-V spec says this about `mcycle`: "Any CSR write takes effect after the writing instruction has otherwise completed."
|
|
|
|
==== mcycleh
|
|
|
|
Address: `0xb80`
|
|
|
|
Upper half of the 64-bit cycle counter. Readable and writable by software. Increments on cycles where `mcycle` has the value `0xffffffff`, unless `mcountinhibit.cy` is 1, or the processor is in Debug Mode.
|
|
|
|
This includes when `mcycle` is written on that same cycle, since RISC-V specifies the CSR write takes place _after_ the increment for that cycle.
|
|
|
|
==== minstret
|
|
|
|
Address: `0xb02`
|
|
|
|
Lower half of the 64-bit instruction retire counter. Readable and writable by software. Increments with every instruction executed, unless `mcountinhibit.ir` is 1, or the processor is in Debug Mode (as <<reg-dcsr>>.`stopcount` is hardwired to 1).
|
|
|
|
If some value `n` is written to `minstret`, and it is read back by the very next instruction, the value read will be exactly `n`. This is because the CSR write logically takes place after the instruction has otherwise completed.
|
|
|
|
==== minstreth
|
|
|
|
Address: `0xb82`
|
|
|
|
Upper half of the 64-bit instruction retire counter. Readable and writable by software. Increments when the core retires an instruction and the value of `minstret` is `0xffffffff`, unless `mcountinhibit.ir` is 1, or the processor is in Debug Mode.
|
|
|
|
==== mhpmcounter3...31
|
|
|
|
Address: `0xb03` through `0xb1f`
|
|
|
|
Hardwired to 0.
|
|
|
|
==== mhpmcounter3...31h
|
|
|
|
Address: `0xb83` through `0xb9f`
|
|
|
|
Hardwired to 0.
|
|
|
|
|
|
[[reg-mcountinhibit]]
|
|
==== mcountinhibit
|
|
|
|
Address: `0x320`
|
|
|
|
Counter inhibit. Read-write. The table below lists the fields which are _not_ hardwired to 0:
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 2 | `ir` | When 1, inhibit counting of `minstret`/`minstreth`. Resets to 1.
|
|
| 0 | `cy` | When 1, inhibit counting of `mcycle`/`mcycleh`. Resets to 1.
|
|
|===
|
|
|
|
==== mhpmevent3...31
|
|
|
|
Address: `0x323` through `0x33f`
|
|
|
|
Hardwired to 0.
|
|
|
|
=== Standard Trigger CSRs
|
|
|
|
==== tselect
|
|
|
|
Address: `0x7a0`
|
|
|
|
Unimplemented. Reads as 0, write causes illegal instruction exception.
|
|
|
|
==== tdata1...3
|
|
|
|
Address: `0x7a1` through `0x7a3`
|
|
|
|
Unimplemented. Access will cause an illegal instruction exception.
|
|
|
|
[[debug-csr-section]]
|
|
=== Standard Debug Mode CSRs
|
|
|
|
This section describes the Debug Mode CSRs, which follow the 0.13.2 RISC-V debug specification. The <<debug-chapter>> section gives more detail on the remainder of Hazard3's debug implementation, including the Debug Module.
|
|
|
|
All Debug Mode CSRs are 32-bit; DXLEN is always 32.
|
|
|
|
[[reg-dcsr]]
|
|
==== dcsr
|
|
|
|
Address: `0x7b0`
|
|
|
|
Debug control and status register. Access outside of Debug Mode will cause an illegal instruction exception. Relevant fields are implemented as follows:
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:28 | `xdebugver` | Hardwired to 4: external debug support as per RISC-V 0.13.2 debug specification.
|
|
| 15 | `ebreakm` | When 1, `ebreak` instructions executed in M-mode will break to Debug Mode instead of trapping
|
|
| 12 | `ebreaku` | When 1, `ebreak` instructions executed in U-mode will break to Debug Mode instead of trapping. Hardwired to 0 if U-mode is not supported.
|
|
| 11 | `stepie` | Hardwired to 0: no interrupts are taken during hardware single-stepping.
|
|
| 10 | `stopcount` | Hardwired to 1: `mcycle`/`mcycleh` and `minstret`/`minstreth` do not increment in Debug Mode.
|
|
| 9 | `stoptime` | Hardwired to 1: core-local timers don't increment in debug mode. This requires cooperation of external hardware based on the halt status to implement correctly.
|
|
| 8:6 | `cause` | Read-only, set by hardware -- see table below.
|
|
| 2 | `step` | When 1, re-enter Debug Mode after each instruction executed in M-mode.
|
|
| 1:0 | `prv` | Hardwired to 3, as only M-mode is implemented.
|
|
|===
|
|
|
|
Fields not mentioned above are hardwired to 0.
|
|
|
|
Hazard3 may set the following `dcsr.cause` values:
|
|
|
|
[cols="10h,~", options="header"]
|
|
|===
|
|
| Cause | Description
|
|
| 1 | Processor entered Debug Mode due to an `ebreak` instruction executed in M-mode.
|
|
| 3 | Processor entered Debug Mode due to a halt request, or a reset-halt request present when the core reset was released.
|
|
| 4 | Processor entered Debug Mode after executing one instruction with single-stepping enabled.
|
|
|===
|
|
|
|
Cause 5 (`resethaltreq`) is never set by hardware. This event is reported as a normal halt, cause 3. Cause 2 (trigger) is never used because there are no triggers. (TODO?)
|
|
|
|
==== dpc
|
|
|
|
Address: `0x7b1`
|
|
|
|
Debug program counter. When entering Debug Mode, `dpc` samples the current program counter, e.g. the address of an `ebreak` which caused Debug Mode entry. When leaving debug mode, the processor jumps to `dpc`. The host may read/write this register whilst in Debug Mode.
|
|
|
|
==== dscratch0
|
|
|
|
Address: `0x7b2`
|
|
|
|
Not implemented. Access will cause an illegal instruction exception.
|
|
|
|
To provide data exchange between the Debug Module and the core, the Debug Module's `data0` register is mapped into the core's CSR space at a read/write M-custom address -- see <<reg-dmdata0>>.
|
|
|
|
==== dscratch1
|
|
|
|
Address: `0x7b3`
|
|
|
|
Not implemented. Access will cause an illegal instruction exception.
|
|
|
|
=== Custom Debug Mode CSRs
|
|
|
|
[[reg-dmdata0]]
|
|
==== dmdata0
|
|
|
|
Address: `0xbff`
|
|
|
|
The Debug Module's internal `data0` register is mapped to this CSR address when the core is in debug mode. At any other time, access to this CSR address will cause an illegal instruction exception.
|
|
|
|
NOTE: The 0.13.2 debug specification allows for the Debug Module's abstract data registers to be mapped into the core's CSR address space, but there is no Debug-custom space, so the read/write M-custom space is used instead to avoid conflict with future versions of the debug specification.
|
|
|
|
The Debug Module uses this mapping to exchange data with the core by injecting `csrr`/`csrw` instructions into the prefetch buffer. This in turn is used to implement the Abstract Access Register command. See <<debug-chapter>>.
|
|
|
|
This CSR address is given by the `dataaddress` field of the Debug Module's `hartinfo` register, and `hartinfo.dataaccess` is set to 0 to indicate this is a CSR mapping, not a memory mapping.
|
|
|
|
=== Custom Interrupt Handling CSRs
|
|
|
|
[[reg-meiea]]
|
|
==== meiea
|
|
|
|
Address: `0xbe0`
|
|
|
|
External interrupt enable array. Contains a read-write bit for each external interrupt request: a `1` bit indicates that interrupt is currently enabled. At reset, all external interrupts are disabled.
|
|
|
|
If enabled, an external interrupt can cause assertion of the standard RISC-V machine external interrupt pending flag (`mip.meip`), and therefore cause the processor to enter the external interrupt vector. See <<reg-meipa>>.
|
|
|
|
There are up to 512 external interrupts. The upper half of this register contains a 16-bit window into the full 512-bit vector. The window is indexed by the 5 LSBs of the write data. For example:
|
|
|
|
----
|
|
csrrs a0, meiea, a0 // Read IRQ enables from the window selected by a0
|
|
csrw meiea, a0 // Write a0[31:16] to the window selected by a0[4:0]
|
|
csrr a0, meiea // Read from window 0 (edge case)
|
|
----
|
|
|
|
The purpose of this scheme is to allow software to _index_ an array of interrupt enables (something not usually possible in the CSR space) without introducing a stateful CSR index register which may have to be saved/restored around IRQs.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:16 | `window` | 16-bit read/write window into the external interrupt enable array
|
|
| 15:5 | - | RES0
|
|
| 4:0 | `index` | Write-only self-clearing field (no value is stored) used to control which window of the array appears in `window`.
|
|
|===
|
|
|
|
[[reg-meipa]]
|
|
==== meipa
|
|
|
|
Address: `0xbe1`
|
|
|
|
External interrupt pending array. Contains a read-only bit for each external interrupt request. Similarly to `meiea`, this register is a window into an array of up to 512 external interrupt flags. The status appears in the upper 16 bits of the value read from `meipa`, and the lower 5 bits of the value _written_ by the same CSR instruction (or 0 if no write takes place) select a 16-bit window of the full interrupt pending array.
|
|
|
|
A `1` bit indicates that interrupt is currently asserted. IRQs are assumed to be level-sensitive, and the relevant `meipa` bit is cleared by servicing the requestor so that it deasserts its interrupt request.
|
|
|
|
When any interrupt of sufficient priority is both set in `meipa` and enabled in `meiea`, the standard RISC-V external interrupt pending bit `mip.meip` is asserted. In other words, `meipa` is filtered by `meiea` to generate the standard `mip.meip` flag. So, an external interrupt is taken when _all_ of the following are true:
|
|
|
|
* An interrupt is currently asserted in `meipa`
|
|
* The matching interrupt enable bit is set in `meiea`
|
|
* The interrupt priority is greater than or equal to the preemption priority in `meicontext`
|
|
* The standard M-mode interrupt enable `mstatus.mie` is set
|
|
* The standard M-mode global external interrupt enable `mie.meie` is set
|
|
|
|
In this case, the processor jumps to either:
|
|
|
|
* `mtvec` directly, if vectoring is disabled (`mtvec[0]` is 0)
|
|
* `mtvec + 0x2c`, if vectoring is enabled (`mtvec[0]` is 1)
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:16 | `window` | 16-bit read-only window into the external interrupt pending array
|
|
| 15:5 | - | RES0
|
|
| 4:0 | `index` | Write-only, self-clearing field (no value is stored) used to control which window of the array appears in `window`.
|
|
|===
|
|
|
|
[[reg-meifa]]
|
|
==== meifa
|
|
|
|
Address: `0xbe2`
|
|
|
|
External interrupt force array. Contains a read-write bit for every interrupt request. Writing a 1 to a bit in the interrupt force array causes the corresponding bit to become pending in `meipa`. Software can use this feature to manually trigger a particular interrupt.
|
|
|
|
There are no restrictions on using `meifa` inside of an interrupt. The more useful case here is to schedule some lower-priority handler from within a high-priority interrupt, so that it will execute before the core returns to the foreground code. Implementers may wish to reserve some external IRQs with their external inputs tied to 0 for this purpose.
|
|
|
|
Bits can be cleared by software, and are cleared automatically by hardware upon a read of `meinext` which returns the corresponding IRQ number in `meinext.irq` (no matter whether `meinext.update` is written).
|
|
|
|
`meifa` implements the same array window indexing scheme as `meiea` and `meipa`.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:16 | `window` | 16-bit read/write window into the external interrupt force array
|
|
| 15:5 | - | RES0
|
|
| 4:0 | `index` | Write-only, self-clearing field (no value is stored) used to control which window of the array appears in `window`.
|
|
|===
|
|
|
|
==== meipra
|
|
|
|
Address: `0xbe3`
|
|
|
|
External interrupt priority array. Each interrupt has an (up to) 4-bit priority value associated with it, and each access to this register reads and/or writes a 16-bit window containing four such priority values. When less than 16 priority levels are available, the LSBs of the priority fields are hardwired to 0.
|
|
|
|
When an interrupt's priority is lower than the current preemption priority `meicontext.preempt`, it is treated as not being pending. The pending bit in `meipa` will still assert, but the machine external interrupt pending bit `mip.meip` will not, so the processor will ignore this interrupt. See <<reg-meicontext>>.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:16 | `window` | 16-bit read/write window into the external interrupt priority array, containing four 4-bit priority values.
|
|
| 15:7 | - | RES0
|
|
| 6:0 | `index` | Write-only, self-clearing field (no value is stored) used to control which window of the array appears in `window`.
|
|
|===
|
|
|
|
==== meinext
|
|
|
|
Address: `0xbe4`
|
|
|
|
Get next interrupt. Contains the index of the highest-priority external interrupt which is both asserted in `meipa` and enabled in `meiea`, left-shifted by 2 so that it can be used to index an array of 32-bit function pointers. If there is no such interrupt, the MSB is set.
|
|
|
|
When multiple interrupts of the same priority are both pending and enabled, the lowest-numbered wins. Interrupts with priority less than `meicontext.ppreempt` -- the _previous_ preemption priority -- are treated as though they are not pending. This is to ensure that a preempting interrupt frame does not service interrupts which may be in progress in the frame that was preempted.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31 | `noirq` | Set when there is no external interrupt which is enabled, pending, and has sufficient priority. Can be efficiently tested with a `bltz` or `bgez` instruction.
|
|
| 30:11 | - | RES0
|
|
| 10:2 | `irq` | Index of the highest-priority active external interrupt. Zero when no external interrupts with sufficient priority are both pending and enabled.
|
|
| 1 | - | RES0
|
|
| 0 | `update` | Writing 1 (self-clearing) causes hardware to update `meicontext` according to the IRQ number and preemption priority of the interrupt indicated in `noirq`/`irq`. This should be done in a single atomic operation, i.e. `csrrsi a0, meinext, 0x1`.
|
|
|===
|
|
|
|
[[reg-meicontext]]
|
|
==== meicontext
|
|
|
|
Address: `0xbe5`
|
|
|
|
External interrupt context register. Configures the priority level for interrupt preemption, and helps software track which interrupt it is currently in. The latter is useful when a common interrupt service routine handles interrupt requests from multiple instances of the same peripheral.
|
|
|
|
A three-level stack of preemption priorities is maintained in the `preempt`, `ppreempt` and `pppreempt` fields. The priority stack is saved when hardware enters the external interrupt vector, and restored by an `mret` instruction if `meicontext.mreteirq` is set.
|
|
|
|
The top entry of the priority stack, `preempt`, is used by hardware to ensure that only higher-priority interrupts can preempt the current interrupt. The next entry, `ppreempt`, is used to avoid servicing interrupts which may already be in progress in a frame that was preempted. The third entry, `pppreempt`, has no hardware effect, but ensures that `preempt` and `ppreempt` can be correctly saved/restored across arbitary levels of preemption.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:28 | `pppreempt` | Previous `ppreempt`. Set to `ppreempt` on priority save, set to zero on priority restore. Has no hardware effect, but ensures that when `meicontext` is saved/restored correctly, `preempt` and `ppreempt` stack correctly through arbitrarily many preemption frames.
|
|
| 27:24 | `ppreempt` | Previous `preempt`. Set to `preempt` on priority save, restored to to `pppreempt` on priority restore.
|
|
|
|
IRQs of lower priority than `ppreempt` are not visible in `meinext`, so that a preemptee is not re-taken in the preempting frame.
|
|
| 23:21 | - | RES0
|
|
| 20:16 | `preempt` | Minimum interrupt priority to preempt the current interrupt. Interrupts with lower priority than `preempt` do not cause the core to transfer to an interrupt handler. Updated by hardware when when `meinext.update` is written, or when hardware enters the external interrupt vector.
|
|
|
|
If an interrupt is present in `meinext`, then `preempt` is set to one level greater than that interrupt's priority. Otherwise, `ppreempt` is set to one level greater than the maximum interrupt priority, disabling preemption.
|
|
| 15 | `noirq` | Not in interrupt (read/write). Set to 1 at reset. Set to `meinext.noirq` when `meinext.update` is written. No hardware effect.
|
|
| 14:13 | - | RES0
|
|
| 12:4 | `irq` | Current IRQ number (read/write). Set to `meinext.irq` when `meinext.update` is written.
|
|
| 3 | `mtiesave` | Reads as the current value of `mie.mtie`, if `clearts` is set. Otherwise reads as 0. Writes are ORed into `mie.mtie`.
|
|
| 2 | `msiesave` | Reads as the current value of `mie.msie`, if `clearts` is set. Otherwise reads as 0. Writes are ORed into `mie.msie`.
|
|
| 1 | `clearts` | Write-1 self-clearing field. Writing 1 will clear `mie.mtie` and `mie.msie`, and present their prior values in the `mtiesave` and `msiesave` of this register. This makes it safe to re-enable IRQs (via `mstatus.mie`) without the possibility of being preempted by the standard timer and soft interrupt handlers, which may not be aware of Hazard3's interrupt hardware.
|
|
|
|
The clear due to `clearts` takes precedence over the set due to `mtiesave`/`msiesave`, although it would be unusual for software to write both on the same cycle.
|
|
| 0 | `mreteirq` | Enable restore of the preemption priority stack on `mret`. This bit is set on entering the external interrupt vector, cleared by `mret`, and cleared upon taking any trap other than an external interrupt.
|
|
|
|
Provided `meicontext` is saved on entry to the external interrupt vector (before enabling preemption), is restored before exiting, and the standard software/timer IRQs are prevented from preempting (e.g. by using `clearts`), this flag allows the hardware to safely manage the preemption priority stack even when an external interrupt handler may take exceptions.
|
|
|===
|
|
|
|
The following is an example of an external interrupt vector (`mip.meip`) which implements nested, prioritised interrupt dispatch using `meicontext` and `meinext`:
|
|
|
|
----
|
|
isr_external_irq:
|
|
// Save caller saves and exception return state whilst IRQs are disabled.
|
|
// We can't be pre-empted during this time, but if a higher-priority IRQ
|
|
// arrives ("late arrival"), that will be the one displayed in meinext.
|
|
addi sp, sp, -80
|
|
sw ra, 0(sp)
|
|
... snip
|
|
sw t6, 60(sp)
|
|
|
|
csrr a0, mepc
|
|
sw a0, 64(sp)
|
|
csrr a0, meicontext
|
|
sw a0, 68(sp)
|
|
csrr a0, mstatus
|
|
sw a0, 72(sp)
|
|
|
|
j get_next_irq
|
|
|
|
dispatch_irq:
|
|
// Preemption priority was configured by meinext update, so enable preemption:
|
|
csrsi mstatus, 0x8
|
|
// meinext is pre-shifted by 2, so only an add is required to index table
|
|
la a1, _external_irq_table
|
|
add a1, a1, a0
|
|
jalr ra, a1
|
|
|
|
// Disable IRQs on returning so we can sample the next IRQ
|
|
csrci mstatus, 0x8
|
|
|
|
get_next_irq:
|
|
// Sample the current highest-priority active IRQ (left-shifted by 2) from
|
|
// meinext, and write 1 to the LSB to tell hardware to tell hw to update
|
|
// meicontext with the preemption priority (and IRQ number) of this IRQ
|
|
csrrsi a0, meinext, 0x1
|
|
// MSB will be set if there is no active IRQ at the current priority level
|
|
bgez a0, dispatch_irq
|
|
|
|
no_more_irqs:
|
|
// Restore saved context and return from handler
|
|
lw a0, 64(sp)
|
|
csrw mepc, a0
|
|
lw a0, 68(sp)
|
|
csrw meicontext, a0
|
|
lw a0, 72(sp)
|
|
csrw mstatus, a0
|
|
|
|
lw ra, 0(sp)
|
|
... snip
|
|
lw t6, 60(sp)
|
|
addi sp, sp, 80
|
|
mret
|
|
----
|
|
|
|
=== Custom Power Control CSRs
|
|
|
|
==== msleep
|
|
|
|
Address: `0xbf0`
|
|
|
|
M-mode sleep control register.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:3 | - | RES0
|
|
| 2 | `deepsleep` | Deassert the clock request signal when entering the block or WFI state, and wait for clock acknowledge to reassert before leaving the block or WFI state.
|
|
| 1 | `block` | Write 1 to enter a WFI sleep state until either an unblock signal is received, or an interrupt is asserted that would cause a WFI to exit. Clears automatically when leaving the blocked state.
|
|
|
|
If an unblock signal has been received since the last time `block` was written to 1, the write is ignored. In other words, the blocked state falls through immediately in this case.
|
|
|
|
An unblock signal is received when another hart writes 1 to its `unblock` register, or for some other platform-specified reason.
|
|
| 0 | `unblock` | Write 1 to post an unblock signal to other harts in the system. Always reads back as 0.
|
|
|===
|
|
|
|
|
|
==== sleep
|
|
|
|
Address: `0x8f0`
|
|
|
|
U-mode sleep control register. A subset of the fields in `msleep`. If `mstatus.tw` is 1, then attempting to access this register from U-mode causes an illegal opcode trap.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:2 | - | RES0
|
|
| 1 | `block` | U-mode access to the `msleep.block` bit
|
|
| 0 | `unblock` | U-mode access to the `msleep.unblock` bit
|
|
|===
|