646 lines
28 KiB
Plaintext
646 lines
28 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-meie>> and <<reg-meip>>. The `misa.x` bit must be set to indicate their presence. Hazard3 does not implement any custom instructions.
|
|
| 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
|
|
| 12:11 | `mpp` | Previous privilege level. Always `0x3`, indicating M-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-meie>>.
|
|
| 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-meie>>, 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-meip>>) and enabled in <<reg-meie>>.
|
|
| 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.
|
|
|===
|
|
|
|
NOTE: Hazard3 assumes interrupts to be level-sensitive at system level. Bits in `mip` are cleared by servicing the requestor and causing it to deassert its interrupt request.
|
|
|
|
==== 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:1 of `mepc` are capable of holding all 31-bit values. 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
|
|
|===
|
|
|
|
NOTE: Not every instruction fetch bus cycle which returns a bus error leads to an exception. Hazard3 prefetches instructions ahead of execution, and associated bus errors are speculated through to the point the processor actually attempts to decode the instruction. Until this point, the error can be flushed by a branch, with no ill effect.
|
|
|
|
==== mtval
|
|
|
|
Address: `0x343`
|
|
|
|
Hardwired to 0.
|
|
|
|
==== mcounteren
|
|
|
|
Address: `0x306`
|
|
|
|
Unimplemented, as only M-mode is supported. Access will cause an illegal instruction exception.
|
|
|
|
Not to be confused with <<reg-mcountinhibit>>.
|
|
|
|
=== Standard Memory Protection
|
|
|
|
==== pmpcfg0...3
|
|
|
|
Address: `0x3a0` through `0x3a3`
|
|
|
|
Unimplemented. Access will cause an illegal instruction exception.
|
|
|
|
==== pmpaddr0...15
|
|
|
|
Address: `0x3b0` through `0x3bf`
|
|
|
|
Unimplemented. Access will cause an illegal instruction exception.
|
|
|
|
=== 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 will break to Debug Mode instead of trapping in M mode.
|
|
| 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-meie]]
|
|
==== 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-meip>>.
|
|
|
|
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-meip]]
|
|
==== 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` with 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 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` correctly track the current interrupt frame across arbitrary levels of nested interrupts, so long as software saves/restores the `meicontext` register appropriately.
|
|
|
|
[cols="10h,20h,~", options="header"]
|
|
|===
|
|
| Bits | Name | Description
|
|
| 31:28 | pppreempt | Previous `ppreempt`. Set to `ppreempt` when taking an interrupt, set to zero by `mret`. Has no hardware effect, but ensures that when `meicontext` is saved/restored correctly, `preempt` and `ppreempt` stack correctly through arbitrarily many preemption frames.
|
|
|
|
TODO: how to track whether the `mret` is a return from external IRQ?
|
|
| 27:24 | ppreempt | Previous `preempt`. Set to `preempt` when taking an interrupt, restored to to `pppreempt` by `mret`. IRQs of lower priority than `preemptp` are not visible in `meinext`, so that the preempted interrupt is not re-taken in the preempting frame.
|
|
|
|
One bit smaller than `preempt`, because when `preempt` has its maximum value of 16, no further preemption is possible.
|
|
| 23:21 | - | RES0
|
|
| 20:16 | preempt | Minimum interrupt priority to preempt the current interrupt. Set to the priority of `meinext.irq`, plus one, when `meinext.update` is written. Interrupts with lower priority than `preempt` do not cause the core to transfer to an interrupt handler.
|
|
| 15:12 | - | RES0
|
|
| 11 | noirq | Not in interrupt (read/write). Set to 1 at reset or when taking any trap other than an external interrupt. Set to `meinext.noirq` when `meinext.update` is written.
|
|
| 10:9 | - | RES0
|
|
| 8:0 | irq | Current IRQ number (read/write). Set to `meinext.irq` when `meinext.update` is written..
|
|
|===
|
|
|
|
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
|
|
|===
|