feat: add organization-per-series EDU catalog

This commit is contained in:
2026-07-19 16:38:49 +02:00
parent d18817dca5
commit 964ecba3e3
28 changed files with 1880 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
# FC02 Plan — First Task, TCB and Task Stack
Status: implemented and validated locally. The card exists at
`series/freertos/lab-rv32i-freertos-c-first-task`, is visible in the
FreeRTOS C catalog and remains outside the deploy manifest until its own
repository is committed and published.
The paired experiment and common E01E12 vocabulary are defined in
[`freertos-first-task-a1-a8.md`](freertos-first-task-a1-a8.md).
## Goal
The student creates a FreeRTOS task through the raw C API, follows
`pvParameters` into a typed context, separates that context from the TCB and
task stack, observes equal-priority time slicing, and proves deterministic
completion in Hazard3/GDB.
The card is the C baseline for K02. It must not imitate a C++ wrapper in C.
## Scope
Included:
- `TaskFunction_t` and `void *pvParameters`;
- `xTaskCreate`, `TaskHandle_t`, scheduler start and self-delete;
- two equal-priority CPU-bound workers and one lower-priority supervisor;
- static application contexts, dynamic TCB/task-stack allocation in `heap_4`;
- TCB, stack, PC/SP, callback ABI and task-state observations;
- the same sum and scheduling invariants as K02.
Excluded:
- C++ classes, templates, RAII or virtual dispatch;
- queues, mutexes, notifications, timers, ISR APIs and peripherals;
- proof that the idle task has already reclaimed deleted tasks;
- report/PDF/Teams generation and the later book/matura lesson payload.
## Implemented program shape
```c
typedef enum
{
TRACE_PREPARED,
TRACE_STARTING,
TRACE_READY,
TRACE_RUNNING,
TRACE_COMPLETED,
TRACE_CREATE_FAILED
} WorkerTraceState;
typedef struct
{
uint32_t id;
volatile uint32_t result;
volatile uint32_t iterations;
volatile WorkerTraceState trace_state;
TaskHandle_t handle;
TickType_t start_tick;
TickType_t end_tick;
} WorkerContext;
```
Source responsibilities:
```text
src/tasks/task01_first_task.c
├── two static WorkerContext instances
├── trace/checkpoint state
├── create_worker(WorkerContext*, ...)
├── sum_task_entry(void *pvParameters)
├── supervisor_task_entry(void *pvParameters)
├── task01_debug_checkpoint(event, subject)
└── main()
```
`trace_state` is an application diagnostic. Its name deliberately avoids
pretending that it is `eTaskState`.
## Applicable viewpoint profile
| View | Status | FC02 purpose |
|---|---|---|
| A1 CONTEXT | enabled | raw application → C API/kernel/port → Hazard3 |
| A2 STRUCTURE | enabled | contexts, handles, entry functions and storage domains |
| A3 DISPATCH | enabled | function pointer + erased parameter → typed C body |
| A4 APPLICATION | unavailable | the raw task module already is the concrete application; no generic application layer exists |
| A5 FLOW | enabled | E01E12 creation and execution scenario |
| A6 STATE | enabled | static context/diagnostic state versus kernel state |
| A7 RUNTIME | enabled | context, TCB, stack, registers, PC and ticks |
| A8 PATTERNS | unavailable | a standalone pattern view would be decorative; idioms are labelled in A2/A3 and compared in K02 A8 |
Unavailable A4/A8 tabs remain readable in the viewer and print index with the
reasons above.
## Canonical tree
```text
Series · FreeRTOS C
└── Card FC02 · First Task, TCB and Task Stack
└── Task01 · Raw C task and typed context
├── Block · A1 CONTEXT
│ └── Phase · SYSTEM BOUNDARY
│ ├── Step 01 · application module requests tasks [CODE]
│ ├── Step 02 · FreeRTOS C API owns scheduling [CODE]
│ └── Step 03 · kernel/port execute on Hazard3 [CODE]
├── Block · A2 STRUCTURE
│ └── Phase · C DATA / FUNCTIONS
│ ├── Step 01 · WorkerContext is application state [CODE]
│ ├── Step 02 · TaskHandle_t is an opaque kernel handle [CODE]
│ ├── Step 03 · two static worker contexts [CODE]
│ ├── Step 04 · sum_task_entry has TaskFunction_t shape [CODE]
│ ├── Step 05 · SupervisorContext observes both workers [CODE]
│ └── Step 06 · common sum/scheduling invariants [CODE]
├── Block · A3 DISPATCH · C FUNCTION POINTER
│ └── Phase · CALLBACK / CONTEXT
│ ├── Step 01 · TaskFunction_t erases the context type [CODE]
│ ├── Step 02 · xTaskCreate receives entry and context [RUN E03]
│ ├── Step 03 · pvParameters arrives in RV32 a0 at entry [RUN E06]
│ ├── Step 04 · cast recovers WorkerContext* [RUN E06]
│ ├── Step 05 · entry executes the typed calculation [RUN E07]
│ └── Step 06 · wrong context cast is not rejected by C [CODE]
├── Block · A5 FLOW
│ ├── Phase · CREATE / START
│ │ ├── Step 01 · static contexts prepared [RUN E01]
│ │ ├── Step 02 · first worker create request [RUN E02]
│ │ ├── Step 03 · xTaskCreate(entry, &workerA) [RUN E03]
│ │ ├── Step 04 · handle != null; diagnostic ready [RUN E04]
│ │ └── Step 05 · vTaskStartScheduler() [RUN E05]
│ └── Phase · DISPATCH / FINISH
│ ├── Step 06 · sum_task_entry(&workerA) [RUN E06]
│ ├── Step 07 · diagnostic running; calculation loop [RUN E07]
│ ├── Step 08 · tick/time slice A -> B -> A [RUN E08]
│ ├── Step 09 · result 200010000 published [RUN E09]
│ ├── Step 10 · diagnostic completed; public handle null [RUN E10]
│ ├── Step 11 · vTaskDelete(NULL) [RUN E11]
│ └── Step 12 · supervisor verifies PASS [RUN E12]
├── Block · A6 STATE
│ ├── Phase · CONTEXT / DIAGNOSTIC
│ │ ├── Step 01 · static WorkerContext lifetime [CODE]
│ │ ├── Step 02 · PREPARED -> STARTING -> READY [RUN E01E04]
│ │ └── Step 03 · RUNNING -> COMPLETED [RUN E07E10]
│ └── Phase · KERNEL
│ ├── Step 04 · Ready -> Running through scheduler [RUN E04E07]
│ ├── Step 05 · equal-priority time slicing [RUN E08]
│ └── Step 06 · Deleted does not destroy static context [RUN E10E11]
├── Block · A7 RUNTIME
│ ├── Phase · MEMORY
│ │ ├── Step 01 · context address is not a TCB address [RUN E04/E06]
│ │ ├── Step 02 · TCB and task stack come from heap_4 [RUN E04/E07]
│ │ └── Step 03 · SP lies inside worker stack interval [RUN E06/E07]
│ └── Phase · CPU / SCHEDULER
│ ├── Step 04 · pvParameters in a0 only at callback entry [RUN E06]
│ ├── Step 05 · PC, tick and switch trace [RUN E08]
│ └── Step 06 · result, source and checkpoint identity [RUN E12]
└── Exercise · Timestamped raw-C task trace
```
## Checkpoint table
FC02 snapshots are local to the FC02 binary even though their `event_id`
matches K02.
| Event | Planned snapshot | Stop condition | Required verification |
|---|---|---|---|
| E01 | `task01.contexts` | point 1, worker A | trace prepared, handle null, result zero |
| E02 | `task01.create-request` | point 2, worker A, previous point 1 | first launch request only |
| E03 | `task01.create-call` | point 3, worker A | trace starting; callback/context arguments correct |
| E04 | `task01.ready` | point 4, worker A | trace ready; handle non-null |
| E05 | `task01.scheduler` | point 5 | all three tasks created; startup stack active |
| E06 | `task01.entry` | point 6, worker A | `pvParameters == &workerA`; callback-entry `a0` matches |
| E07 | `task01.work` | point 7, worker A | typed context recovered; trace running |
| E08 | `task01.timeslice` | point 8, worker A only | A/B/A trace; >=2 switches and elapsed ticks |
| E09 | `task01.result` | point 9, worker A | result `200010000`, iterations `20000` |
| E10 | `task01.completed` | point 10, worker A | trace completed; public handle null |
| E11 | `task01.delete` | point 11, worker A | published state persists before non-returning delete |
| E12 | `task01.pass` | point 12, supervisor | two results, contexts, handles, stacks and scheduler invariants pass |
The checkpoint sink is `noinline` and `used`, writes volatile point/subject
state, and ends with a compiler memory barrier. E08 is emitted once by worker
A after the trace has proved `A -> B -> A`.
## A3 dispatch evidence
The exact boundary is:
```text
xTaskCreate(sum_task_entry, ..., &workerA, ..., &workerA.handle)
│ │
│ TaskFunction_t │ void* context
▼ ▼
sum_task_entry(void *pvParameters)
│ callback-entry a0 == &workerA
WorkerContext *worker = (WorkerContext *)pvParameters
typed calculation and published result
```
There is one C callback dispatch. The cast does not cause a second dynamic
dispatch. At callback entry, source + disassembly + `a0` prove the ABI. After
ordinary instructions execute, the card stops claiming that `a0` retains
`pvParameters`.
A compile-only negative exhibit demonstrates that C can accept a wrong
`void*`-to-structure cast that the K02 C++ contract rejects. The unsafe fixture
is never linked into or executed by the teaching program.
## Per-view debugging strategies
| View/anchor | Mode | Layout/action | Expected evidence |
|---|---|---|---|
| A1 dependency boundary | CODE | include/link/config excerpts | application calls public API; kernel/port/target boundary |
| A2 context layout | CODE | source excerpt + `ptype`/DWARF or bounded layout output | static context fields and no hidden C++ object model |
| A2 handle/storage | CODE | declaration + memory-region map | handle is opaque; context is static; TCB/stack are dynamic |
| A3 entry/context | RUN | source + entry disassembly + `a0` + context memory | callback ABI and exact worker identity |
| A3 wrong-cast exhibit | CODE | deterministic compile output and source blob | C compiler does not encode the desired context type |
| A5 E01E12 | RUN | per-event minimal source/asm/register/stack/memory set | ordered scenario and non-empty assertions |
| A6 diagnostic state | RUN | context fields beside public `eTaskGetState`/`vTaskGetInfo` result | correlated domains remain explicitly distinct |
| A7 TCB/stack | RUN | context address, public handle, TCB address, heap range, SP interval | context != TCB; SP belongs to the selected task stack |
| A7 scheduling | RUN | PC, tick, trace buffer and current-task observation | computation crosses real ticks and task changes |
Application code uses public task APIs. `pxCurrentTCB` may appear only in a
clearly labelled debugger-internal observation, never in a C assertion or
source dependency.
## State model
```text
static context lifetime: initialized -------------------------- program end
diagnostic trace state: PREPARED -> STARTING -> READY -> RUNNING -> COMPLETED
kernel task state: Ready <-> Running <-> Blocked; Deleted
```
The lanes can be correlated by an event and handle, but they are not equal.
Clearing the public handle before `vTaskDelete(NULL)` is an application
diagnostic choice. It does not prove immediate heap reclamation.
## Page and spread plan
1. goal/scope, paired invariants and viewpoint index;
2. compact A1 plus main A2 C structure/storage map;
3. A3 callback/parameter dispatch;
4. A5 CREATE/START (E01E05);
5. A5 DISPATCH/FINISH (E06E12), joined with sheet 4 in the viewer;
6. A6 diagnostic and kernel state lanes;
7. A7 runtime map plus exercise/rubric.
The A5 split occurs at the scheduler boundary. Both assets share a
`spread_group`, stable anchor numbering and common participant alignment.
## Acceptance contract
- freestanding C11 build, same pinned kernel/port/config as K02;
- no `_Z*`, `.init_array`, `_GLOBAL__sub_I`, `_Unwind*`, `__cxa_*` or hosted
C++ runtime symbols; positive C/FreeRTOS/checkpoint symbols must exist;
- two independent results equal `200010000`;
- two workers each execute `20000` loop iterations;
- work crosses at least two ticks and proves at least two task changes with an
`A -> B -> A` subsequence;
- callback-entry `a0`, `pvParameters` and worker A address match;
- static context, TCB and task stack are shown as distinct objects/domains;
- all twelve RUN checkpoints have assertions and replay from clean RAM;
- every CODE step has a deterministic source/artifact query and no snapshot;
- all enabled/disabled views, anchors and full tree keys validate;
- plain cursor movement never runs or resets the simulator.
## Validation result
1. The raw C workload and A1/A2/A3/A5/A6/A7 views are implemented.
2. Kernel/config/workload parity is pinned in
`config/paired-experiment.json` and passes `make check-pair`.
3. C-only ABI, context contract and host workload checks pass.
4. Hazard3 finishes with `PASS`; two results equal `200010000`, the trace
contains `A -> B -> A`, and three clean simulator processes produce
identical evidence.
5. HTML and TeX render with A1/A6 portrait, A2/A3/A7 landscape and A5 as a
two-page landscape spread. No PDF was generated.
6. The catalog marks FC02 as existing. The deploy manifest is unchanged
until a published card repository exists.
HTML and TeX may be rendered during validation. PDF generation and commit
remain outside this pass.