K04 — FreeRTOS C++: scheduler states, priorities and typed ticks
K04 turns the scheduler into an observable experiment. Two worker tasks start at priority 2. The program proves three separate mechanisms on the real FreeRTOS kernel and Hazard3/RV32I:
- equal-priority ready tasks alternate when time slicing is enabled;
vTaskDelay(3)moves A toeBlocked, so B runs even though it has no higher priority;- raising B from priority 2 to 3 makes B run before A returns from
vTaskPrioritySet().
The C++ layer is deliberately small. Ticks and
TickPoint separate durations from time points.
TaskRef is a non-owning view of a
TaskHandle_t; it can inspect state and priority but never deletes a task.
Evidence sequence
The executable stores eight records in g_snapshots:
| Event | A | B | Current | Meaning |
|---|---|---|---|---|
| 1 | READY | READY | startup | scheduler is running; both workers can run |
| 2 | RUNNING | READY | A | A is about to request a three-tick delay |
| 3 | BLOCKED | RUNNING | B | B observes A on the delayed list |
| 4 | RUNNING | READY | A | A wakes exactly three ticks later |
| 5 | RUNNING | READY | A | marker BEFORE=1, just before raising B |
| 6 | READY | RUNNING, p=3 | B | marker HIGH=2, immediate preemption |
| 7 | RUNNING | READY, p=2 | A | marker AFTER=3, API call finally returned |
| 8 | DELETED | DELETED | verifier | both workers completed; PASS can be checked |
The first record is intentionally taken by a short highest-priority startup
task after vTaskStartScheduler(). Calling eTaskGetState() before the
scheduler starts can expose the kernel's selected current TCB and is not a
sound way to claim that a task has already run.
Relative and periodic delay
vTaskDelay(Ticks{3}.count())blocks relative to the tick at which the call is made.xTaskDelayUntil(&next, Ticks{1}.count())advances a stored wake reference; the verifier uses it for periodic checks without accumulating its own work time as drift.
A tick is not inherently one millisecond. In this laboratory
configTICK_RATE_HZ == 1000, so it happens to be 1 ms, but the code and the
student's reasoning use tick counts rather than a hidden wall-clock unit.
Build and verify
The local fallback obtains Hazard3 startup support and the simulator from the
neighboring FreeRTOS C scheduler card. They can also be supplied explicitly
with RV_ENV_ROOT and TB.
make check
Expected summary:
PASS host: typed ticks and non-owning TaskRef
PASS ABI: zero-cost Tick/TaskRef wrappers and stable evidence symbols
PASS task01: states + time slicing + priority preemption
00000001
00000002
00000001
00000003
00000001
00000002
00000003
The alternating task IDs may be 1,2,1 or 2,1,2; the contract checks the
relation, not which equal-priority task happens to start first. The final four
values must always be delay 3 and priority markers 1,2,3.
Debugger evidence
p g_switch_count
p g_alternating_tasks
p g_alternating_ticks
p g_snapshot_count
p g_snapshots
p g_delay_start_tick
p g_delay_observed_tick
p g_delay_end_tick
p g_delay_elapsed
p g_priority_order
p g_scheduler_states_pass
Scope boundary
This card does not introduce an owning task class or a scheduler facade.
TaskRef becomes invalid when the referenced task's TCB is deleted. Event 8
therefore records the known terminal state without dereferencing stale worker
handles. Static/dynamic task ownership and deletion policy belong to K07–K09.
Render the printable worksheet with:
make pdf