Merge branch 'main' into memory_config

This commit is contained in:
stnolting 2024-10-21 21:33:28 +02:00 committed by GitHub
commit 3ece3f9ab1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 127 additions and 161 deletions

View file

@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 20.10.2024 | 1.10.5.9 | :warning: rework XIRQ controller; remove "interrupt pending" register `EIP` | [#1071](https://github.com/stnolting/neorv32/pull/1071) |
| 18.10.2024 | 1.10.5.8 | minor RTL code cleanups | [#1068](https://github.com/stnolting/neorv32/pull/1068) |
| 18.10.2024 | 1.10.5.7 | use individual/new module for XBUS-to-AXI4-Lite bridge | [#1063](https://github.com/stnolting/neorv32/pull/1063) |
| 12.10.2024 | 1.10.5.6 | :warning: remove legacy support for on-chip debugger DM version v0.13; now only supporting DM v1.0 (removing `OCD_DM_LEGACY_MODE` generic) | [#1056](https://github.com/stnolting/neorv32/pull/1056) |

View file

@ -28,22 +28,17 @@ The XIRQ provides up to 32 external interrupt channels configured via the `XIRQ_
`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 external interrupt controller features five interface registers:
The external interrupt controller features four interface registers:
[start=1]
. external interrupt channel enable (`EIE`)
. external interrupt channel pending (`EIP`)
. external interrupt source (`ESC`)
. trigger type configuration (`TTYP`)
. trigger polarity configuration (`TPOL`)
[TIP]
From a functional point of view, the `EIE`, `EIP` and `ESC` registers follow the behavior
of the RISC-V <<_mie>>, <<_mip>> and <<_mcause>> CSRs.
The actual interrupt trigger type can be configured individually for each channel using the `TTYP` and `TPOL`
registers. `TTYP` defines the actual trigger type (level-triggered or edge-triggered), while `TPOL` defines
the trigger's polarity (low-level/falling-edge or high-level_/rising-edge). The position of each bit in these
the trigger's polarity (low-level/falling-edge or high-level/rising-edge). The position of each bit in these
registers corresponds the according XIRQ channel.
.XIRQ Trigger Configuration
@ -57,24 +52,19 @@ registers corresponds the according XIRQ channel.
| `1` | `1` | rising-edge
|=======================
When the configured trigger of an interrupt channel fires 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 manually cleared at any time by writing zero to the according `EIP` bit.
Each interrupt channel can be enabled or disabled individually using the `EIE` register. If the trigger of a
disabled channel fires the interrupt request is entirely ignored.
A pending interrupt can only generate a CPU interrupt if the according channel is enabled by the `EIE`
register. Once triggered, disabled channels that **were already triggered** remain pending until explicitly
(= manually) 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
also enabled, an interrupt request is sent to the CPU.
If the configured trigger of an _enabled_ channels fires, the according interrupt request is buffered internally
and an interrupt request is sent to the CPU. If more than one trigger fires at one a prioritization is used:
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.
The CPU can determine the most prioritized external interrupt request either by checking the bits in the `EIP`
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 source channel. Writing _any_ value to this register will
acknowledge and clear the _current_ CPU 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 XIRQ channel by clearing the according `EIP` bit
* writing _any_ value to `ESC` to acknowledge the XIRQ CPU interrupt
The CPU can determine the most prioritized external interrupt request by reading the interrupt source register `ESC`.
This register provides a 5-bit wide ID (0..31) identifying the currently firing external interrupt source channel as
well as a single bit (the MSB) that
Writing _any_ value to this register will acknowledge and clear the _current_ CPU interrupt (so the XIRQ controller
can issue a new CPU interrupt).
**Register Map**
@ -85,11 +75,9 @@ In order to acknowledge an XIRQ interrupt, the interrupt handler has to...
|=======================
| 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 CPU interrupt
| `0xfffff30c` | `TTYP` | `31:0` | r/w | Trigger type select (`0` = level trigger, `1` = edge trigger); each bit corresponds to the according channel number
| `0xfffff310` | `TPOL` | `31:0` | r/w | Trigger polarity select (`0` = low-level/falling-edge, `1` = high-level/rising-edge); each bit corresponds to the according channel number
| `0xfffff314` | - | `31:0` | r/- | _reserved_, read as zero
| `0xfffff318` | - | `31:0` | r/- | _reserved_, read as zero
| `0xfffff31c` | - | `31:0` | r/- | _reserved_, read as zero
.3+^| `0xfffff304` .3+<| `ESC` ^| `31` ^| r/c <| XIRQ interrupt when set; write any value to this register to acknowledge the current XIRQ interrupt
^| `30:5` ^| r/- <| _reserved_, read as zero
^| `4:0` ^| r/c <| Interrupt source ID (0..31) of firing IRQ (prioritized!)
| `0xfffff308` | `TTYP` | `31:0` | r/w | Trigger type select (`0` = level trigger, `1` = edge trigger); each bit corresponds to the according channel number
| `0xfffff30c` | `TPOL` | `31:0` | r/w | Trigger polarity select (`0` = low-level/falling-edge, `1` = high-level/rising-edge); each bit corresponds to the according channel number
|=======================

View file

@ -29,7 +29,7 @@ package neorv32_package is
-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100508"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100509"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

View file

@ -2,8 +2,8 @@
-- NEORV32 SoC - External Interrupt Controller (XIRQ) --
-- -------------------------------------------------------------------------------- --
-- Simple interrupt controller for platform (processor-external) interrupts. Up to --
-- 32 channels are supported that get (optionally) prioritized into a single CPU --
-- interrupt. Trigger type is programmable per channel by configuration registers. --
-- 32 channels are supported that get prioritized into a single CPU interrupt. --
-- Trigger type is programmable per-channel by configuration registers. --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -36,27 +36,29 @@ end neorv32_xirq;
architecture neorv32_xirq_rtl of neorv32_xirq is
-- register addresses --
constant addr_enable_c : std_ulogic_vector(2 downto 0) := "000"; -- r/w: channel enable
constant addr_pending_c : std_ulogic_vector(2 downto 0) := "001"; -- r/w: pending IRQs
constant addr_source_c : std_ulogic_vector(2 downto 0) := "010"; -- r/w: source IRQ, ACK on write
constant addr_ttype_c : std_ulogic_vector(2 downto 0) := "011"; -- r/w: trigger type (level/edge)
constant addr_tpolarity_c : std_ulogic_vector(2 downto 0) := "100"; -- r/w: trigger polarity (high/low or rising/falling)
constant addr_eie_c : std_ulogic_vector(1 downto 0) := "00"; -- r/w: channel enable
constant addr_esc_c : std_ulogic_vector(1 downto 0) := "01"; -- r/w: source IRQ, ACK on write
constant addr_ttyp_c : std_ulogic_vector(1 downto 0) := "10"; -- r/w: trigger type (level/edge)
constant addr_tpol_c : std_ulogic_vector(1 downto 0) := "11"; -- r/w: trigger polarity (high/low or rising/falling)
-- interface registers --
signal irq_enable, nclr_pending, irq_type, irq_polarity : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
signal irq_source : std_ulogic_vector(4 downto 0);
-- configuration registers --
signal irq_enable, irq_type, irq_polarity : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
-- interrupt trigger --
signal irq_sync, irq_sync2, irq_trig : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
signal irq_sync1, irq_sync2, irq_trig : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
-- interrupt buffer --
signal irq_pending, irq_raw : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
signal irq_fire, irq_active : std_ulogic;
-- pending interrupt(s) --
signal irq_pending : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
-- priority encoder --
type prio_enc_t is array (0 to XIRQ_NUM_CH-1) of std_ulogic_vector(4 downto 0);
signal prio_enc : prio_enc_t;
-- interrupt arbiter --
signal irq_state : std_ulogic_vector(1 downto 0);
signal irq_source : std_ulogic_vector(4 downto 0);
signal irq_clear : std_ulogic_vector(31 downto 0);
begin
-- Bus Access -----------------------------------------------------------------------------
@ -65,7 +67,6 @@ begin
begin
if (rstn_i = '0') then
bus_rsp_o <= rsp_terminate_c;
nclr_pending <= (others => '0');
irq_type <= (others => '0');
irq_polarity <= (others => '0');
irq_enable <= (others => '0');
@ -74,29 +75,29 @@ begin
bus_rsp_o.ack <= bus_req_i.stb;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= (others => '0');
nclr_pending <= (others => '1');
-- bus access --
if (bus_req_i.stb = '1') then
if (bus_req_i.rw = '1') then -- write access
if (bus_req_i.addr(4 downto 2) = addr_enable_c) then -- channel-enable
if (bus_req_i.addr(3 downto 2) = addr_eie_c) then -- channel-enable
irq_enable <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0);
end if;
if (bus_req_i.addr(4 downto 2) = addr_pending_c) then -- clear pending IRQs
nclr_pending <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0); -- set zero to clear pending IRQ
end if;
if (bus_req_i.addr(4 downto 2) = addr_ttype_c) then -- trigger type
if (bus_req_i.addr(3 downto 2) = addr_ttyp_c) then -- trigger type
irq_type <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0);
end if;
if (bus_req_i.addr(4 downto 2) = addr_tpolarity_c) then -- trigger polarity
if (bus_req_i.addr(3 downto 2) = addr_tpol_c) then -- trigger polarity
irq_polarity <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0);
end if;
else -- read access
case bus_req_i.addr(4 downto 2) is
when addr_enable_c => bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_enable; -- channel-enable
when addr_pending_c => bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_pending; -- pending IRQs
when addr_source_c => bus_rsp_o.data(4 downto 0) <= irq_source; -- IRQ source
when addr_ttype_c => bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_type; -- trigger type
when others => bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_polarity; -- trigger polarity
case bus_req_i.addr(3 downto 2) is
when addr_eie_c => -- channel-enable
bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_enable;
when addr_esc_c =>
bus_rsp_o.data(31) <= irq_state(1); -- active interrupt waiting for ACK
bus_rsp_o.data(4 downto 0) <= irq_source; -- interrupt source (channel number)
when addr_ttyp_c => -- trigger type
bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_type;
when others => -- trigger polarity
bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_polarity;
end case;
end if;
end if;
@ -109,55 +110,51 @@ begin
synchronizer: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_sync <= (others => '0');
irq_sync1 <= (others => '0');
irq_sync2 <= (others => '0');
elsif rising_edge(clk_i) then
irq_sync <= xirq_i(XIRQ_NUM_CH-1 downto 0);
irq_sync2 <= irq_sync;
irq_sync1 <= xirq_i(XIRQ_NUM_CH-1 downto 0);
irq_sync2 <= irq_sync1;
end if;
end process synchronizer;
-- trigger type select --
irq_trigger_gen:
for i in 0 to XIRQ_NUM_CH-1 generate
irq_trigger: process(irq_sync, irq_sync2, irq_type, irq_polarity)
irq_trigger: process(irq_sync1, irq_sync2, irq_type, irq_polarity)
variable sel_v : std_ulogic_vector(1 downto 0);
begin
sel_v := irq_type(i) & irq_polarity(i);
case sel_v is
when "00" => irq_trig(i) <= not irq_sync(i); -- low-level
when "01" => irq_trig(i) <= irq_sync(i); -- high-level
when "10" => irq_trig(i) <= (not irq_sync(i)) and irq_sync2(i); -- falling-edge
when "11" => irq_trig(i) <= irq_sync(i) and (not irq_sync2(i)); -- rising-edge
when "00" => irq_trig(i) <= not irq_sync1(i); -- low-level
when "01" => irq_trig(i) <= irq_sync1(i); -- high-level
when "10" => irq_trig(i) <= (not irq_sync1(i)) and irq_sync2(i); -- falling-edge
when "11" => irq_trig(i) <= irq_sync1(i) and (not irq_sync2(i)); -- rising-edge
when others => irq_trig(i) <= '0';
end case;
end process irq_trigger;
end generate;
-- IRQ Buffer ---------------------------------------------------------------
-- Interrupt-Pending Buffer -------------------------------------------------
-- -----------------------------------------------------------------------------
irq_buffer: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_pending <= (others => '0');
elsif rising_edge(clk_i) then
irq_pending <= (irq_pending and nclr_pending) or irq_trig;
irq_pending <= irq_enable and ((irq_pending and (not irq_clear(XIRQ_NUM_CH-1 downto 0))) or irq_trig);
end if;
end process irq_buffer;
-- filter enabled channels --
irq_raw <= irq_pending and irq_enable;
-- anyone firing? --
irq_fire <= or_reduce_f(irq_raw);
-- encode highest-priority source (structural code: mux-chain) --
-- Priority Encoder (structural code: mux-chain) ----------------------------
-- -----------------------------------------------------------------------------
priority_encoder_gen:
for i in 0 to XIRQ_NUM_CH-1 generate -- start with highest priority
for i in 0 to XIRQ_NUM_CH-1 generate -- start with highest priority (=0)
priority_encoder_gen_chain: -- inside chain
if i < XIRQ_NUM_CH-1 generate
prio_enc(i) <= std_ulogic_vector(to_unsigned(i, 5)) when (irq_raw(i) = '1') else prio_enc(i+1);
prio_enc(i) <= std_ulogic_vector(to_unsigned(i, 5)) when (irq_pending(i) = '1') else prio_enc(i+1);
end generate;
priority_encoder_gen_last: -- end of chain
if i = XIRQ_NUM_CH-1 generate
@ -171,23 +168,34 @@ begin
irq_arbiter: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_active <= '0';
irq_clear <= (others => '0');
irq_source <= (others => '0');
irq_state <= (others => '0');
elsif rising_edge(clk_i) then
if (irq_active = '0') then -- no active IRQ
irq_source <= prio_enc(0); -- get IRQ source
if (irq_fire = '1') then
irq_active <= '1';
end if;
elsif (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and
(bus_req_i.addr(4 downto 2) = addr_source_c) then -- acknowledge on write access
irq_active <= '0';
end if;
irq_clear <= (others => '0'); -- default
case irq_state is
when "00" => -- wait for pending interrupt
irq_source <= prio_enc(0); -- highest-priority channel
if (or_reduce_f(irq_pending) = '1') then
irq_state <= "01";
end if;
when "01" => -- clear triggering channel
irq_clear(to_integer(unsigned(irq_source))) <= '1'; -- ACK/clear according pending bit
irq_state <= "11";
when others => -- wait for CPU acknowledge
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(3 downto 2) = addr_esc_c) then -- acknowledge on write access
irq_state <= "00";
end if;
end case;
end if;
end process irq_arbiter;
-- CPU interrupt --
cpu_irq_o <= irq_active;
cpu_irq_o <= irq_state(0);
end neorv32_xirq_rtl;

