diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c9da4c9..0d9d85d3f 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 | |:----:|:-------:|:--------|:------:| +| 27.05.2024 | 1.9.9.3 | removed `XIRQ_TRIGGER_*` generics; XIRQ trigger type is now _programmable_ by dedicated configuration registers | [#911](https://github.com/stnolting/neorv32/pull/911) | | 21.05.2024 | 1.9.9.2 | :sparkles: add SLINK routing information ports (compatible to AXI-stream's `TID` and `TDEST` signals) | [#908](https://github.com/stnolting/neorv32/pull/908) | | 04.05.2024 | 1.9.9.1 | :sparkles: add NEORV32 as Vivado IP block | [#894](https://github.com/stnolting/neorv32/pull/894) | | 03.05.2024 | [**:rocket:1.9.9**](https://github.com/stnolting/neorv32/releases/tag/v1.9.9) | **New release** | | diff --git a/docs/datasheet/soc.adoc b/docs/datasheet/soc.adoc index 5eaede336..f4d500c41 100644 --- a/docs/datasheet/soc.adoc +++ b/docs/datasheet/soc.adoc @@ -264,8 +264,6 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt | `XIP_CACHE_BLOCK_SIZE` | natural | 256 | Number of bytes per XIP cache block. Has to be a power of two, min 4. 4+^| **<<_external_interrupt_controller_xirq>>** | `XIRQ_NUM_CH` | natural | 0 | Number of channels of the external interrupt controller. Valid values are 0..32. -| `XIRQ_TRIGGER_TYPE` | suv(31:0) | 0xFFFFFFFF | Trigger type (one bit per channel): `0` = level-triggered, '1' = edge triggered. -| `XIRQ_TRIGGER_POLARITY` | suv(31:0) | 0xFFFFFFFF | Trigger polarity (one bit per channel): `0` = low-level/falling-edge, '1' = high-level/rising-edge. 4+^| **Peripheral/IO Modules** | `IO_GPIO_NUM` | natural | 0 | Number of general purpose input/output pairs of the <<_general_purpose_input_and_output_port_gpio>>. | `IO_MTIME_EN` | boolean | false | Implement the <<_machine_system_timer_mtime>>. diff --git a/docs/datasheet/soc_xirq.adoc b/docs/datasheet/soc_xirq.adoc index 0e62ee5bd..94bc65c3e 100644 --- a/docs/datasheet/soc_xirq.adoc +++ b/docs/datasheet/soc_xirq.adoc @@ -5,62 +5,75 @@ [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>>) +| 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 generic(s): | `XIRQ_NUM_CH` | Number of external IRQ channels to implement (0..32) +| 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. +The external interrupt controller provides a simple mechanism to implement up to 32 platform-level / processor-external +interrupt request signals. The external IRQ requests are prioritized, queued and signaled to the CPU via a +_single_ CPU fast interrupt request channel. **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). +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 external interrupt controller features five 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 +registers corresponds the according XIRQ channel. .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 +| `TTYP(i)` | `TPOL(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. +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 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. +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. -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). +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 interrupt +* writing _any_ value to `ESC` to acknowledge the XIRQ CPU interrupt **Register Map** @@ -70,8 +83,12 @@ In order to acknowledge an XIRQ interrupt, the interrupt handler has to... [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 +| `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 |======================= diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index 77c71daaf..14e32fe0b 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"01090902"; -- hardware version + constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090903"; -- hardware version constant archid_c : natural := 19; -- official RISC-V architecture ID constant XLEN : natural := 32; -- native data path width @@ -769,8 +769,6 @@ package neorv32_package is XIP_CACHE_BLOCK_SIZE : natural range 1 to 2**16 := 256; -- External Interrupts Controller (XIRQ) -- XIRQ_NUM_CH : natural range 0 to 32 := 0; - XIRQ_TRIGGER_TYPE : std_ulogic_vector(31 downto 0) := x"ffffffff"; - XIRQ_TRIGGER_POLARITY : std_ulogic_vector(31 downto 0) := x"ffffffff"; -- Processor peripherals -- IO_GPIO_NUM : natural range 0 to 64 := 0; IO_MTIME_EN : boolean := false; diff --git a/rtl/core/neorv32_top.vhd b/rtl/core/neorv32_top.vhd index 4c92cf303..7ff2839c4 100644 --- a/rtl/core/neorv32_top.vhd +++ b/rtl/core/neorv32_top.vhd @@ -99,8 +99,6 @@ entity neorv32_top is -- External Interrupts Controller (XIRQ) -- XIRQ_NUM_CH : natural range 0 to 32 := 0; -- number of external IRQ channels (0..32) - XIRQ_TRIGGER_TYPE : std_ulogic_vector(31 downto 0) := x"ffffffff"; -- trigger type: 0=level, 1=edge - XIRQ_TRIGGER_POLARITY : std_ulogic_vector(31 downto 0) := x"ffffffff"; -- trigger polarity: 0=low-level/falling-edge, 1=high-level/rising-edge -- Processor peripherals -- IO_GPIO_NUM : natural range 0 to 64 := 0; -- number of GPIO input/output pairs (0..64) @@ -1434,9 +1432,7 @@ begin if io_xirq_en_c generate neorv32_xirq_inst: entity neorv32.neorv32_xirq generic map ( - XIRQ_NUM_CH => XIRQ_NUM_CH, - XIRQ_TRIGGER_TYPE => XIRQ_TRIGGER_TYPE, - XIRQ_TRIGGER_POLARITY => XIRQ_TRIGGER_POLARITY + XIRQ_NUM_CH => XIRQ_NUM_CH ) port map ( clk_i => clk_i, diff --git a/rtl/core/neorv32_xirq.vhd b/rtl/core/neorv32_xirq.vhd index c134c0461..89238154e 100644 --- a/rtl/core/neorv32_xirq.vhd +++ b/rtl/core/neorv32_xirq.vhd @@ -3,10 +3,7 @@ -- -------------------------------------------------------------------------------- -- -- Simple interrupt controller for platform (processor-external) interrupts. Up to -- -- 32 channels are supported that get (optionally) prioritized into a single CPU -- --- interrupt. -- --- The actual trigger configuration has to be done BEFORE synthesis using the -- --- XIRQ_TRIGGER_TYPE and XIRQ_TRIGGER_POLARITY generics. These allow to configure -- --- channel-independent low-/high-level, falling-/rising-edge triggers. -- +-- interrupt. Trigger type is programmable per channel by configuration registers. -- -- -------------------------------------------------------------------------------- -- -- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 -- -- Copyright (c) NEORV32 contributors. -- @@ -24,9 +21,7 @@ use neorv32.neorv32_package.all; entity neorv32_xirq is generic ( - XIRQ_NUM_CH : natural range 0 to 32; -- number of IRQ channels - XIRQ_TRIGGER_TYPE : std_ulogic_vector(31 downto 0); -- trigger type: 0=level, 1=edge - XIRQ_TRIGGER_POLARITY : std_ulogic_vector(31 downto 0) -- trigger polarity: 0=low-level/falling-edge, 1=high-level/rising-edge + XIRQ_NUM_CH : natural range 0 to 32 -- number of IRQ channels ); port ( clk_i : in std_ulogic; -- global clock line @@ -40,10 +35,16 @@ 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 : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0); -- r/w: channel enable - signal nclr_pending : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0); -- r/w: pending IRQs - signal irq_source : std_ulogic_vector(4 downto 0); -- r/w: source IRQ, ACK on write + 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); -- interrupt trigger -- signal irq_sync, irq_sync2, irq_trig : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0); @@ -67,6 +68,8 @@ begin bus_rsp_o.err <= '0'; bus_rsp_o.data <= (others => '0'); nclr_pending <= (others => '0'); + irq_type <= (others => '0'); + irq_polarity <= (others => '0'); irq_enable <= (others => '0'); elsif rising_edge(clk_i) then -- defaults -- @@ -74,21 +77,28 @@ begin 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(3 downto 2) = "00") then -- channel-enable + if (bus_req_i.addr(4 downto 2) = addr_enable_c) then -- channel-enable irq_enable <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0); end if; - if (bus_req_i.addr(3 downto 2) = "01") then -- clear pending IRQs + 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 + 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 + irq_polarity <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0); + end if; else -- read access - case bus_req_i.addr(3 downto 2) is - when "00" => bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_enable; -- channel-enable - when "01" => bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_pending; -- pending IRQs - when others => bus_rsp_o.data(4 downto 0) <= irq_source; -- IRQ source + 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 end case; end if; end if; @@ -112,10 +122,10 @@ begin -- trigger type select -- irq_trigger_gen: for i in 0 to XIRQ_NUM_CH-1 generate - irq_trigger: process(irq_sync, irq_sync2) + irq_trigger: process(irq_sync, irq_sync2, irq_type, irq_polarity) variable sel_v : std_ulogic_vector(1 downto 0); begin - sel_v := XIRQ_TRIGGER_TYPE(i) & XIRQ_TRIGGER_POLARITY(i); + 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 @@ -165,7 +175,8 @@ begin 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(3 downto 2) = "10") then -- acknowledge on write access + 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; end if; diff --git a/rtl/system_integration/neorv32_vivado_ip.tcl b/rtl/system_integration/neorv32_vivado_ip.tcl index 05e32eec2..e18a8e6c6 100644 --- a/rtl/system_integration/neorv32_vivado_ip.tcl +++ b/rtl/system_integration/neorv32_vivado_ip.tcl @@ -244,10 +244,6 @@ ipgui::move_param -component [ipx::current_core] -order 14 [ipgui::get_guiparams # Configuration GUI: Peripherals # ************************************************************** set_property display_name {External interrupt controller (XIRQ)} [ipgui::get_guiparamspec -name "XIRQ_NUM_CH" -component [ipx::current_core]] -set_property display_name {External interrupt controller (XIRQ trigger type} [ipgui::get_guiparamspec -name "XIRQ_TRIGGER_TYPE" -component [ipx::current_core]] -set_property tooltip {0=level, 1=edge} [ipgui::get_guiparamspec -name "XIRQ_TRIGGER_TYPE" -component [ipx::current_core]] -set_property display_name {External interrupt controller (XIRQ trigger polarity} [ipgui::get_guiparamspec -name "XIRQ_TRIGGER_POLARITY" -component [ipx::current_core]] -set_property tooltip {0=low-level/falling-edge, 1=high-level/rising-edge} [ipgui::get_guiparamspec -name "XIRQ_TRIGGER_POLARITY" -component [ipx::current_core]] set_property display_name {GPIO port pins} [ipgui::get_guiparamspec -name "IO_GPIO_NUM" -component [ipx::current_core]] set_property display_name {Machine timer} [ipgui::get_guiparamspec -name "IO_MTIME_EN" -component [ipx::current_core]] set_property display_name {Primary UART (UART0)} [ipgui::get_guiparamspec -name "IO_UART0_EN" -component [ipx::current_core]] @@ -307,9 +303,7 @@ ipgui::move_param -component [ipx::current_core] -order 24 [ipgui::get_guiparams ipgui::move_param -component [ipx::current_core] -order 25 [ipgui::get_guiparamspec -name "IO_ONEWIRE_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "Peripherals" -component [ipx::current_core]] ipgui::move_param -component [ipx::current_core] -order 26 [ipgui::get_guiparamspec -name "IO_DMA_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "Peripherals" -component [ipx::current_core]] ipgui::move_param -component [ipx::current_core] -order 27 [ipgui::get_guiparamspec -name "XIRQ_NUM_CH" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "Peripherals" -component [ipx::current_core]] -ipgui::move_param -component [ipx::current_core] -order 28 [ipgui::get_guiparamspec -name "XIRQ_TRIGGER_TYPE" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "Peripherals" -component [ipx::current_core]] -ipgui::move_param -component [ipx::current_core] -order 29 [ipgui::get_guiparamspec -name "XIRQ_TRIGGER_POLARITY" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "Peripherals" -component [ipx::current_core]] -ipgui::move_param -component [ipx::current_core] -order 30 [ipgui::get_guiparamspec -name "IO_CRC_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "Peripherals" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 28 [ipgui::get_guiparamspec -name "IO_CRC_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "Peripherals" -component [ipx::current_core]] # ************************************************************** diff --git a/rtl/system_integration/neorv32_vivado_ip.vhd b/rtl/system_integration/neorv32_vivado_ip.vhd index 92c739a6b..eec9f3626 100644 --- a/rtl/system_integration/neorv32_vivado_ip.vhd +++ b/rtl/system_integration/neorv32_vivado_ip.vhd @@ -87,8 +87,6 @@ entity neorv32_vivado_ip is XIP_CACHE_BLOCK_SIZE : natural range 1 to 2**16 := 256; -- External Interrupts Controller (XIRQ) -- XIRQ_NUM_CH : natural := 0; - XIRQ_TRIGGER_TYPE : std_ulogic_vector(31 downto 0) := x"ffffffff"; - XIRQ_TRIGGER_POLARITY : std_ulogic_vector(31 downto 0) := x"ffffffff"; -- Processor peripherals -- IO_GPIO_NUM : natural range 0 to 64 := 0; IO_MTIME_EN : boolean := false; @@ -332,8 +330,6 @@ begin XIP_CACHE_BLOCK_SIZE => XIP_CACHE_BLOCK_SIZE, -- External Interrupts Controller -- XIRQ_NUM_CH => XIRQ_NUM_CH, - XIRQ_TRIGGER_TYPE => XIRQ_TRIGGER_TYPE, - XIRQ_TRIGGER_POLARITY => XIRQ_TRIGGER_POLARITY, -- Processor peripherals -- IO_GPIO_NUM => IO_GPIO_NUM, IO_MTIME_EN => IO_MTIME_EN, diff --git a/sim/neorv32_tb.vhd b/sim/neorv32_tb.vhd index 42cdf8486..2ae37c837 100644 --- a/sim/neorv32_tb.vhd +++ b/sim/neorv32_tb.vhd @@ -249,8 +249,6 @@ begin XIP_CACHE_BLOCK_SIZE => 256, -- block size in bytes (min 4), has to be a power of 2 -- External Interrupts Controller (XIRQ) -- XIRQ_NUM_CH => 32, -- number of external IRQ channels (0..32) - XIRQ_TRIGGER_TYPE => (others => '1'), -- trigger type: 0=level, 1=edge - XIRQ_TRIGGER_POLARITY => (others => '1'), -- trigger polarity: 0=low-level/falling-edge, 1=high-level/rising-edge -- Processor peripherals -- IO_GPIO_NUM => 64, -- number of GPIO input/output pairs (0..64) IO_MTIME_EN => true, -- implement machine system timer (MTIME)? diff --git a/sim/simple/neorv32_tb.simple.vhd b/sim/simple/neorv32_tb.simple.vhd index 6b3884cd1..9aa94d647 100644 --- a/sim/simple/neorv32_tb.simple.vhd +++ b/sim/simple/neorv32_tb.simple.vhd @@ -225,8 +225,6 @@ begin XIP_CACHE_BLOCK_SIZE => 256, -- block size in bytes (min 4), has to be a power of 2 -- External Interrupts Controller (XIRQ) -- XIRQ_NUM_CH => 32, -- number of external IRQ channels (0..32) - XIRQ_TRIGGER_TYPE => (others => '1'), -- trigger type: 0=level, 1=edge - XIRQ_TRIGGER_POLARITY => (others => '1'), -- trigger polarity: 0=low-level/falling-edge, 1=high-level/rising-edge -- Processor peripherals -- IO_GPIO_NUM => 64, -- number of GPIO input/output pairs (0..64) IO_MTIME_EN => true, -- implement machine system timer (MTIME)? diff --git a/sw/example/demo_xirq/main.c b/sw/example/demo_xirq/main.c index 75ecb9896..8a0fb0db1 100644 --- a/sw/example/demo_xirq/main.c +++ b/sw/example/demo_xirq/main.c @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -125,6 +125,13 @@ int main() { } + // configure per-channel trigger type + neorv32_xirq_setup_trigger(0, XIRQ_TRIGGER_EDGE_RISING); // rising-edge + neorv32_xirq_setup_trigger(1, XIRQ_TRIGGER_EDGE_RISING); // rising-edge + neorv32_xirq_setup_trigger(2, XIRQ_TRIGGER_EDGE_RISING); // rising-edge + neorv32_xirq_setup_trigger(3, XIRQ_TRIGGER_EDGE_RISING); // rising-edge + + // install handler functions for XIRQ channel 0,1,2,3. note that these functions are "normal" functions! // (details: these are "third-level" interrupt handlers) // neorv32_xirq_install() also enables the specified XIRQ channel and clears any pending interrupts diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index 75ea5a485..b8ac19aae 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -1322,6 +1322,8 @@ int main() { xirq_err_cnt += neorv32_xirq_setup(); // initialize XIRQ xirq_err_cnt += neorv32_xirq_install(0, xirq_trap_handler0); // install XIRQ IRQ handler channel 0 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 // enable XIRQ FIRQ neorv32_cpu_csr_write(CSR_MIE, 1 << XIRQ_FIRQ_ENABLE); diff --git a/sw/lib/include/neorv32_xirq.h b/sw/lib/include/neorv32_xirq.h index 01b01a768..41199eb26 100644 --- a/sw/lib/include/neorv32_xirq.h +++ b/sw/lib/include/neorv32_xirq.h @@ -22,10 +22,14 @@ /**@{*/ /** 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 */ - const uint32_t reserved; /**< offset 12: reserved */ + 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 */ } neorv32_xirq_t; /** XIRQ module hardware access (#neorv32_xirq_t) */ @@ -33,6 +37,17 @@ typedef volatile struct __attribute__((packed,aligned(4))) { /**@}*/ +/**********************************************************************//** + * XIRQ trigger 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 +/**@}*/ + + /**********************************************************************//** * @name Prototypes **************************************************************************/ @@ -42,6 +57,7 @@ int neorv32_xirq_setup(void); 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); diff --git a/sw/lib/source/neorv32_xirq.c b/sw/lib/source/neorv32_xirq.c index d81505dba..51013b67f 100644 --- a/sw/lib/source/neorv32_xirq.c +++ b/sw/lib/source/neorv32_xirq.c @@ -146,6 +146,35 @@ 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. + **************************************************************************/ +void neorv32_xirq_setup_trigger(int channel, int config) { + + if (channel > 31) { + return; + } + + uint32_t t = (((uint32_t)config) >> 1) & 1; + uint32_t p = (((uint32_t)config) >> 0) & 1; + + uint32_t trig_typ = NEORV32_XIRQ->TTYP; + uint32_t trig_pol = NEORV32_XIRQ->TPOL; + + trig_typ &= ~(1 << channel); // clear bit + trig_typ |= t << channel; + + trig_pol &= ~(1 << channel); // clear bit + trig_pol |= p << channel; + + NEORV32_XIRQ->TTYP = trig_typ; + NEORV32_XIRQ->TPOL = trig_pol; +} + + /**********************************************************************//** * Clear pending interrupt. * diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index 823ee49a9..b5489ceab 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -868,7 +868,7 @@ 0 - 0x10 + 0x14 registers @@ -888,6 +888,16 @@ IRQ source register 0x08 + + TTYP + IRQ trigger type (level/edge) + 0x0c + + + TPOL + IRQ trigger polarity (high/low, rising/falling) + 0x10 +