feat: add lab-rv32i-freertos-task-wrapper card

This commit is contained in:
user
2026-07-21 19:14:20 +02:00
commit 50c425c88e
65 changed files with 33756 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
# K07 — TaskHandle_t, explicit start and static trampoline
## Position
- Series: FreeRTOS C++
- Lesson: L06, card K07
- Duration: 30 minutes
- One C++ owner object plus one raw verifier task
- Dynamic FreeRTOS TCB/stack from real `heap_4.c`
K02 introduced CRTP in a larger first-task experiment. K07 revisits the task
boundary after the memory/resource cards and demands a precise identity proof:
object, TCB/handle and task stack are separate things with separate lifetimes.
## Outcome
After 30 minutes the student can annotate every argument of `xTaskCreate`,
explain why construction and start are separate, trace `void*` back to a typed
member body, and prove stable object address without virtual dispatch.
## Lesson plan — 30 minutes
| Time | Mode | Evidence |
| --- | --- | --- |
| 05 | predict addresses | object vs handle/TCB vs task stack |
| 510 | explicit start | constructor heap delta 0; start creates kernel objects |
| 1016 | C boundary | annotate entry, name, words, context, priority, handle-out |
| 1621 | trampoline | raw context equals object; static cast; private `run()` |
| 2126 | lifecycle | ready/running/completed, handle cleared, self-delete |
| 2630 | GDB/exit | stack local inside bounds; no move/vtable/destructor delete |
## Stable identity contract
```text
object_before == object_after_start
== xTaskCreate context
== trampoline context
== this in run
== completed object
handle_after_start == current handle == TCB address
handle_after_start != C++ object address
local variable address lies in task stack bounds
```
## Why explicit start
The derived object is not complete while its base constructor runs. Starting
there could let the scheduler enter `run()` against an incompletely constructed
object. `start()` is a separate state transition invoked only after the complete
owner exists. Duplicate start is rejected without another `xTaskCreate` call.
## Wrapper contract
- constructor stores name, stack words and priority only;
- `start()` moves `constructed -> starting -> ready` and calls the C API;
- static trampoline sets `running`, invokes `Derived::run()`, then records
`completed`, clears the handle and deletes the current kernel task;
- copy and move are deleted because the context address must not change;
- no destructor force-deletes a live task.
## Acceptance
- host test proves no constructor create call, one explicit create, stable
context, duplicate rejection and `start_failed` behavior;
- ABI contains the static trampoline but no vtable/RTTI/hosted runtime;
- Hazard3 shows object outside `ucHeap`, TCB in `ucHeap`, stack-local inside
reported task stack bounds;
- counter result is 500500, wrapper ends completed with null handle;
- all identity equalities and checkpoint 12 PASS hold.
## Main traps
1. `TaskHandle_t` is not the C++ object pointer; it identifies the TCB.
2. Stack depth is in `StackType_t` words, not necessarily bytes.
3. Moving a started wrapper invalidates the kernel's stored context.
4. Starting in a constructor exposes a partial object.
5. Hiding forced deletion in a destructor does not unwind arbitrary task-local
C++ objects and creates unsafe asynchronous lifetime coupling.