raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.24k stars 837 forks source link

FreeRTOS SMP: Unable to call cyw43_arch_init outside of FreeRTOS task (intentional?) #1540

Open gemarcano opened 7 months ago

gemarcano commented 7 months ago

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?)?

gemarcano commented 7 months ago

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.

peterharperuk commented 7 months ago

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?

gemarcano commented 7 months ago

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.