diff --git a/CHANGELOG.md b/CHANGELOG.md index 3848892e1..85a904c81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) | diff --git a/docs/datasheet/soc_xirq.adoc b/docs/datasheet/soc_xirq.adoc index b2ebc8377..5cb97b853 100644 --- a/docs/datasheet/soc_xirq.adoc +++ b/docs/datasheet/soc_xirq.adoc @@ -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. - -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. +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. -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). +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. -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 |======================= diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index fe005134d..30ccb8d86 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -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 diff --git a/rtl/core/neorv32_xirq.vhd b/rtl/core/neorv32_xirq.vhd index 5ed5b45e5..8841c62c2 100644 --- a/rtl/core/neorv32_xirq.vhd +++ b/rtl/core/neorv32_xirq.vhd @@ -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) - - -- 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); + 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) + + -- 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; diff --git a/sw/example/demo_xirq/main.c b/sw/example/demo_xirq/main.c index 9349b6517..ce81d3ca7 100644 --- a/sw/example/demo_xirq/main.c +++ b/sw/example/demo_xirq/main.c @@ -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 diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index cb687e899..530ec7185 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -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 diff --git a/sw/lib/include/neorv32_xirq.h b/sw/lib/include/neorv32_xirq.h index 590a6fde0..c160118b8 100644 --- a/sw/lib/include/neorv32_xirq.h +++ b/sw/lib/include/neorv32_xirq.h @@ -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)); diff --git a/sw/lib/source/neorv32_xirq.c b/sw/lib/source/neorv32_xirq.c index f2eb4299d..4904f92be 100644 --- a/sw/lib/source/neorv32_xirq.c +++ b/sw/lib/source/neorv32_xirq.c @@ -44,10 +44,8 @@ #include -/**********************************************************************//** - * 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; } diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index 52b90a2ce..8655685c4 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -847,29 +847,24 @@ - IER - IRQ input enable register + EIE + External IRQ channel enable register 0x00 - IPR - IRQ pending/ack/clear register + ESC + External IRQ source register 0x04 - - SCR - IRQ source register - 0x08 - TTYP - IRQ trigger type (level/edge) - 0x0c + External IRQ trigger type (level/edge) + 0x08 TPOL - IRQ trigger polarity (high/low, rising/falling) - 0x10 + External IRQ trigger polarity (high/low, rising/falling) + 0x0c