View file

@ -155,8 +155,6 @@ int main() {
// All incoming XIRQ interrupt requests are "prioritized" in this example. The XIRQ FIRQ handler
// reads the ID of the interrupt with the highest priority from the XIRQ controller ("source" register) and calls the according
// handler function (installed via neorv32_xirq_install();).
// Non-prioritized handling of interrupts (or custom prioritization) can be implemented by manually reading the
// XIRQ controller's "pending" register. Then it is up to the software to define which pending IRQ should be serviced first.
asm volatile ("nop");
asm volatile ("nop");
@ -165,15 +163,12 @@ int main() {
// just as an example: to disable certain XIRQ interrupt channels, we can
// un-install the according handler. this will also clear a pending interrupt for that channel
// un-install the according handler. this will also disable the according channel.
neorv32_xirq_uninstall(0); // disable XIRQ channel 0 and remove associated handler
neorv32_xirq_uninstall(1); // disable XIRQ channel 1 and remove associated handler
neorv32_xirq_uninstall(2); // disable XIRQ channel 2 and remove associated handler
neorv32_xirq_uninstall(3); // disable XIRQ channel 3 and remove associated handler
// you can also manually clear pending interrupts
neorv32_xirq_clear_pending(0); // clear pending interrupt of channel 0
// manually enable and disable XIRQ channels
neorv32_xirq_channel_enable(0); // enable channel 0
neorv32_xirq_channel_disable(0); // disable channel 0

View file

@ -1324,8 +1324,6 @@ int main() {
xirq_err_cnt += neorv32_xirq_install(1, xirq_trap_handler1); // install XIRQ IRQ handler channel 1
neorv32_xirq_setup_trigger(0, XIRQ_TRIGGER_EDGE_RISING); // configure channel 0 as rising-edge trigger
neorv32_xirq_setup_trigger(1, XIRQ_TRIGGER_EDGE_RISING); // configure channel 1 as rising-edge trigger
neorv32_xirq_clear_pending(0); // clear any pending request
neorv32_xirq_clear_pending(1); // clear any pending request
neorv32_xirq_channel_enable(0); // enable XIRQ channel 0
neorv32_xirq_channel_enable(1); // enable XIRQ channel 1

View file

@ -25,14 +25,10 @@
/**@{*/
/** XIRQ module prototype */
typedef volatile struct __attribute__((packed,aligned(4))) {
uint32_t EIE; /**< offset 0: external interrupt enable register */
uint32_t EIP; /**< offset 4: external interrupt pending register */
uint32_t ESC; /**< offset 8: external interrupt source register */
uint32_t TTYP; /**< offset 12: external interrupt source register */
uint32_t TPOL; /**< offset 16: external interrupt source register */
const uint32_t reserved0; /**< offset 20: reserved */
const uint32_t reserved1; /**< offset 24: reserved */
const uint32_t reserved2; /**< offset 28: reserved */
uint32_t EIE; /**< offset 0: external interrupt enable register */
uint32_t ESC; /**< offset 4: external interrupt source register */
uint32_t TTYP; /**< offset 8: external interrupt source register */
uint32_t TPOL; /**< offset 12: external interrupt source register */
} neorv32_xirq_t;
/** XIRQ module hardware access (#neorv32_xirq_t) */
@ -41,14 +37,14 @@ typedef volatile struct __attribute__((packed,aligned(4))) {
/**********************************************************************//**
* XIRQ trigger configuration
* XIRQ trigger type configuration
**************************************************************************/
/**@{*/
#define XIRQ_TRIGGER_LEVEL_LOW (0b00) // low-level
#define XIRQ_TRIGGER_LEVEL_HIGH (0b01) // high-level
#define XIRQ_TRIGGER_EDGE_FALLING (0b10) // falling-edge
#define XIRQ_TRIGGER_EDGE_RISING (0b11) // rising-edge
/**@}*/
enum XIRQ_TRIGGER_enum {
XIRQ_TRIGGER_LEVEL_LOW = 0b00, // low-level
XIRQ_TRIGGER_LEVEL_HIGH = 0b01, // high-level
XIRQ_TRIGGER_EDGE_FALLING = 0b10, // falling-edge
XIRQ_TRIGGER_EDGE_RISING = 0b11 // rising-edge
};
/**********************************************************************//**
@ -61,7 +57,6 @@ void neorv32_xirq_global_enable(void);
void neorv32_xirq_global_disable(void);
int neorv32_xirq_get_num(void);
void neorv32_xirq_setup_trigger(int channel, int config);
void neorv32_xirq_clear_pending(int channel);
void neorv32_xirq_channel_enable(int channel);
void neorv32_xirq_channel_disable(int channel);
int neorv32_xirq_install(int channel, void (*handler)(void));

View file

@ -44,10 +44,8 @@
#include <neorv32.h>
/**********************************************************************//**
* The >private< trap vector look-up table of the XIRQ.
**************************************************************************/
static uint32_t __neorv32_xirq_vector_lut[32] __attribute__((unused)); // trap handler vector table
// the private trap vector look-up table
static uint32_t __neorv32_xirq_vector_lut[32] __attribute__((unused));
// private functions
static void __neorv32_xirq_core(void);
@ -73,15 +71,14 @@ int neorv32_xirq_available(void) {
/**********************************************************************//**
* Initialize XIRQ controller.
*
* @note All interrupt channels will be deactivated, all pending IRQs will be deleted and all
* handler addresses will be deleted.
* @note All interrupt channels will be deactivated and all installed
* handlers addresses will be deleted.
*
* @return 0 if success, != 0 if error.
**************************************************************************/
int neorv32_xirq_setup(void) {
NEORV32_XIRQ->EIE = 0; // disable all input channels
NEORV32_XIRQ->EIP = 0; // clear all pending IRQs
NEORV32_XIRQ->EIE = 0; // disable all channels
NEORV32_XIRQ->ESC = 0; // acknowledge (clear) XIRQ interrupt
int i;
@ -160,7 +157,7 @@ int neorv32_xirq_get_num(void) {
* Configure a channel's trigger type.
*
* @param[in] channel XIRQ interrupt channel (0..31).
* @param[in] config Trigger type: 00 = low-level, 01 = high-level, 10 = falling-edge, 11 = rising-edge.
* @param[in] config Trigger type (#XIRQ_TRIGGER_enum).
**************************************************************************/
void neorv32_xirq_setup_trigger(int channel, int config) {
@ -185,18 +182,6 @@ void neorv32_xirq_setup_trigger(int channel, int config) {
}
/**********************************************************************//**
* Clear pending interrupt.
*
* @param[in] channel XIRQ interrupt channel (0..31).
**************************************************************************/
void neorv32_xirq_clear_pending(int channel) {
channel &= 0x1f;
NEORV32_XIRQ->EIP = ~(1 << channel);
}
/**********************************************************************//**
* Enable IRQ channel.
*
@ -204,8 +189,7 @@ void neorv32_xirq_clear_pending(int channel) {
**************************************************************************/
void neorv32_xirq_channel_enable(int channel) {
channel &= 0x1f;
NEORV32_XIRQ->EIE |= 1 << channel;
NEORV32_XIRQ->EIE |= 1 << (channel & 0x1f);
}
@ -216,8 +200,7 @@ void neorv32_xirq_channel_enable(int channel) {
**************************************************************************/
void neorv32_xirq_channel_disable(int channel) {
channel &= 0x1f;
NEORV32_XIRQ->EIE &= ~(1 << channel);
NEORV32_XIRQ->EIE &= ~(1 << (channel & 0x1f));
}
@ -226,7 +209,7 @@ void neorv32_xirq_channel_disable(int channel) {
*
* @param[in] channel XIRQ interrupt channel (0..31).
* @param[in] handler The actual handler function for the specified interrupt (function MUST be of type "void function(void);").
* @return 0 if success, 1 if error.
* @return 0 if success, -1 if invalid channel.
**************************************************************************/
int neorv32_xirq_install(int channel, void (*handler)(void)) {
@ -235,7 +218,9 @@ int neorv32_xirq_install(int channel, void (*handler)(void)) {
__neorv32_xirq_vector_lut[channel] = (uint32_t)handler; // install handler
return 0;
}
return 1;
else {
return -1;
}
}
@ -245,39 +230,40 @@ int neorv32_xirq_install(int channel, void (*handler)(void)) {
* @note This will also deactivate the according XIRQ channel.
*
* @param[in] channel XIRQ interrupt channel (0..31).
* @return 0 if success, 1 if error.
* @return 0 if success, -1 if invalid channel.
**************************************************************************/
int neorv32_xirq_uninstall(int channel) {
// channel valid?
if (channel < 32) {
__neorv32_xirq_vector_lut[channel] = (uint32_t)(&__neorv32_xirq_dummy_handler); // override using dummy handler
uint32_t mask = 1 << channel;
NEORV32_XIRQ->EIE &= ~mask; // disable channel
neorv32_xirq_channel_disable(channel); // disable channel
return 0;
}
return 1;
else {
return -1;
}
}
/**********************************************************************//**
* This is the actual second-level (F)IRQ handler for the XIRQ. It will
* call the previously installed handler if an XIRQ fires.
*
* @note The XIRQ's channel interrupt is acknowledge AFTER the handler has been executed.
**************************************************************************/
static void __neorv32_xirq_core(void) {
// get highest-priority XIRQ channel
uint32_t src = NEORV32_XIRQ->ESC;
// clear the currently pending XIRQ interrupt
NEORV32_XIRQ->EIP = ~(1 << src);
uint32_t src = NEORV32_XIRQ->ESC & 0x1f; // mask for channel ID
// execute handler
typedef void handler_t();
handler_t* handler = (handler_t*)__neorv32_xirq_vector_lut[src];
handler();
NEORV32_XIRQ->ESC = 0; // acknowledge the current XIRQ interrupt
// acknowledge XIRQ channel interrupt
NEORV32_XIRQ->ESC = 0;
}

View file

@ -847,29 +847,24 @@
<registers>
<register>
<name>IER</name>
<description>IRQ input enable register</description>
<name>EIE</name>
<description>External IRQ channel enable register</description>
<addressOffset>0x00</addressOffset>
</register>
<register>
<name>IPR</name>
<description>IRQ pending/ack/clear register</description>
<name>ESC</name>
<description>External IRQ source register</description>
<addressOffset>0x04</addressOffset>
</register>
<register>
<name>SCR</name>
<description>IRQ source register</description>
<name>TTYP</name>
<description>External IRQ trigger type (level/edge)</description>
<addressOffset>0x08</addressOffset>
</register>
<register>
<name>TTYP</name>
<description>IRQ trigger type (level/edge)</description>
<addressOffset>0x0c</addressOffset>
</register>
<register>
<name>TPOL</name>
<description>IRQ trigger polarity (high/low, rising/falling)</description>
<addressOffset>0x10</addressOffset>
<description>External IRQ trigger polarity (high/low, rising/falling)</description>
<addressOffset>0x0c</addressOffset>
</register>
</registers>
</peripheral>