neorv32/docs/datasheet/soc_xirq.adoc

79 lines
4.4 KiB
Plaintext

<<<
:sectnums:
==== External Interrupt Controller (XIRQ)
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_xirq.vhd |
| Software driver file(s): | neorv32_xirq.c |
| | neorv32_xirq.h |
| Top entity port: | `xirq_i` | External interrupts input (32-bit)
| Configuration generics: | `XIRQ_NUM_CH` | Number of external IRQ channels to implement (0..32)
| | `XIRQ_TRIGGER_TYPE` | IRQ trigger type configuration
| | `XIRQ_TRIGGER_POLARITY` | IRQ trigger polarity configuration
| CPU interrupts: | fast IRQ channel 8 | XIRQ (see <<_processor_interrupts>>)
|=======================
**Overview**
The external interrupt controller provides a simple mechanism to implement up to 32 processor-external interrupt
request signals. The external IRQ requests are prioritized, queued and signaled to the CPU via a
_single_ CPU fast interrupt request.
**Theory of Operation**
The XIRQ provides up to 32 external interrupt channels configured via the `XIRQ_NUM_CH` generic. Each bit in the `xirq_i`
input signal vector represents one interrupt channel. If less than 32 channels are configured, only the LSB-aligned channels
are used while the remaining ones are left unconnected internally. The actual interrupt trigger type is configured before
synthesis using the `XIRQ_TRIGGER_TYPE` and `XIRQ_TRIGGER_POLARITY` generics (see table below).
.XIRQ Trigger Configuration
[cols="^2,^2,<3"]
[options="header",grid="all"]
|=======================
| `XIRQ_TRIGGER_TYPE(i)` | `XIRQ_TRIGGER_POLARITY(i)` | Resulting Trigger of `xirq_i(i)`
| `0` | `0` | low-level
| `0` | `1` | high-level
| `1` | `0` | falling-edge
| `1` | `1` | rising-edge
|=======================
The interrupt controller features three interface registers: external interrupt channel enable (`EIE`), external interrupt
channel pending (`EIP`) and external interrupt source (`ESC`). From a functional point of view, the functionality of these
registers follow the one of the RISC-V <<_mie>>, <<_mip>> and <<_mcause>> CSRs.
If the configured trigger of an interrupt channel fires (e.g. a rising edge) the according interrupt channel becomes _pending_,
which is indicated by the according channel bit being set in the `EIP` register. This pending interrupt can be cleared at any time
by writing zero to the according `EIP` bit.
A pending interrupt can only trigger a CPU interrupt if the according is enabled via the `EIE` register. Once triggered, disabled
channels that were triggered remain pending until explicitly cleared. The channels are prioritized in a static order, i.e. channel 0
(`xirq_i(0)`) has the highest priority and channel 31 (`xirq_i(31)`) has the lowest priority. If any pending interrupt channel is
actually enabled, an interrupt request is sent to the CPU.
The CPU can determine the most prioritized external interrupt request either by checking the bits in the `IPR` register or by reading
the interrupt source register `ESC`. This register provides a 5-bit wide ID (0..31) identifying the currently firing external interrupt.
Writing _any_ value to this register will acknowledge the _current_ XIRQ interrupt (so the XIRQ controller can issue a new CPU interrupt).
In order to acknowledge an XIRQ interrupt, the interrupt handler has to...
* clear the pending CPU FIRQ by clearing the according <<_mip>> CSR bit
* clear the pending XIRQ channel by clearing the according `EIP` bit
* writing _any_ value to `ESC` to acknowledge the XIRQ interrupt
**Register Map**
.XIRQ register map (`struct NEORV32_XIRQ`)
[cols="^4,<2,^2,^2,<14"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s) | R/W | Description
| `0xfffff300` | `EIE` | `31:0` | r/w | External interrupt enable register (one bit per channel, LSB-aligned)
| `0xfffff304` | `EIP` | `31:0` | r/w | External interrupt pending register (one bit per channel, LSB-aligned); writing 0 to a bit clears the according pending interrupt
| `0xfffff308` | `ESC` | `4:0` | r/w | Interrupt source ID (0..31) of firing IRQ (prioritized!); writing _any_ value will acknowledge the current XIRQ interrupt
| `0xfffff30c` | - | `31:0` | r/- | _reserved_, read as zero
|=======================