radio_esp32::tasks
_task_create, _task_create_pinned_to_core and _task_delete — the
blob’s own threads.
Three entries, and the reason esp_wifi_init_internal could not return.
The Wi-Fi driver creates wifiT before it does anything else, so with
these null the first call into the blob jumped to address zero — which is
what EXCCAUSE=0x14 epc=0x00000000 was saying all along.
The mismatch, and the trampolines
Section titled “The mismatch, and the trampolines”Same shape as [crate::interrupts], for the same reason. FreeRTOS’s task
entry is void (*)(void *param); kernel::dynobj::spawn_task takes a
plain fn(), which carries no environment, and a closure cannot bridge
that. What can is a distinct function per slot, each knowing its own slot
number at compile time — TRAMPOLINES is [SLOTS] different fn()
values, each of which looks up its slot and calls what the blob asked for
with the argument the blob supplied.
[SLOTS] is the ceiling on how many blob tasks may exist at once. It is a
fixed array rather than a growable one because the trampolines have to be
written out by hand — there is no way to generate fn() items at runtime —
and because the number of tasks the driver creates is a property of the
driver, not of the workload.
Handles are ids plus one
Section titled “Handles are ids plus one”FreeRTOS’s TaskHandle_t is a TCB pointer, and a null handle means “the
calling task” — vTaskDelete(NULL) deletes yourself. FlintOS’s task ids
start at zero, so handing an id out raw would make task 0 indistinguishable
from “myself”, and the blob would delete the wrong thread exactly once, at
a point far from here.
So a handle is id + 1. [handle_from_id] and [id_from_handle] are the
only two places that know it, and _task_get_current_task uses them too —
the blob compares the handle it was given against the one it stored, and
two encodings would never match.
Stack sizes are bytes
Section titled “Stack sizes are bytes”Vanilla FreeRTOS counts usStackDepth in words. The ESP-IDF port counts
it in bytes, and the blob is built against the ESP-IDF port: NuttX passes
stack_depth straight to kthread_create, which is bytes. Treating it as
words would give the Wi-Fi task four times the stack it asked for, and
kernel::spawn would reject it against MAX_STACK_SIZE — a failure that
reads like “out of memory” rather than like a units bug.
Structs
Section titled “Structs”Created
Section titled “Created”struct Created { pub core_id: u32, pub prio: u32, pub stack: u32, pub pinned_to: Option<u8>, pub slot: u8, pub spawned: bool,}One _task_create request, as it arrived and as it was resolved.
Recorded rather than logged: this runs on the blob’s init path, and logging from inside the blob has hung the board before. The application reads it back afterwards.
The question it exists to answer: does the driver ever ask for the
second core? That core is not started in this application
(soc_esp32::appcpu::is_running() is false, measured), and
[create] maps a request for it onto Affinity::Core(1) because
MAX_CORES is 2 — a task pinned to a core that never runs is created
successfully, reports success to the blob, and then sits there. That would
explain both the init hang and the receiver never being switched on, and
it is entirely unconfirmed until one of these records shows core_id of 1.
Implements: Clone, Copy
Functions
Section titled “Functions”create
Section titled “create”unsafe fn create(entry: *mut core::ffi::c_void, name: *const core::ffi::c_char, stack_depth: u32, param: *mut core::ffi::c_void, prio: u32, task_handle: *mut core::ffi::c_void, core_id: u32) -> i32_task_create_pinned_to_core(entry, name, stack_depth, param, prio, task_handle, core_id).
task_handle is an out-parameter, and may be null when the caller does not
want one back. core_id is FreeRTOS’s tskNO_AFFINITY (0xFFFFFFFF) or
any out-of-range value when the task may float, which is how
[create_anywhere] is written in terms of this.
Returns pdTRUE/pdFALSE, not an id — the id goes through task_handle.
Safety
Section titled “Safety”entry is called on a new task with param. name is a C string.
task_handle, if non-null, is written with one TaskHandle_t.
create_anywhere
Section titled “create_anywhere”unsafe fn create_anywhere(entry: *mut core::ffi::c_void, name: *const core::ffi::c_char, stack_depth: u32, param: *mut core::ffi::c_void, prio: u32, task_handle: *mut core::ffi::c_void) -> i32_task_create(entry, name, stack_depth, param, prio, task_handle).
esp-idf’s is xTaskCreate, which is xTaskCreatePinnedToCore with
tskNO_AFFINITY. Written the same way here rather than duplicated.
Safety
Section titled “Safety”As [create].
delete
Section titled “delete”unsafe fn delete(handle: *mut core::ffi::c_void)_task_delete(handle). A null handle deletes the calling task.
The slot is released before the task is, because after delete_task the id
belongs to the allocator again and a later create could be handed it — at
which point a slot still claiming that id would be freed out from under a
live task.
Safety
Section titled “Safety”handle is one this module handed out, or null. Called by the blob.
for_each_create
Section titled “for_each_create”fn for_each_create<impl FnMut(&Created)>(f: impl FnMut)Every task the blob has asked for, in order.
handle_from_id
Section titled “handle_from_id”fn handle_from_id(id: u32) -> *mut core::ffi::c_voidA FlintOS task id as the blob’s TaskHandle_t.
id_from_handle
Section titled “id_from_handle”fn id_from_handle(handle: *mut core::ffi::c_void) -> Option<u32>The blob’s TaskHandle_t as a FlintOS task id, or None for null — which
FreeRTOS defines as “the calling task”.
set_start_delay_ms
Section titled “set_start_delay_ms”fn set_start_delay_ms(ms: u32)Set the injected delay. See START_DELAY_MS.
slot_counts
Section titled “slot_counts”fn slot_counts(n: usize) -> (u32, u32)Entered and exited counts for slot n.
Constants
Section titled “Constants”const SLOTS: usize;How many blob tasks may exist at once.
The Wi-Fi driver creates one (wifiT); the timer and event machinery adds
a couple more, and BLE will want its own when #66 lands. Eight is room for
all of that with the array still small enough to write out below.