gpio_set_irq_enabled in pico-sdk 2.1.0 hits assert on RP2040 #2266

Closed
opened 2025-02-14 08:44:33 -06:00 by sdbbs · 3 comments
sdbbs commented 2025-02-14 08:44:33 -06:00 (Migrated from github.com)

I have some RP2040 code which used to work fine in pico-sdk 1.4.0.

Now, I've upgraded to pico-sdk 2.1.0, and after making all the changes so the project builds again, I've stumbled upon quite a surprising problem - namely, somewhat randomly, the code fails to run after being flashed on the RP2040 MCU, because it hits an assert in gpio_set_irq_enabled.

I say that I hit the condition "somewhat randomly", because the first time I noticed this problem with the Debug .elf, I think I just reflashed the same .elf (or maybe I rebuilt the code, however without any code changes, can't really remember exactly), and then it started working. But then I rebuilt and reflashed Debug .elf again, and it started hitting the assert again; ever since, I cannot build a Debug .elf which works (does not hit the assert). However, I have built a Release .elf that does work.

I have seen gpio_set_irq_enabled() will not enable GPIO interrupt handling · Issue #1594 · raspberrypi/pico-sdk and tried to reorganize my setup code according to https://github.com/raspberrypi/pico-sdk/issues/1594#issuecomment-1888396046 .

My main() function in this project calls this function somewhere at start:

void prestart_irq_setup( void )
{
  irq_set_exclusive_handler( IO_IRQ_BANK0, on_gpio_edge_isr );
  irq_set_enabled( IO_IRQ_BANK0, true );
  gpio_set_irq_enabled( iopins.PIN_RX, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true );

  // disable the interrupt until it's needed
  irq_set_enabled( IO_IRQ_BANK0, false );
}

... where the function prototype for on_gpio_edge_isr is

__attribute__( ( interrupt( "IRQ" ) ) ) void __time_critical_func( on_gpio_edge_isr )( void );

I compile in a MINGW64 bash shell on Windows 10, with:

$ arm-none-eabi-gcc --version | head -1
arm-none-eabi-gcc.exe (GCC) 13.3.0

Here is a transcript of gdb-multiarch session, where the assert has been hit after flashing, and then (after program restart) stepping through the first three lines of prestart_irq_setup (some spacing added, and removed "target halted due ..." lines after p commands, for readability):

__breakpoint () at C:/src/pico-sdk/src/rp2040/pico_platform/include/pico/platform.h:126
126         pico_default_asm_volatile ("bkpt #0" : : : "memory");
(gdb) bt
#0  __breakpoint () at C:/src/pico-sdk/src/rp2040/pico_platform/include/pico/platform.h:126
#1  _exit (status=status@entry=1) at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45
#2  0x1000c894 in __assert_func (file=file@entry=0x10056dd8 "C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c",
    line=line@entry=190, func=func@entry=0x1005dc24 <__func__.4> "gpio_set_irq_enabled",
    failedexpr=failedexpr@entry=0x10056e18 "!enabled || (raw_irq_mask[get_core_num()] & (1ull<<gpio)) || callbacks[get_core_num()]")
    at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:168
#3  0x10009a84 in gpio_set_irq_enabled (gpio=<optimized out>, events=events@entry=12, enabled=enabled@entry=true)
    at C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c:190
#4  0x1000f110 in prestart_irq_setup () at C:/src/myproject/my_setup.c:430
#5  0x100008ea in main () at C:/src/myproject/main.c:607

(gdb) b prestart_irq_setup
Breakpoint 1 at 0x1000f0f0: file C:/src/myproject/my_setup.c, line 408.
Note: automatically using hardware breakpoints for read-only addresses.

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: C:\src\myproject\build\Debug\myproject.elf
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000184 msp: 0x20041f00
[New Thread 1]
[Switching to Thread 1]

