380 lines
16 KiB
Markdown
380 lines
16 KiB
Markdown
# Paired FC02/K02 Plan — First FreeRTOS Task through A1–A8
|
||
|
||
Status: agreed paired design; FC02 C and K02 C++ now have local
|
||
implementations that follow the shared E01–E12 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
|
||
|
||
```text
|
||
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.
|
||
|
||
## A1–A8 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 E01–E12 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.
|
||
|
||
```text
|
||
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 A1–A8 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 E01–E12 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.
|
||
|
||
```text
|
||
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:
|
||
|
||
```text
|
||
goal and scope
|
||
→ applicable A1–A8 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;
|
||
4–5. joined A5 CREATE/START and DISPATCH/FINISH spread;
|
||
6. A6 diagnostic and kernel state lanes;
|
||
7. 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;
|
||
4–5. joined A5 CREATE/START and DISPATCH/FINISH spread;
|
||
6. A6 object/wrapper and kernel state lanes;
|
||
7. A7 runtime evidence;
|
||
8. 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. E01–E12 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 A1–A8 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 E01–E12.
|
||
|
||
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
|
||
|
||
- FreeRTOS task creation:
|
||
<https://www.freertos.org/Documentation/02-Kernel/04-API-references/01-Task-creation/01-xTaskCreate>
|
||
- FreeRTOS task states:
|
||
<https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/01-Tasks-and-co-routines/02-Task-states>
|
||
- FreeRTOS task implementation and self-deletion:
|
||
<https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/01-Tasks-and-co-routines/05-Implementing-a-task>
|
||
- Shared A1–A8 convention:
|
||
`tools/card-layouts/docs/UML-VIEWPOINTS.md`
|
||
- Shiki bundled languages and transformers:
|
||
<https://shiki.style/languages>,
|
||
<https://shiki.style/packages/transformers>
|