More thinking about interrupt priorities
This commit is contained in:
parent
7946432d7a
commit
c73c09a48a
17096
doc/hazard3.pdf
17096
doc/hazard3.pdf
File diff suppressed because it is too large
Load Diff
|
@ -395,11 +395,7 @@ Address: `0x7b3`
|
|||
|
||||
Not implemented. Access will cause an illegal instruction exception.
|
||||
|
||||
=== Custom CSRs
|
||||
|
||||
These are all allocated in the space `0xbc0` through `0xbff` which is available for custom read/write M-mode CSRs, and `0xfc0` through `0xfff` which is available for custom read-only M-mode CSRs.
|
||||
|
||||
Hazard3 also allocates a custom _Debug Mode_ register <<reg-dmdata0>> in this space.
|
||||
=== Custom Debug Mode CSRs
|
||||
|
||||
[[reg-dmdata0]]
|
||||
==== dmdata0
|
||||
|
@ -414,6 +410,8 @@ The Debug Module uses this mapping to exchange data with the core by injecting `
|
|||
|
||||
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
|
||||
|
||||
|
@ -433,6 +431,13 @@ 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
|
||||
|
@ -443,7 +448,7 @@ External interrupt pending array. Contains a read-only bit for each external int
|
|||
|
||||
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, `meip0` is filtered by `meiea` to generate the standard `mip.meip` flag. So, an external interrupt is taken when _all_ of the following are true:
|
||||
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`
|
||||
|
@ -456,21 +461,58 @@ 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)
|
||||
|
||||
==== meipra
|
||||
[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: `0xbe3`
|
||||
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.preempt` are treated as though they are not pending.
|
||||
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"]
|
||||
|===
|
||||
|
@ -485,20 +527,29 @@ When multiple interrupts of the same priority are both pending and enabled, the
|
|||
[[reg-meicontext]]
|
||||
==== meicontext
|
||||
|
||||
Address: `0xbe4`
|
||||
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.
|
||||
|
||||
Generally this register is updated by hardware on an `meinext` access, and saved/restored by software when entering/exiting an interrupt dispatch routine.
|
||||
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: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 | noirq | Not in interrupt (read/write). Set to 1 at reset, and set to `meinext.noirq` when `meinext.update` is written.
|
||||
| 14:9 | - | RES0
|
||||
| 8:0 | irq | Current IRQ number (read/write). Set to `meinext.irq` when `meinext.update` is written..
|
||||
| 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`:
|
||||
|
@ -557,6 +608,8 @@ no_more_irqs:
|
|||
mret
|
||||
----
|
||||
|
||||
=== Custom Power Control CSRs
|
||||
|
||||
==== msleep
|
||||
|
||||
Address: `0xbf0`
|
||||
|
|
Loading…
Reference in New Issue