The puzzle
The prototype runs for a week without missing a sample. Then a customer enables the Bluetooth link, a log flush lands during a burst of motor interrupts, and the control loop is late for the first time. Testing showed that the system usually meets its deadlines; it cannot show that it always does, because the worst combination may never have happened on the bench. How do you show, before shipping, that every deadline will be met?
STEP 1
The task model
Real-time analysis describes each periodic task by three numbers:
- , its worst-case execution time (WCET): the longest it can run for one activation, without being interrupted;
- , its period (or minimum time between activations, for sporadic tasks such as a button handler);
- , its relative deadline: how soon after activation it must finish. Often .
Its utilisation is the fraction of the CPU it needs, and the total over all tasks is
If , no scheduler can meet every deadline on one CPU: there is more work than time. is necessary but, for fixed priorities, not sufficient.
STEP 2
Measuring execution time and setting budgets
Every number in the analysis rests on . Measure it as unit 6, lesson 6 showed: a GPIO toggled at the start and end of the task’s work and a logic analyser, or a cycle counter read before and after. Record the maximum over long runs with realistic inputs, not the average. FreeRTOS can also accumulate each task’s run time when given a counter at least ten times faster than the tick; its report disables interrupts while it runs, so it is a debugging aid, not something to call from a task with a deadline.
A measured maximum is a lower bound on the true worst case, not a proof; a proven bound needs static or hybrid WCET analysis of the code on the actual hardware. Code paths that the tests never took, flash wait states and cache misses (unit 3, lesson 6), interrupts that happened to be quiet and bus contention from DMA all make the real worst case longer. Add a margin, and turn each into a budget: a number the design promises, which testing then checks is never exceeded. Treat interrupt handlers as the most urgent tasks of all, with their own and minimum inter-arrival time: they preempt every task.
STEP 3
A quick test: the utilisation bound
For independent periodic tasks (no shared locks, negligible switching cost) with and preemptive fixed priorities assigned rate-monotonically (lesson 3), a classic result (Liu and Layland, 1973) guarantees every deadline if
For n independent periodic tasks with deadlines equal to their periods, scheduled preemptively with shorter periods given higher priorities, every deadline is met if the total utilisation is at most n(2^(1/n) − 1). The bound falls from 100 % for one task towards ln 2 ≈ 69.3 %. Above it the test says nothing: the task set may or may not be schedulable, and response-time analysis decides. Above 100 % no schedule on one CPU can work.
The bound is 100 % for one task, 82.8 % for two, 78.0 % for three, and falls towards ln 2 ≈ 69.3 %. It is sufficient, not necessary: a set that passes is safe, a set above the bound may still be fine. If priorities can instead follow the nearest absolute deadline (earliest deadline first, EDF), the Linux documentation states the exact condition for on one CPU: all deadlines are met if and only if . Zephyr can break ties between threads of equal static priority by earliest deadline.
STEP 4
The exact test (under its assumptions): response-time analysis
For fixed priorities, the worst case happens when a task is released at the same moment as every more urgent task. Its worst response time is its own execution, plus the longest time a less urgent task can block it through a shared lock (lesson 5), plus every execution of each more urgent task that is released within the window:
appears on both sides, so it is found by iteration: start from , evaluate the right-hand side, and repeat until the value stops changing (the task meets its deadline if ) or exceeds (it does not).
↑ This step uses the figure at the top of the page.
The analysis gives a verdict for every task, including those above the utilisation bound, and it shows where the time goes: which higher-priority task preempts how often, and how much a critical section costs everyone above it. Interrupts enter as the highest-priority terms of the sum, and the blocking is where the locking protocol matters: under a priority ceiling it is the longest single lower-priority critical section, under inheritance it can be the sum of several, and without inheritance, a ceiling or non-preemptive critical sections it must include all medium-priority work. The analysis assumes deadlines no longer than periods, independent tasks apart from , no release jitter, and context-switch overhead folded into each ; it is exact without blocking and a safe (sufficient) test with it.
STEP 5
Detecting overruns at run time
Analysis rests on budgets; the running system should check them. A periodic FreeRTOS task written with xTaskDelayUntil() gets an overrun detector for free: the function returns pdFALSE when the next wake time is already due (at or before the current tick), that is, when the activation finished after its next release. The measure is tick-granular and includes preemption, and after a long overrun the following calls also return pdFALSE while the task catches up, so record the maximum execution time separately with a cycle counter.
TickType_t last = xTaskGetTickCount();
for (;;) {
control_step();
if (xTaskDelayUntil(&last, pdMS_TO_TICKS(10)) == pdFALSE)
overruns++; /* this activation finished after its next release */
}
Count overruns, log the longest execution time seen, and check stack high-water marks (uxTaskGetStackHighWaterMark()) in long tests. What the system does when a budget is exceeded, from a watchdog reset to a safe state, is the subject of unit 14.
STEP 6
Worked example: a set above the bound
Three tasks, most urgent first: τ1 needs 1 ms every 5 ms, τ2 2 ms every 8 ms, τ3 10 ms every 20 ms, with and no blocking. The utilisation is
so the quick test is inconclusive. Response-time analysis for τ3 starts from :
It converges at exactly 20 ms: τ3 meets its deadline with no margin at all. τ1 responds in 1 ms and τ2 in 2 + ⌈3/5⌉·1 = 3 ms. A simulation of the synchronous release gives the same 20 ms. With 11 ms for τ3 (), the iteration goes 14, 18, 21 and passes the deadline. If τ3 held a lock for up to 4 ms that τ1 and τ2 also need, their responses would become 1 + 4 = 5 ms and 2 + 4 + 2·1 = 8 ms, exactly their deadlines: the whole margin is gone.
MYTHS AND FACTS
Common misconceptions
The average execution time is what matters
Deadlines are missed by the worst activation; analysis uses the worst case, and budgets must cover it.
The tests passed, so the timing is fine
Testing samples the combinations that happened; analysis covers the ones that did not.
Above 69 % load, rate-monotonic scheduling fails
The bound is sufficient, not necessary; response-time analysis often shows much higher loads are fine.
Interrupts are not tasks, so they do not count
They preempt every task and must appear in every response-time sum.
Check yourself
Answer in your head, then open the card.
Four tasks have C/T of 1/4, 1/5, 2/10 and 3/20 ms. What is U, and does the rate-monotonic utilisation test guarantee the set?
U = 0.25 + 0.2 + 0.2 + 0.15 = 0.8. The bound for 4 tasks is 4(2^(1/4) − 1) ≈ 0.757, so the test is inconclusive; run response-time analysis.
Compute R for a task with C = 3 ms below two more urgent tasks (C = 1, T = 4) and (C = 2, T = 10), with no blocking.
Start at 3 + 1 + 2 = 6; then 3 + ⌈6/4⌉·1 + ⌈6/10⌉·2 = 3 + 2 + 2 = 7; then 3 + ⌈7/4⌉ + 2 = 7. R = 7 ms.
Why is a measured maximum execution time not a safe WCET on its own?
It only covers the paths, inputs and interference that occurred during the measurement; untested paths, wait states, cache misses, DMA contention and interrupt bursts can make a later activation longer. Add a margin and keep checking it.
A periodic task calls xTaskDelayUntil with a 10 ms period and sometimes gets pdFALSE. What does that mean?
The next wake time was already due when the task asked to wait: that activation finished after its next release (an overrun, or the catch-up after one), so the task was not delayed at all.
Sources (4)
- Linux kernel, Documentation/scheduler/sched-deadline.rst, §3 “Scheduling Real-Time Tasks” — “The maximum execution time max{c_j} is called ‘Worst Case Execution Time’ (WCET)”; Task = (WCET, D, P); “The utilization of a real-time task is defined as the ratio between its WCET and its period”; on one CPU, if Di = Pi for all tasks, “EDF is able to respect all the deadlines … if and only if the total utilization … is smaller or equal than 1”; the density test “is only sufficient, and not necessary”
- FreeRTOS Kernel, include/task.h (xTaskDelayUntil, run-time statistics, stack high-water mark) — xTaskDelayUntil “can be used by periodic tasks to ensure a constant execution frequency” and returns pdFALSE when “the next expected wake time is in the past”; the run-time stats counter “should be at least 10 times the frequency of the tick count”, and vTaskGetRunTimeStatistics() “will disable interrupts for its duration … not intended for normal application runtime use but as a debug aid”; uxTaskGetStackHighWaterMark() returns “the minimum free stack space there has been (in words …) since the task started”
- Zephyr Project documentation, doc/kernel/services/scheduling/index.rst — with CONFIG_SCHED_DEADLINE, among threads of equal static priority “the thread with the earlier deadline is considered to have the higher priority”; k_thread_deadline_set(); “Execution of ISRs takes precedence over thread execution”
- Linux kernel, Documentation/locking/rt-mutex-design.rst — unbounded priority inversion: a high-priority task “prevented from running by a lower priority process for an undetermined amount of time”, which priority inheritance turns into a bounded blocking term