Files

284 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.