mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-24 06:07:52 -04:00
Merge branch 'main' into memory_config
This commit is contained in:
commit
3ece3f9ab1
9 changed files with 127 additions and 161 deletions
|
@ -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) |
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue