pico-sdk
pico-sdk copied to clipboard
FreeRTOS SMP: Unable to call cyw43_arch_init outside of FreeRTOS task (intentional?)
I am using upstream FreeRTOS with the SMP work merged into main (and using PR #1530 to fix the renaming of a FreeRTOS config).
I've successfully managed to convert the NTP client example Pico W example from its original LwIP raw API form to one using NO_SYS=0 FreeRTOS SMP and the LwIP socket API. In the process, I discovered that if I tried calling cyw43_arch_init from main(), the pico would hard crash with no output whatsoever. It worked fine if called from a FreeRTOS task.
I rigged up a second pico as a debug probe, and found a hard_assert that was failing in async_context_freertos_execute_sync. Specifically:
hard_assert(xSemaphoreGetMutexHolder(self->lock_mutex) != xTaskGetCurrentTaskHandle());
When called from main() or outside of a FreeRTOS task, xTaskGetCurrentTaskHandle() returns 0, and in general I'm seeing xSemaphoreGetMutexHolder(self->lock_mutex) return 0 during initialization (as apparently no tasks have claimed the mutex). This clearly leads to the hard_assert to fire when calling async_context_freertos_execute_sync outside of a FreeRTOS task.
Is this intentional? I'm also not 100% sure what that hard_assert is checking. Is it to catch some sort of recursive calling/deadlock (although I don't see that mutex being claimed during init-- maybe it is in other execution paths?)?
After some more experimentation, the implementation of async_context_freertos_* functions, which are called by cyw43_arch_init, assume that they are taking place within the context of an already running FreeRTOS. For example, async_context_freertos_execute_sync calls xQueueSemaphoreTake near its end, and that in turn calls vTaskPlaceOnEventList, which implicitly assumes the current thread is running within FreeRTOS.
Per the documentation on async_context_execute_sync I think I understand what the hard_assert is checking against-- it is very clear in the documentation that the context's lock should not be held by whoever is calling execute_sync, so it's just checking against that.
So it does look like this behavior is intentional-- it might be a good idea to document that when using the FreeRTOS async_context, cyw43* functions should be called within a FreeRTOS task.
edit: and possibly also async_context_freertos functions.
Yes, you're probably right - it needs to be documented.
I've successfully managed to convert the NTP client example Pico W example from its original LwIP raw API form to one using NO_SYS=0 FreeRTOS SMP and the LwIP socket API.
I was thinking the other day that we really need more examples of this. Any chance of contributing this to pico-examples?
Sure, I've submitted a draft example based on my code. My own personal code was all in C++, so it took a bit but I managed to get it back to C.