Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
sdbbs opened this issue Feb 14, 2025 · 3 comments
Closed

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

sdbbs opened this issue Feb 14, 2025 · 3 comments
Assignees
Milestone

Comments

@sdbbs
Copy link

sdbbs commented Feb 14, 2025

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 #1594 (comment) .

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?

@kilograham
Copy link
Contributor

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

@kilograham kilograham self-assigned this Feb 14, 2025
@kilograham kilograham added this to the 2.1.1 milestone Feb 14, 2025
@sdbbs
Copy link
Author

sdbbs commented Feb 14, 2025

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
Copy link
Contributor

fix merged into develop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants