Files
workspace-info/plans/informatyka/freertos-first-task-a1-a8.md

16 KiB
Raw Permalink Blame History

Paired FC02/K02 Plan — First FreeRTOS Task through A1A8

Status: agreed paired design; FC02 C and K02 C++ now have local implementations that follow the shared E01E12 contract.

This plan defines one controlled teaching experiment rendered twice:

  • FC02 / FreeRTOS C: raw TaskFunction_t, void *pvParameters, TaskHandle_t and xTaskCreate;
  • K02 / FreeRTOS C++: the existing header-only Task<Derived> CRTP wrapper, explicit start() and static trampoline.

The design remains the comparison contract. FC02 now has a local executable, HTML and TeX implementation; PDF generation and publication remain separate operations.

Learning question

The student must be able to answer one question with source, binary and runtime evidence:

What remains identical when a FreeRTOS task is expressed through raw C or a small C++ wrapper, and what new compile-time and runtime boundaries does the wrapper introduce?

The C and C++ cards therefore use the same calculation and kernel behaviour. Only the language-facing task abstraction changes.

Curriculum position

C pointers
→ C structures and memory regions
→ heap_4 mechanics in C
→ FC02 raw FreeRTOS task in C
→ C++ language/OOP prerequisites
→ K02 FreeRTOS task wrapper in C++
→ later lifetime, RAII, communication and synchronization cards

FC02 establishes the raw kernel boundary first. K02 reuses that known behaviour and makes the abstraction delta observable instead of teaching the kernel and C++ wrapper as two simultaneous unknowns.

Controlled experiment contract

Both cards must share these invariants:

Property Required value
Kernel FreeRTOS-Kernel V11.3.0, same pinned source revision
Target RV32I/Hazard3 simulator, cycle-exact teaching profile
Workers two equal-priority CPU-bound workers, created before the scheduler
Work sum integers 1..20000 independently
Expected result 200010000 for each worker
Supervisor one lower-priority task validates both workers
Scheduling proof elapsed work crosses at least two ticks and records A -> B -> A
Allocation dynamic xTaskCreate backed by the same heap_4 configuration
Completion diagnostic state/result is published before self-delete: NULL in C, nullptr in C++
Replay clean RAM and a new generation for every requested RUN checkpoint

The comparison is valid only when the two builds also use byte-identical FreeRTOSConfig.h, the same tick rate, time-slicing setting, priorities, stack depths, worker creation order and checkpoint-sink signature. CI must compare those inputs rather than relying on prose.

Language flags differ only where the lesson requires it:

  • FC02: freestanding C11;
  • K02: freestanding C++17, no exceptions, no RTTI and no hosted C++ runtime.

The workload must remain present in disassembly. Bounds, trace writes and published results are observable; a compiler-folded constant is a failed teaching build.

A1A8 is a palette, not a quota

The stable viewer keeps all eight positions. A card enables only viewpoints that answer a real question. An unavailable tab remains visible with a short reason, and the print index repeats that reason.

Viewpoint FC02 C K02 C++ Reason
A1 CONTEXT yes yes system and language boundaries
A2 STRUCTURE yes yes C contexts/handles versus C++ classes/fields
A3 DISPATCH yes yes C function pointer versus CRTP trampoline
A4 APPLICATION no yes raw C module already is the application; C++ specializes a generic wrapper
A5 FLOW yes yes common E01E12 scenario
A6 STATE yes yes diagnostic/language lifetime separated from kernel task state
A7 RUNTIME yes yes addresses, TCB, stack, registers, PC and ticks
A8 PATTERNS no yes a separate C pattern view would be decorative; K02 provides the comparison

FC02 may label local idioms such as context object, opaque handle and C callback ABI in A2/A3. It must not manufacture an A8 diagram merely to fill the tab.

Teaching hierarchy

Each applicable viewpoint is a normal Block. This preserves the existing card tree and does not add a new hierarchy level.

Series
└── Card
    └── Task01 · First FreeRTOS task
        ├── Block · A1 CONTEXT
        ├── Block · A2 STRUCTURE
        ├── Block · A3 DISPATCH
        ├── Block · A4 APPLICATION       K02 only
        ├── Block · A5 FLOW
        │   ├── Phase · CREATE / START
        │   └── Phase · DISPATCH / FINISH
        ├── Block · A6 STATE
        │   ├── Phase · LANGUAGE / DIAGNOSTIC
        │   └── Phase · KERNEL
        ├── Block · A7 RUNTIME
        │   ├── Phase · MEMORY
        │   └── Phase · CPU / SCHEDULER
        ├── Block · A8 PATTERNS          K02 only
        └── Exercise · Paired timestamped trace

The A1A8 top strip jumps to its block. Up/Down walks steps inside a block; Ctrl+Up/Down crosses blocks. Cursor movement is inert. Enter in the viewer and F2 from Neovim explicitly activate the selected item.

Common semantic event vocabulary

Snapshot identifiers remain local to each card and container. A shared event_id makes the observations comparable without pretending that one debugger checkpoint can be reused across two binaries.

Event Shared meaning FC02 C boundary K02 C++ boundary
E01 application representation prepared static worker contexts initialized static SumTask objects constructed
E02 launch interface entered first raw create request first workerA.start() entry
E03 kernel create boundary xTaskCreate(entry, ctx) xTaskCreate(trampoline, this)
E04 task ready and handle stored context handle is non-null wrapper state ready, handle non-null
E05 scheduler start vTaskStartScheduler() vTaskStartScheduler()
E06 C callback entry sum_task_entry(void *) static trampoline(void *)
E07 calculation running typed WorkerContext * body SumTask::run() and state running
E08 deterministic time slice worker A observes A -> B -> A worker A observes A -> B -> A
E09 result published context/global result stored g_results[id] stored, run() returns
E10 diagnostic completion context state complete, public handle cleared wrapper state complete, public handle cleared
E11 current task self-deletes vTaskDelete(NULL) vTaskDelete(nullptr)
E12 lower-priority supervisor passes raw context invariants pass wrapper/object invariants pass

E02 is the first launch hit and is guarded by prior checkpoint state. E08 is pinned to worker A. Per-instance conditions must prevent worker B from accidentally satisfying worker A checkpoints.

The supervisor executes before the idle task necessarily reclaims deleted TCBs. Heap reclamation is therefore not an E01E12 acceptance event. A7 may measure allocator deltas, but it must describe them as observations at a defined instant.

CODE and RUN semantics

An interactive anchor has one of two modes:

  • CODE selects source or a deterministic binary query. It never resets or runs the target and does not require snapshot_ref.
  • RUN opens the associated source and performs a clean deterministic replay to its card-local snapshot_ref.

Compile-time facts—types, inheritance, friend, field layout, absence of virtual dispatch and ABI restrictions—are CODE facts. Scheduling, registers, task states and memory values are RUN facts. A class diagram must never fake a runtime event.

Repeated visible step numbers are permitted in different blocks, but tooling must address a step by its full task/block/phase/step key. A bare ambiguous --step request is rejected.

Debug strategy attached to every step

Every selectable UML anchor owns one default debugging strategy. The strategy is keyed by the full navigation position rather than embedded as unstructured prose in the step.

strategy
├── id and kind: code | run
├── prerequisites: card, profile, target and required artifact
├── code_ref: repository-validated file and line/range
├── action: open | replay
├── layout: source, asm, registers, stack, memory, kernel state
├── commands: deterministic GDB or artifact queries
├── expected_observations
├── assertions
└── evidence_fields

RUN assertions reuse debug_checkpoints.verify.expressions. CODE assertions are exact command/pattern pairs over an identified ELF or source blob, for example nm, readelf or a bounded disassembly query. A screenshot of an unidentified terminal is not sufficient CODE evidence.

The minimum Termdebug observation set is chosen per step, not displayed by habit:

  • source and assembly at the boundary being taught;
  • a0 only where the RV32 ABI still carries the callback argument;
  • sp and task stack interval where stack ownership is the question;
  • object/context address, public handle and TCB address where identity is the question;
  • PC, tick and trace when scheduling is the question;
  • result and diagnostic state when completion is the question.

pxCurrentTCB may be inspected by the debugger and is marked kernel internal. Card code uses public FreeRTOS APIs and does not make this symbol a program contract.

Visual and code evidence contract

Each UML element has a visible numbered anchor and a stable authored svg_target. Selection by mouse and keyboard controls the same cursor.

  • disabled content remains readable and uses a neutral outline plus an explicit unavailable marker/reason;
  • selectable inactive neighbours alternate two restrained treatments so their hit areas remain distinguishable;
  • active selection adds a stronger outline, small positional marker and light fill;
  • focus, selectability and completion remain distinguishable in grayscale and do not rely on hue alone;
  • non-selected elements remain legible rather than disappearing.

Code shown beside a diagram is read-only evidence. Neovim remains the editor and live debugger. The component receives a validated code_ref, source blob hash and explicit highlighted line range; it never fetches an arbitrary file path supplied by the browser.

The planned CodeEvidence pipeline uses Shiki at generator/build time, not as a browser editor or runtime highlighter. A fine-grained, version-pinned highlighter loads only c, cpp, riscv, shellscript and json, produces flat per-line token arrays, and the React viewer renders that allow-listed data. This keeps the browser bundle free of the regex/WASM engine while retaining an actual RISC-V TextMate grammar and exact line/range classes. Rendering never relies on dangerouslySetInnerHTML.

A custom Shiki theme emits CSS-variable values rather than stock-theme hex colours. Light, dark and print treatments therefore remain viewer CSS decisions. Assembly source uses the riscv grammar directly. Objdump .lst input first passes through a small deterministic column parser: address, byte and symbol fields receive fixed semantic classes, while only the instruction tail is tokenized as RISC-V. A non-matching listing line falls back to plain text. Disassembly keeps white-space: pre and horizontal scrolling rather than soft wrapping.

Audit chain and later evidence capture

The intended chain is:

goal and scope
→ applicable A1A8 viewpoints
→ UML anchor
→ checkpoint debugging strategy
→ CODE proof or RUN replay
→ Neovim + Termdebug + Hazard3 measurement
→ approved checkpoint
→ later composite screenshot
→ later lesson report

Approval must be bound to snapshot_ref, replay generation and verification result. A later capture must reject stale identity/generation, retain the raw PNG, store an image SHA-256 and record backend timestamp, card/version/source hash, target, event, checkpoint and viewer state. Metadata is rendered from the database rather than burned irreversibly into the original image.

This plan does not implement screenshot capture or report generation.

Page plan

FC02 — seven sheets

  1. goal, scope, experiment invariants and viewpoint index;
  2. compact A1 plus main A2;
  3. A3 callback/parameter dispatch; 45. joined A5 CREATE/START and DISPATCH/FINISH spread;
  4. A6 diagnostic and kernel state lanes;
  5. A7 runtime evidence plus exercise.

A4 and A8 are printed in the index as unavailable with their reasons.

K02 — eight sheets

  1. goal, scope, experiment invariants and viewpoint index;
  2. compact A1 plus main A2;
  3. main A3 plus compact A4; 45. joined A5 CREATE/START and DISPATCH/FINISH spread;
  4. A6 object/wrapper and kernel state lanes;
  5. A7 runtime evidence;
  6. A8 C-versus-wrapper role table plus exercise and rubric.

One diagram must fit the content box. A long logical diagram is split only at a phase/group boundary and shares a spread_group; the viewer may join those sheets side by side without changing print pagination.

Acceptance gates for a future implementation

  1. Parity check proves common kernel/config/workload/priority/stack inputs.
  2. Every enabled viewpoint has purpose, model kind and ordered anchors.
  3. Every disabled viewpoint has a non-empty reason.
  4. Every svg_target resolves after rendering and anchor order passes lint.
  5. Every CODE step has a validated code/artifact proof and no snapshot.
  6. Every RUN step has a card-local snapshot and non-empty assertions.
  7. E01E12 are unique, ordered and comparable across the two cards.
  8. A clean replay produces two sums of 200010000 and the A -> B -> A trace.
  9. FC02 has C-only ABI evidence; K02 has no exceptions, RTTI, vtables or hosted runtime.
  10. Navigation never mutates the simulator until explicit Enter/F2.
  11. No program assertion depends on undocumented kernel internals.
  12. Print remains meaningful without viewer controls or colour.

Decisions from the Claude review

Accepted corrections:

  • treat viewpoints as blocks and A1A8 as an optional palette;
  • enforce the paired-experiment parity gates in tooling;
  • add shared semantic event_id while keeping snapshots card-local;
  • validate viewpoint availability, anchors, RUN snapshots and strategies;
  • make assertions mandatory rather than merely displaying observations;
  • bind approval and later capture to a verified replay generation;
  • support deterministic text evidence for CODE steps;
  • pre-tokenize source with fine-grained Shiki grammars and keep the React component display-only;
  • pin the highlighter/grammar versions and test the objdump-column parser with fixed listing fixtures;
  • distinguish diagnostic state from eTaskState and mark kernel internals;
  • keep idle-task reclamation outside E01E12.

Rejected for these cards:

  • replacing K02 CRTP with the upstream virtual TaskBase design;
  • forcing A4/A8 into FC02;
  • automatic replay on cursor movement;
  • claiming RAII or ownership semantics before the later lifetime card;
  • using pxCurrentTCB as application code;
  • treating a class relationship as a runtime checkpoint.

Deferred curriculum envelope — not part of this implementation

For Year 2, a later curriculum pass will attach three coordinated layers to a lesson:

  1. one FreeRTOS mechanism/concurrent algorithm being learned, for example a task or queue;
  2. one algorithm selected from the course book;
  3. one examination task selected from the current matura schedule/material.

The current technology is the execution medium, not a decorative appendix:

  • a bare-C card implements and debugs the work in bare C;
  • a FreeRTOS-C task card places suitable work inside raw C task(s);
  • a FreeRTOS-C++ card expresses the same responsibility through the wrapper under study.

Later work may capture approved UML/debugger evidence, compose a lesson report, generate a PDF and prepare it for Teams. None of that is implemented or assigned to FC02/K02 by this plan.

Primary references