Thread 2 hit Breakpoint 1, prestart_irq_setup () at C:/src/myproject/my_setup.c:408
427     {

(gdb) n
428       irq_set_exclusive_handler( IO_IRQ_BANK0, on_gpio_edge_isr );

(gdb) p raw_irq_mask[get_core_num()]
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000184 msp: 0x20041f00
$1 = 0
(gdb) p callbacks[get_core_num()]
$2 = (gpio_irq_callback_t) 0x0

(gdb) n
429       irq_set_enabled( IO_IRQ_BANK0, true );
(gdb) p raw_irq_mask[get_core_num()]
$3 = 0
(gdb) p callbacks[get_core_num()]
$4 = (gpio_irq_callback_t) 0x0

(gdb) n
430       gpio_set_irq_enabled( iopins.PIN_RX, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true );
(gdb) p raw_irq_mask[get_core_num()]
$5 = 0
(gdb) p callbacks[get_core_num()]
$6 = (gpio_irq_callback_t) 0x0

(gdb) p get_core_num()
$7 = 0

(gdb) n
Thread 2 received signal SIGTRAP, Trace/breakpoint trap.
_exit (status=status@entry=1) at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45
45              __breakpoint();

(gdb) bt
#0  _exit (status=status@entry=1) at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45
#1  0x1000c894 in __assert_func (file=file@entry=0x10056dd8 "C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c",
    line=line@entry=190, func=func@entry=0x1005dc24 <__func__.4> "gpio_set_irq_enabled",
    failedexpr=failedexpr@entry=0x10056e18 "!enabled || (raw_irq_mask[get_core_num()] & (1ull<<gpio)) || callbacks[get_core_num()]")
    at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:168
#2  0x10009a84 in gpio_set_irq_enabled (gpio=<optimized out>, events=events@entry=12, enabled=enabled@entry=true)
    at C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c:190
#3  0x1000f110 in prestart_irq_setup () at C:/src/myproject/my_setup.c:430
#4  0x100008ea in main () at C:/src/myproject/main.c:607

So, I guess, neither of irq_set_exclusive_handler or irq_set_enabled changes neither callbacks nor raw_irq_mask from their starting value of 0 for that core, which ends up triggering the assert in gpio_set_irq_enabled.

I've also tried using gpio_set_irq_callback( &on_gpio_edge_isr ); instead of irq_set_exclusive_handler, but it generates compiler warnings:

warning: passing argument 1 of 'gpio_set_irq_callback' from incompatible pointer type [-Wincompatible-pointer-types]
note: expected 'gpio_irq_callback_t' {aka 'void (*)(unsigned int,  long unsigned int)'} but argument is of type 'void (*)(void)'

,,, as it apparently expects a function with two int arguments instead of no (void) arguments; this skips the assert and allows the rest of my code to run, but on_gpio_edge_isr never fires (so overall, it does not work for me). Apparently I'm using gpio_set_irq_callback incorrectly, as I'd need a different prototype for on_gpio_edge_isr, but I haven't been able to find a proper example for that so far. But at least, it confirms that gpio_set_irq_callback does set either callbacks or raw_irq_mask, as the assert in gpio_set_irq_enabled is not hit.

What can I do, to have my code running again?

