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:

  1. equal-priority ready tasks alternate when time slicing is enabled;
  2. vTaskDelay(3) moves A to eBlocked, so B runs even though it has no higher priority;
  3. 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 K07K09.

Render the printable worksheet with:

make pdf
S
Description
No description provided
Readme 755 KiB
Languages
C++ 52.3%
C 22.7%
Makefile 12.1%
Shell 9%
Assembly 2.7%
Other 1.2%