Description
This problem was discussed on the SDK forum here, where @kilograham thought it sounds like a bug.
The issue arises with single-core FreeRTOS (configNUMBER_OF_CORES=1
), starting the task scheduler on core0, and an ISR running on core1. The same code was also tested with threadsafe background mode, poll mode, and SMP FreeRTOS, and the problem doesn't happen in any of those versions.
On core1, the ADC is set to free-running mode, and this ISR is initiated to trigger on ADC_IRQ_FIFO
. It saves the value from adc_fifo_get()
in a static variable:
static volatile uint16_t temp_adc_raw;
static volatile unsigned long isr_runs = 0;
static volatile absolute_time_t last_isr_t;
static void
__time_critical_func(temp_isr)(void)
{
uint16_t val = adc_fifo_get();
critical_section_enter_blocking(&temp_critsec);
temp_adc_raw = val;
critical_section_exit(&temp_critsec);
isr_runs++;
last_isr_t = get_absolute_time();
}
isr_runs
and last_isr_t
are debugging variables, to count the number of ISR runs and the time of the last run. The critical section is initialized in main()
.
On core0, the FreeRTOS task scheduler runs a task that gets a WiFi connection and initializes lwIP callbacks. The lwIP calls are then managed by the task scheduler. The app uses this function to retrieve the latest ADC value, called from the lwIP callbacks on core0:
uint32_t
get_temp(void)
{
uint16_t raw;
critical_section_enter_blocking(&temp_critsec);
raw = temp_adc_raw;
critical_section_exit(&temp_critsec);
printf("isr_runs=%u last_isr_t=%llu\n", isr_runs,
to_us_since_boot(last_isr_t));
/* ... do some computation and return a value ... */
}
The printf output shows how often the ISR ran, and when it ran last in µs since boot.
In the problematic version of the app, when a network client makes a request and the lwIP callback calls get_temp()
, the output shows that the ISR ran a few thousand times, then stopped:
isr_runs=12133 last_isr_t=5549347
These values never increase. The effect is that the network client gets back the value that was saved on the last ISR run, rather than continuously updated values derived from the ADC.
When I run the app in the other modes (threadsafe background, poll, and SMP FreeRTOS), the debug outputs always increase, and the network client gets the most recent value from the ADC.
This has been tested with SDK 1.5.1 and both of FreeRTOS V11.1.0 (current latest) and V10.6.2, which was the last version before the smp branch was merged to main. The same thing happened with both FreeRTOS versions.
In the same app, I have an async_context
at-time worker that is also started on core1. It does the intercore synchronization in the same way as with the ISR -- the recurring worker function saves a static value, and the lwIP calls from core0 retrieve it, protected by a critical section. The network client always gets an updated value, including in the single-core FreeRTOS version. So although the ISR has stopped, the at-time worker continues running.
There is evidence that the ISR may have stopped around the time that the WiFi connection reaches the linkup state (which is done by the initializer task run by FreeRTOS on core0). I'm also saving a timestamp right after cyw43_tcpip_link_status()
returns CYW43_LINK_UP
, whose us_since_boot value is shown here as t
:
t=5549470
[...]
isr_runs=12133 last_isr_t=5549347
The difference between the LINK_UP
timestamp and the last ISR timestamp was 123µs in this case. The deltas I've seen here are usually in the 100s of µs range, always <= 1ms.
So to summarize:
- An ISR triggered on
ADC_IRQ_FIFO
for a free-running ADC stops for some reason after a few thousand runs. - This happens when the app runs with single-core FreeRTOS, but not with the non-FreeRTOS choices for cyw43_arch, and not with SMP FreeRTOS.
- The ISR is stopped at a time that is apparently close to the time when the WiFi connection, executed in a FreeRTOS task, reaches the linkup state.