I have some RP2040 code which used to work fine in pico-sdk 1.4.0. Now, I've upgraded to pico-sdk 2.1.0, and after making all the changes so the project builds again, I've stumbled upon quite a surprising problem - namely, somewhat randomly, the code fails to run after being flashed on the RP2040 MCU, because it hits an assert in `gpio_set_irq_enabled`. I say that I hit the condition "somewhat randomly", because the first time I noticed this problem with the Debug .elf, I think I just reflashed the same .elf (or maybe I rebuilt the code, however without any code changes, can't really remember exactly), and then it started working. But then I rebuilt and reflashed Debug .elf again, and it started hitting the assert again; ever since, I cannot build a Debug .elf which works (does not hit the assert). However, I have built a Release .elf that does work. I have seen [gpio_set_irq_enabled() will not enable GPIO interrupt handling · Issue #1594 · raspberrypi/pico-sdk](https://github.com/raspberrypi/pico-sdk/issues/1594) and tried to reorganize my setup code according to https://github.com/raspberrypi/pico-sdk/issues/1594#issuecomment-1888396046 . My `main()` function in this project calls this function somewhere at start: ```c void prestart_irq_setup( void ) { irq_set_exclusive_handler( IO_IRQ_BANK0, on_gpio_edge_isr ); irq_set_enabled( IO_IRQ_BANK0, true ); gpio_set_irq_enabled( iopins.PIN_RX, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true ); // disable the interrupt until it's needed irq_set_enabled( IO_IRQ_BANK0, false ); } ``` ... where the function prototype for `on_gpio_edge_isr` is ```c __attribute__( ( interrupt( "IRQ" ) ) ) void __time_critical_func( on_gpio_edge_isr )( void ); ``` I compile in a MINGW64 `bash` shell on Windows 10, with: ``` $ arm-none-eabi-gcc --version | head -1 arm-none-eabi-gcc.exe (GCC) 13.3.0 ``` Here is a transcript of gdb-multiarch session, where the assert has been hit after flashing, and then (after program restart) stepping through the first three lines of `prestart_irq_setup` (some spacing added, and removed "target halted due ..." lines after `p` commands, for readability): ``` __breakpoint () at C:/src/pico-sdk/src/rp2040/pico_platform/include/pico/platform.h:126 126 pico_default_asm_volatile ("bkpt #0" : : : "memory"); (gdb) bt #0 __breakpoint () at C:/src/pico-sdk/src/rp2040/pico_platform/include/pico/platform.h:126 #1 _exit (status=status@entry=1) at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45 #2 0x1000c894 in __assert_func (file=file@entry=0x10056dd8 "C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c", line=line@entry=190, func=func@entry=0x1005dc24 <__func__.4> "gpio_set_irq_enabled", failedexpr=failedexpr@entry=0x10056e18 "!enabled || (raw_irq_mask[get_core_num()] & (1ull<<gpio)) || callbacks[get_core_num()]") at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:168 #3 0x10009a84 in gpio_set_irq_enabled (gpio=<optimized out>, events=events@entry=12, enabled=enabled@entry=true) at C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c:190 #4 0x1000f110 in prestart_irq_setup () at C:/src/myproject/my_setup.c:430 #5 0x100008ea in main () at C:/src/myproject/main.c:607 (gdb) b prestart_irq_setup Breakpoint 1 at 0x1000f0f0: file C:/src/myproject/my_setup.c, line 408. Note: automatically using hardware breakpoints for read-only addresses. (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: C:\src\myproject\build\Debug\myproject.elf target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x00000184 msp: 0x20041f00 [New Thread 1] [Switching to Thread 1] Thread 2 hit Breakpoint 1, prestart_irq_setup () at C:/src/myproject/my_setup.c:408 427 { (gdb) n 428 irq_set_exclusive_handler( IO_IRQ_BANK0, on_gpio_edge_isr ); (gdb) p raw_irq_mask[get_core_num()] target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x00000184 msp: 0x20041f00 $1 = 0 (gdb) p callbacks[get_core_num()] $2 = (gpio_irq_callback_t) 0x0 (gdb) n 429 irq_set_enabled( IO_IRQ_BANK0, true ); (gdb) p raw_irq_mask[get_core_num()] $3 = 0 (gdb) p callbacks[get_core_num()] $4 = (gpio_irq_callback_t) 0x0 (gdb) n 430 gpio_set_irq_enabled( iopins.PIN_RX, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true ); (gdb) p raw_irq_mask[get_core_num()] $5 = 0 (gdb) p callbacks[get_core_num()] $6 = (gpio_irq_callback_t) 0x0 (gdb) p get_core_num() $7 = 0 (gdb) n Thread 2 received signal SIGTRAP, Trace/breakpoint trap. _exit (status=status@entry=1) at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45 45 __breakpoint(); (gdb) bt #0 _exit (status=status@entry=1) at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45 #1 0x1000c894 in __assert_func (file=file@entry=0x10056dd8 "C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c", line=line@entry=190, func=func@entry=0x1005dc24 <__func__.4> "gpio_set_irq_enabled", failedexpr=failedexpr@entry=0x10056e18 "!enabled || (raw_irq_mask[get_core_num()] & (1ull<<gpio)) || callbacks[get_core_num()]") at C:/src/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:168 #2 0x10009a84 in gpio_set_irq_enabled (gpio=<optimized out>, events=events@entry=12, enabled=enabled@entry=true) at C:/src/pico-sdk/src/rp2_common/hardware_gpio/gpio.c:190 #3 0x1000f110 in prestart_irq_setup () at C:/src/myproject/my_setup.c:430 #4 0x100008ea in main () at C:/src/myproject/main.c:607 ``` So, I guess, neither of `irq_set_exclusive_handler` or `irq_set_enabled` changes neither `callbacks` nor `raw_irq_mask` from their starting value of 0 for that core, which ends up triggering the assert in `gpio_set_irq_enabled`. I've also tried using `gpio_set_irq_callback( &on_gpio_edge_isr );` instead of `irq_set_exclusive_handler`, but it generates compiler warnings: ``` warning: passing argument 1 of 'gpio_set_irq_callback' from incompatible pointer type [-Wincompatible-pointer-types] note: expected 'gpio_irq_callback_t' {aka 'void (*)(unsigned int, long unsigned int)'} but argument is of type 'void (*)(void)' ``` ,,, as it apparently expects a function with two int arguments instead of no (void) arguments; this skips the assert and allows the rest of my code to run, but `on_gpio_edge_isr` never fires (so overall, it does not work for me). Apparently I'm using `gpio_set_irq_callback` incorrectly, as I'd need a different prototype for `on_gpio_edge_isr`, but I haven't been able to find a proper example for that so far. But at least, it confirms that `gpio_set_irq_callback` *does* set either `callbacks` or `raw_irq_mask`, as the assert in `gpio_set_irq_enabled` is not hit. What can I do, to have my code running again?
kilograham commented 2025-02-14 09:25:32 -06:00 (Migrated from github.com)

ah, the assertion (which is trying to be helpful) assumes you have used some of the hardware_gpio functionality to set up the IRQ handler (either via gpio_set_irq_callback or gpio_add_raw_irq_handler... it should probably be relaxed

ah, the assertion (which is trying to be helpful) assumes you have used some of the `hardware_gpio` functionality to set up the IRQ handler (either via `gpio_set_irq_callback` or `gpio_add_raw_irq_handler`... it should probably be relaxed
sdbbs commented 2025-02-14 10:09:50 -06:00 (Migrated from github.com)

Many thanks, @kilograham

ah, the assertion (which is trying to be helpful) assumes you have used some of the hardware_gpio functionality to set up the IRQ handler (either via gpio_set_irq_callback or gpio_add_raw_irq_handler... it should probably be relaxed

Great - thanks for the hint: I tried gpio_add_raw_irq_handler( iopins.PIN_RX, on_gpio_edge_isr ); instead of irq_set_exclusive_handler( IO_IRQ_BANK0, on_gpio_edge_isr ); and things seem to work fine again; although if the assert gets relaxed in 2.1.1, I'd definitely try to go back to irq_set_exclusive_handler, just to keep changes in respect to older versions of my code minimal.

Many thanks, @kilograham > ah, the assertion (which is trying to be helpful) assumes you have used some of the `hardware_gpio` functionality to set up the IRQ handler (either via `gpio_set_irq_callback` or `gpio_add_raw_irq_handler`... it should probably be relaxed Great - thanks for the hint: I tried `gpio_add_raw_irq_handler( iopins.PIN_RX, on_gpio_edge_isr );` instead of `irq_set_exclusive_handler( IO_IRQ_BANK0, on_gpio_edge_isr );` and things seem to work fine again; although if the assert gets relaxed in 2.1.1, I'd definitely try to go back to `irq_set_exclusive_handler`, just to keep changes in respect to older versions of my code minimal.
kilograham commented 2025-02-17 16:07:44 -06:00 (Migrated from github.com)

fix merged into develop

fix merged into develop
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-raspberrypi/pico-sdk#2266
No description provided.