-
Hi Stephan, I have a task where I want to use the external interrupt module (XIRQ) with Neorv32 for edge detection on signals, but I have been testing with a simple push button to get the code to work. I have been having some issues with what seems like clearing the interrupt request. Note that I am using an old version of Neorv32 and not using the RTE (I will expand the trap handler code in this code once interrupts are working)! In hardware I have enabled the LSB of the 32 XIRQ inputs as edge triggered on rising edges as shown below: library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity top_level is
Port (clk_i : in std_logic;
rst_i : in std_logic;
uart_rx_i : in std_logic;
uart_tx_o : out std_logic;
JB1_i : in std_logic
);
end top_level;
architecture Behavioral of top_level is
attribute keep: boolean;
signal rst_debounced : std_logic;
signal rst_n : std_logic;
signal chA_ff_1 : std_logic;
signal chA_ff_2 : std_logic;
signal chA_ff_3 : std_logic;
signal xirq_lines : std_ulogic_vector(31 downto 0); -- IRQ channels
--signal gpio_i_top : std_ulogic_vector(63 downto 0);
--attribute keep of gpio_i_top: signal is true;
--signal gpio_o_top_ulv : std_ulogic_vector(63 downto 0);
--signal gpio_o_top : std_logic_vector(63 downto 0);
component neorv32_ProcessorTop_Minimal is
generic (
-- General --
CLOCK_FREQUENCY : natural := 100_000_000; -- clock frequency of clk_i in Hz
-- Internal Instruction memory --
MEM_INT_IMEM_EN : boolean := true; -- implement processor-internal instruction memory
MEM_INT_IMEM_SIZE : natural := 16*1024; -- size of processor-internal instruction memory in bytes
INT_BOOTLOADER_EN : boolean := true;
-- Internal Data memory --
MEM_INT_DMEM_EN : boolean := true; -- implement processor-internal data memory
MEM_INT_DMEM_SIZE : natural := 16*1024; -- size of processor-internal data memory in bytes
REGFILE_HW_RST : boolean := true;
IO_GPIO_NUM : natural := 64;
IO_PWM_NUM_CH : natural := 1;
IO_UART0_EN : boolean := true;
IO_GPTMR_EN : boolean := false;
XIRQ_NUM_CH : natural := 1; -- number of external IRQ channels (0..32)
XIRQ_TRIGGER_TYPE : std_ulogic_vector(31 downto 0) := x"00000001"; -- trigger type: 0=level, 1=edge
XIRQ_TRIGGER_POLARITY : std_ulogic_vector(31 downto 0) := x"00000001" -- trigger polarity: 0=low-level/falling-edge, 1=high-level/rising-edge
);
port (
-- Global control --
clk_i : in std_logic;
rstn_i : in std_logic;
gpio_i : in std_ulogic_vector(63 downto 0) := (others => 'L'); -- parallel input
gpio_o : out std_ulogic_vector(63 downto 0); -- parallel output
pwm_o : out std_ulogic_vector(11 downto 0); -- pwm channels
uart0_txd_o : out std_ulogic; -- UART0 send data
uart0_rxd_i : in std_ulogic := '0'; -- UART0 receive data
xirq_i : in std_ulogic_vector(31 downto 0) -- IRQ channels
);
end component;
component input_debouncer is
Generic (g_clk_freq : natural;
g_debounce_time_ms : natural;
g_counter_bit_width : natural);
Port (clk_i : in std_logic;
bit_i : in std_logic;
bit_o : out std_logic
);
end component;
begin
--- debouncing, inverting and registering the reset button ----------------------------------------------------
reset_button_debouncer : input_debouncer
generic map (g_clk_freq => 100_000_000,
g_debounce_time_ms => 50,
g_counter_bit_width => 24)
port map (clk_i => clk_i,
bit_i => rst_i,
bit_o => rst_debounced
);
rst_n <= not rst_debounced when rising_edge(clk_i);
---------------------------------------------------------------------------------------------------------------
--- registering ----------------------------------------------------------------------------------------------
Process(clk_i)
begin
if (rising_edge(clk_i)) then
chA_ff_1 <= JB1_i;
chA_ff_2 <= chA_ff_1;
chA_ff_3 <= chA_ff_2;
end if;
end Process;
xirq_lines(0) <= chA_ff_3;
xirq_lines(31 downto 1) <= (others => '0');
---------------------------------------------------------------------------------------------------------------
--- neorv32 component instantiation ----------------------------------------------------------------------
neorv32_inst : neorv32_ProcessorTop_Minimal
port map (clk_i => clk_i,
rstn_i => rst_n,
gpio_i => (others => '0'),
gpio_o => open,
pwm_o => open,
uart0_txd_o => uart_tx_o,
uart0_rxd_i => uart_rx_i,
xirq_i => xirq_lines
);
--gpio_o_top <= to_stdlogicvector(gpio_o_top_ulv);
---------------------------------------------------------------------------------------------------------------
end Behavioral; The C software is shown below: #include <neorv32.h>
#define UART_BAUD_RATE 19200
void global_trap_handler(void);
int err_cnt = 0;
int main() {
neorv32_uart0_setup(UART_BAUD_RATE, 0);
neorv32_cpu_csr_write(CSR_MTVEC, (uint32_t)&global_trap_handler);
err_cnt = neorv32_xirq_setup();
if (err_cnt) {
neorv32_uart0_printf("Error during XIRQ setup!\n");
return 1;
}
err_cnt = neorv32_xirq_install(0, global_trap_handler);
if (err_cnt) {
neorv32_uart0_printf("Error during XIRQ install!\n");
return 1;
}
// Enable interrupts
neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MEIE);
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE);
neorv32_xirq_channel_enable(0);
neorv32_xirq_global_enable();
neorv32_uart0_printf("XIRQ(0) enabled, waiting for interrupt...\n");
while (1) {
neorv32_cpu_sleep();
}
return 0;
}
void __attribute__((interrupt)) global_trap_handler(void) {
uint32_t mcause = neorv32_cpu_csr_read(CSR_MCAUSE);
neorv32_uart0_printf("mcause: 0x%x\n", mcause);
uint32_t mip = neorv32_cpu_csr_read(CSR_MIP);
neorv32_uart0_printf("MIP at trap entry: 0x%x\n", mip);
if (mcause == 0x80000018) {
neorv32_uart0_printf("XIRQ(0) interrupt\n");
neorv32_xirq_clear_pending(0);
}
else {
neorv32_uart0_printf("ELSE\n");
}
mip = neorv32_cpu_csr_read(CSR_MIP);
neorv32_uart0_printf("MIP at trap exit: 0x%x\n", mip);
} Ok so when I run this code it first prints this message and waits for the interrupt:
After the button press, the print out looks like this:
So it seems obvious that the interrupt is not getting cleared somehow using the function The mcause every time the trap is triggered is the trap code for the XIRQ which seems correct, but the Any suggestions? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
Hey @DS-567! First of all, which version of the core (hardware) are you using? There was a rather large rework of the XIRQ controller some time ago (#1071)...
I think you are right - the code clearing the pending XIRQ is just missing here.
Correct.
You could add the XIRQ-clearing code to your custom global trap handler. Depending on the version of the core you are using, this could do the job: neorv32/sw/lib/source/neorv32_xirq.c Lines 264 to 268 in 4e4e0e0 |
Beta Was this translation helpful? Give feedback.
The version is hardcoded in the main VHDL package file:
neorv32/rtl/core/neorv32_package.vhd
Line 32 in 457f902
All versions are listed in the project's Changelog.
Oh OK, this is a rather old version... How about an upgrade? 😅
Seems like I have forgotten to post the last (and most important) line of code... Here is the full XIRQ handler corresponding to your version:
neorv32/sw/lib…