20 KiB
FreeRTOS C — book-led card sequence
Status: FC01–FC15 istnieją; blok 15 kart FreeRTOS C jest kompletny i stanowi zamknięty fundament przed serią FreeRTOS C++. Każda karta używa publicznego API C, a końcowa FC15 scala mechanizmy w jeden deterministyczny eksperyment.
Primary source and ordering rule
The content map follows the official FreeRTOS Kernel Book:
- https://github.com/FreeRTOS/FreeRTOS-Kernel-Book
- https://github.com/FreeRTOS/FreeRTOS-Kernel-Book/blob/main/toc.md
The book supplies the concepts and API order. Each card adds the local Hazard3/RV32I observation contract: stable checkpoints, source and ELF identity, a debugging strategy and a timestamped runtime proof. We do not copy the book examples verbatim; every program is a small deterministic experiment suitable for replay.
FreeRTOS Kernel Book
├── Ch. 3 Heap memory management → FC01, revisited in FC11
├── Ch. 4 Task management → FC02–FC04
├── Ch. 5 Queue management → FC05
├── Ch. 6 Software timer management → FC06
├── Ch. 7 Interrupt management → FC07, FC13–FC14
├── Ch. 8 Resource management → FC08
├── Ch. 9 Event groups → FC09
├── Ch. 10 Task notifications → FC10, FC13
├── Ch. 11 Low-power support → optional extension after FC15
├── Ch. 12 Developer support → FC12
└── Ch. 13 Troubleshooting → FC12 and every acceptance gate
FC11 deliberately revisits static allocation after the student has already seen tasks, queues and timers. The book introduces it in Chapter 3; the later placement lets one card compare real dynamic and static forms of several kernel objects without teaching APIs that have not appeared yet.
A1–A8 viewpoint policy
A1–A8 is a set of questions, not a requirement to draw eight decorative diagrams. A view is enabled only when it adds a distinct proof.
A1 CONTEXT system boundary and responsibility
A2 STRUCTURE C structs, handles, kernel objects and ownership
A3 DISPATCH callback, ISR or other binding boundary
A4 APPLICATION concrete task/object topology and invariants
A5 FLOW ordered execution and checkpoints
A6 STATE task/object state and lifetime
A7 RUNTIME addresses, TCB, stack, registers, tick and ELF evidence
A8 PATTERNS named reusable design decision, only when real
Every enabled diagram is interactive. A CODE step opens a stable source or artifact reference without running the target. A RUN step replays from clean RAM to a verified checkpoint. Moving the cursor alone never mutates the simulator.
Curated sequence
FreeRTOS C — 15 cards
├── FC01 · heap_4: from a linear cursor to reusable blocks
├── FC02 · First task, typed context, TCB and task stack
├── FC03 · Tick, priorities, preemption and time slicing
├── FC04 · Delay, blocking, task states and deletion
├── FC05 · Queues, blocking and byte-copy semantics
├── FC06 · Software timers and the daemon task
├── FC07 · Interrupt-safe API and semaphore handoff
├── FC08 · Critical sections, mutexes and priority inheritance
├── FC09 · Event groups and multi-event synchronization
├── FC10 · Direct-to-task notifications
├── FC11 · Static allocation and bounded kernel storage
├── FC12 · Assertions, hooks, stack checks and runtime diagnostics
├── FC13 · UART interrupt-to-task pipeline
├── FC14 · GPIO, hardware timer and deferred work
└── FC15 · Integrated deterministic FreeRTOS C application
FC01 — heap_4: from a linear cursor to reusable blocks
Duration: one 30-minute lesson.
Direct predecessor: C07, K&R 5.4 Task04 — allocbuf[64], allocp,
alloc(5), alloc(7) and cursor offsets 0 -> 5 -> 12.
Book basis: the heap_4 and heap-statistics parts of Chapter 3. The other
FreeRTOS heap implementations are a one-minute map of alternatives, not
parallel lesson content.
- Opening problem: the linear project cannot release the first 5-byte area while preserving the later 7-byte area, cannot remember reusable holes and recovers the arena only with a whole-arena reset.
- New model: a block header, an address-ordered free list, first-fit selection, splitting and coalescing of physically adjacent free blocks.
- Raw API:
pvPortMalloc,vPortFree,xPortGetFreeHeapSize,xPortGetMinimumEverFreeHeapSize,vPortGetHeapStats. - Task 1 and Task 2 are short teacher-led models with a student prediction; they are not separate build/debug sessions during the lesson.
- Task 3 is the only central student replay: real upstream
heap_4.c, two stable checkpoints, fragmentation metrics, recovery and OOM. - Pico 2 W supplies one prebuilt RP2350 hardware observation.
heap_4is the allocator selected and linked by the project, not a hardware feature of the microcontroller. - Acceptance: the student names the three additions over the linear allocator
(header, free list, coalescing), predicts
2 free blocks -> 1 free block, and distinguishes current free bytes, largest free block and minimum-ever free bytes. - Main pitfall: assuming that a large sum of free bytes guarantees one large
allocation, or that minimum-ever-free increases after
free.
FC02 — First task, typed context, TCB and task stack
Book basis: 4.2–4.6 and the creation/parameter parts of Examples 4.1–4.2.
- Raw API:
xTaskCreate,vTaskStartScheduler,vTaskDelete,uxTaskGetStackHighWaterMark. - Main task: two equal-priority workers compute
sum(1..20000)throughvoid *pvParameters; a lower-priority supervisor verifies both results. - Enabled views: A1, A2, A3, A5, A6 and A7.
- Exercise: add a third typed context without introducing global task data.
- Hazard3/GDB proof: callback-entry
a0, context, opaque handle/TCB, task stack and A→B→A execution trace are distinct and consistent. - Main pitfall: confusing
&handle, the handle value, TCB, context and stack.
Detailed plan: FC02 — A1–A8 raw C task.
FC03 — Tick, priorities, preemption and time slicing
Book basis: 4.5, 4.6, 4.9 and 4.12, especially prioritized preemptive scheduling, runtime priority changes and time slicing.
- Raw API/config:
uxTaskPriorityGet,vTaskPrioritySet,xTaskGetTickCount,configUSE_PREEMPTION,configUSE_TIME_SLICING. - Main task: two equal-priority peers establish A→B→A; one peer raises a dormant probe from priority 0 to 3, which must preempt before the peer's next committed write; a priority-1 verifier runs only after higher-priority work ends.
- Blocks: A1 tick/scheduler boundary; A2 task/priority/trace data; A4 experiment topology; A5 two-part scheduling flow; A6 Ready/Running lanes; A7 timer interrupt, current TCB, PC/SP and trace evidence.
- Exercise: run a bounded expected-noncompletion observation with time slicing disabled, then explain why a CPU-bound peer can starve its equal-priority neighbor.
- Hazard3/GDB proof: nondecreasing ticks, A→B→A, immediate high-priority takeover and delayed low-priority verifier.
- Main pitfall: interpreting priority as fairness or treating a tick as a guarantee that every Ready task ran.
Detailed plan: FC03 — scheduler experiment.
FC04 — Delay, blocking, task states and deletion
Book basis: 4.7–4.10 and Examples 4.4–4.8.
- Raw API:
vTaskDelay,vTaskDelayUntil,eTaskGetState,vTaskSuspend,vTaskResume,vTaskDelete. - Main task: periodic and non-periodic tasks cross Ready, Running, Blocked, Suspended and Deleted states on a controlled tick timeline.
- Blocks: A2 state-bearing contexts/handles; A5 delay and resume sequence; A6 canonical task-state machine; A7 wake tick, PC/SP, TCB and idle cleanup.
- Exercise: replace relative delay with
vTaskDelayUntiland quantify drift. - Hazard3/GDB proof: actual wake ticks and public
eTaskStateobservations match the selected state-machine edges. - Main pitfall: busy waiting instead of blocking, or assuming
vTaskDeleteimmediately erases every forensic trace.
FC05 — Queues, blocking and byte-copy semantics
Book basis: Chapter 5, with emphasis on 5.2–5.5 and Examples 5.1–5.2.
- Raw API:
xQueueCreate,xQueueSendToBack,xQueueSendToFront,xQueueReceive,uxQueueMessagesWaiting,xQueuePeek. - Main task: two producers send typed messages by value to one consumer; a full and an empty queue deliberately block the correct task.
- Blocks: A1 producer/kernel/consumer boundary; A2 queue item/ring/storage; A4 producer-consumer topology; A5 send/copy/block/wake/receive; A6 queue and task states; A7 source address versus copied queue slot and destination.
- Exercise: compare a value queue with a queue of pointers and write the ownership contract for the latter.
- Hazard3/GDB proof: changing the producer buffer after send does not change the queued value; waiting counts and task transitions are correct.
- Main pitfall: assuming queues hold references, or queuing a pointer without an explicit lifetime owner.
FC06 — Software timers and the daemon task
Book basis: Chapter 6 and Examples 6.1–6.3.
- Raw API:
xTimerCreate,xTimerStart,xTimerChangePeriod,xTimerReset,pvTimerGetTimerID. - Main task: one-shot and auto-reload timers share a callback but use distinct IDs; task code resets one timer and changes the other's period.
- Blocks: A1 application/timer service/kernel boundary; A2 timer objects, IDs and command queue; A3 callback dispatch; A5 command→daemon→callback; A6 dormant/active/expired states; A7 daemon task, callback PC and tick.
- Exercise: derive the callback order for a changed period and verify it.
- Hazard3/GDB proof: callback context is the timer daemon task, not the task
that issued
xTimerStart. - Main pitfall: blocking inside a timer callback or assuming timer commands execute synchronously in the caller.
FC07 — Interrupt-safe API and semaphore handoff
Book basis: Chapter 7, especially 7.2–7.5 and queue/ISR considerations.
- Raw API:
xSemaphoreCreateBinary,xSemaphoreCreateCounting,xSemaphoreGiveFromISR,xSemaphoreTake,portYIELD_FROM_ISR. - Main task: a deterministic simulated interrupt gives a semaphore, sets
xHigherPriorityTaskWoken, and transfers execution to a waiting task. - Blocks: A1 ISR/task/kernel boundary; A2 semaphore and ISR evidence; A3
FromISR dispatch/yield contract; A5 interrupt→unblock→yield→task flow; A6
waiting/ready/running; A7 trap frame,
mcause, PC/SP and awakened TCB. - Exercise: replace binary with counting semantics and explain event loss or accumulation.
- Hazard3/GDB proof: ISR uses only FromISR API, does not block, and a required yield selects the unblocked higher-priority task.
- Main pitfall: calling task-context API from an ISR or ignoring
xHigherPriorityTaskWoken.
FC08 — Critical sections, mutexes and priority inheritance
Book basis: Chapter 8, including critical sections, scheduler suspension, mutexes, priority inversion/inheritance, deadlock and gatekeeper tasks.
- Raw API:
taskENTER_CRITICAL,taskEXIT_CRITICAL,vTaskSuspendAll,xTaskResumeAll,xSemaphoreCreateMutex,xSemaphoreTake/Give. - Main task: low/medium/high tasks first expose bounded priority inversion, then prove inherited priority while a mutex protects a shared resource.
- Blocks: A2 mutex owner/waiters/resource; A4 three-task topology; A5 inversion and inheritance flow; A6 mutex/task states; A7 effective versus base priority and owner TCB; A8 mutex versus gatekeeper decisions.
- Exercise: construct a lock-order table that removes a demonstrated two-mutex deadlock.
- Hazard3/GDB proof: low task's effective priority rises while high waits and returns after release; the shared invariant remains intact.
- Main pitfall: using a binary semaphore where priority inheritance is required, or holding a mutex across an unbounded operation.
FC09 — Event groups and multi-event synchronization
Book basis: Chapter 9.
- Raw API:
xEventGroupCreate,xEventGroupSetBits,xEventGroupWaitBits,xEventGroupSync. - Main task: two workers publish independent readiness bits; a coordinator waits for ALL bits, then all participants cross a reusable barrier.
- Blocks: A2 bit ownership and event object; A4 participant topology; A5 set/wait/unblock/barrier; A6 bit mask and blocked-task state; A7 EventBits_t, wait masks and TCB wake evidence; A8 barrier pattern.
- Exercise: compare ANY versus ALL and clear-on-exit semantics on the same event trace.
- Hazard3/GDB proof: the coordinator cannot pass before the complete mask and the barrier releases the intended set of tasks once.
- Main pitfall: reusing one bit for incompatible meanings or forgetting the effects of automatic bit clearing.
FC10 — Direct-to-task notifications
Book basis: Chapter 10, especially benefits/limits and 10.3.2–10.3.6.
- Raw API:
xTaskNotifyGive,ulTaskNotifyTake,xTaskNotify,xTaskNotifyWaitand their explicit FromISR variants as a comparison. - Main task: one receiver exercises counting, value overwrite and bit-set notification modes without an intermediary queue/semaphore object.
- Blocks: A2 notification value in the target task; A3 sender→target binding; A5 notify/block/wake/consume; A6 notification state/value; A7 target TCB, value transitions and RAM comparison with queue/semaphore alternatives.
- Exercise: choose the correct notification action for three stated protocols and justify what information can be lost.
- Hazard3/GDB proof: only the selected target wakes, and the observed value transition matches the selected action.
- Main pitfall: treating a single notification slot as a general multi-reader message queue.
FC11 — Static allocation and bounded kernel storage
Book basis: 3.4 plus the static creation variants introduced throughout Chapters 4–6.
- Raw API:
xTaskCreateStatic,xQueueCreateStatic,xTimerCreateStatic,vApplicationGetIdleTaskMemory,vApplicationGetTimerTaskMemory. - Main task: rebuild a small task+queue+timer topology with explicit TCB, stack and object buffers; compare it to the dynamic build.
- Blocks: A1 ownership boundary; A2 complete storage graph; A4 bounded
topology; A5 initialization order; A7
.bssaddresses, map file and zero heap growth. - Exercise: calculate and declare all required storage for one additional task and queue.
- Hazard3/GDB proof: kernel object addresses lie in declared static storage, and free-heap values do not change during creation.
- Main pitfall: confusing a statically allocated task with a static C function, or under-sizing stack/object buffers.
FC12 — Assertions, hooks, stack checks and runtime diagnostics
Book basis: Chapters 12–13.
- Raw API/config:
configASSERT, malloc-failed and stack-overflow hooks,uxTaskGetStackHighWaterMark,uxTaskGetSystemState, trace hooks. - Main task: run controlled positive diagnostics plus isolated expected-fail fixtures for an assertion, allocation failure and stack guard.
- Blocks: A1 diagnostic boundaries; A2 evidence record and hook contracts; A5 fault→hook→frozen evidence; A6 healthy/failing terminal states; A7 stack watermark, task statistics, fault PC and build identity.
- Exercise: diagnose a supplied trace without changing the program first.
- Hazard3/GDB proof: every fixture stops in the intended hook with preserved source/ELF/checkpoint identity and no false PASS.
- Main pitfall: optimizing hooks away, allocating/printing unsafely inside a hook, or treating watermark as the exact current stack use.
FC13 — UART interrupt-to-task pipeline
Book basis: 7.2–7.7 and the UART notification example in 10.3.7.
- Raw API: explicit UART ISR,
vTaskNotifyGiveFromISRorxTaskNotifyFromISR,ulTaskNotifyTake,portYIELD_FROM_ISR. - Main task: RX ISR transfers bytes into a bounded ring; a task drains and validates complete frames while TX ownership remains explicit.
- Blocks: A1 peripheral/ISR/task boundary; A2 MMIO/ring/task context; A3 ISR→notification binding; A5 byte→interrupt→ring→task→frame flow; A6 ring and receiver states; A7 MMIO, indices, trap frame and task stack.
- Exercise: specify overflow policy and prove it with an overlong frame.
- Hazard3/GDB proof: ISR duration is bounded, no blocking API is used, ring indices remain valid and the consumer wakes at the correct boundary.
- Main pitfall: doing parsing in the ISR or publishing an index before the corresponding byte is visible.
FC14 — GPIO, hardware timer and deferred work
Book basis: 6.4, 7.3 and 7.6, applied to GPIO/timer peripherals.
- Raw API: hardware timer ISR,
xTimerPendFunctionCallFromISR, optional notification/semaphore comparison, GPIO MMIO. - Main task: a timer edge records minimal ISR evidence, defers a bounded callback to the daemon task, and updates GPIO state under a stated timing contract.
- Blocks: A1 hardware/ISR/daemon/task boundary; A2 event record; A3 deferred
callback binding; A5 edge→ISR→daemon queue→callback→GPIO; A6 pending and
applied states; A7
mtime/mtimecmp, trap frame, daemon task and MMIO. - Exercise: select notification, semaphore or daemon deferral for three different workloads.
- Hazard3/GDB proof: ISR and deferred callback have different stacks/PCs, exactly one GPIO transition is associated with each accepted event.
- Main pitfall: confusing a hardware timer interrupt with a FreeRTOS software timer callback or overloading the daemon task.
FC15 — Integrated deterministic FreeRTOS C application
Book basis: synthesis of Chapters 3–13.
- Raw API: tasks, queues, notification, event group, mutex, software timer, FromISR handoff and diagnostics, selected only where justified.
- Main task: acquire deterministic UART/GPIO/timer events, process one algorithmic workload, publish results and supervise health under bounded memory.
- Blocks: all A1–A8 views may be enabled because architecture, topology, dispatch, flow, state, runtime and patterns are all distinct here.
- Exercise: change one application policy while preserving the published concurrency and memory contracts.
- Hazard3/GDB proof: timestamped end-to-end trace connects input event, selected task, communication object, result, deadline/ordering invariant and final PASS.
- Main pitfall: adding every learned primitive instead of choosing the smallest correct synchronization mechanism.
Common card contract
Every new card must contain:
- a one-page goal and scope contract;
- one principal Task divided into viewpoint Blocks and Phases/Steps;
- only applicable A1–A8 views, with explicit reasons for unavailable views;
- a stable
code_reffor every CODE step; - a debug strategy, checkpoint and non-empty assertion set for every RUN step;
- a student exercise that changes one variable without changing the whole experiment;
- a Hazard3/GDB acceptance proof including source and ELF identity;
- HTML and TeX generated from one
card_source.json; - no PDF generation and no commit unless explicitly requested.
Completed implementation order
FC01 heap_4
→ FC02 first task
→ FC03 scheduler
→ FC04 state/delay
→ FC05 queues
→ FC06 software timers
→ FC07 interrupts/semaphores
→ FC08 resources/mutex
→ FC09 event groups
→ FC10 notifications
→ FC11 static allocation
→ FC12 diagnostics
→ FC13 UART
→ FC14 GPIO/timer
→ FC15 integration
Obie bramki wejścia do FreeRTOS C++ są spełnione: FC15 zamyka tor systemowy C, a właściwe karty językowe C++/OOP dostarczają model własności, RAII, szablony i statyczne trampoline. Następnym etapem jest audyt i realizacja serii FreeRTOS C++ karta po karcie, bez mieszania wrapperów z implementacją kernela w C.