5.5.22 · D3Embedded Systems & Real-Time Software

Worked examples — Software-in-the-Loop (SIL) simulation — all software, simulated hardware

3,895 words18 min readBack to topic

Before we start, one promise: every symbol is earned before use. Here are the only ingredients, defined once:


The scenario matrix

Every SIL simulation you will ever run falls into one of these cells. The examples below are labelled with the cell they cover.

Cell Case class What makes it special Covered by
A Nominal, (too slow) The happy path: converge upward Example 1
B Sign flip, (too fast) Command must go negative Example 2
C Zero input, Degenerate: nothing should move Example 3
D Degenerate hardware model () Instant plant — limiting behaviour Example 4
E Numerical instability ( too large) Solver diverges — a simulation bug, not a code bug Example 5
F Real-world word problem Braking-distance stopping question Example 6
G Exam-style twist "SIL passed but hardware failed" — abstraction error Example 7

For the running examples (unless a cell overrides it) we fix: , s, s.

Figure s01 — the closed SIL loop. Sketch of the layout: a lavender rounded box on the left labelled "Software under test — ", and a mint rounded box on the right labelled "Simulated plant — ". A coral arrow points right from software to plant carrying the "command "; a lavender arrow loops back underneath, pointing left from plant to software carrying the "sensed speed ". A small butter-coloured arrow drops into the software box from above, marked " = target enters here". The caption underneath reads: the software never learns the plant is fake — it just reads . As you read each example, trace which arrow the numbers travel along.

Figure — Software-in-the-Loop (SIL) simulation — all software, simulated hardware

Example 1 — Cell A: nominal, the motor is too slow

Step 1 — Compute the error . Why this step? The controller can't act until it knows "how far off". Error is the software's only input.

Step 2 — Controller computes the command . Why this step? This is literally the line of production code under test: u = K * (v_ref - v). Positive error → positive push. Good.

Step 3 — Plant advances one Euler step. Why this step? This is the simulated hardware reacting. The plant uses the command directly (no extra ) — notice the software never sees this equation, it only sees the resulting next tick. That is the "in-the-loop" feedback.

Answer: rev/s.

Recall Was your forecast right?

It moves up by only , not near 10 ::: Correct — Euler with a small nudges gently; convergence takes many ticks, which is exactly why we simulate rather than eyeball.

Verify: Sign check — motor was too slow (), and increased (). Units check: is in rev/s, is dimensionless (s/s), so the increment is rev/s. ✓


Example 2 — Cell B: the sign flip, motor is too fast

Step 1 — Error is now negative. Why this step? We must show the reader the case where the sign of the input flips — the parent contract demands every sign. Negative error means "too fast".

Step 2 — Command flips sign. Why this step? A correct proportional controller reverses its push automatically. If your code had a abs() or unsigned type bug, SIL would catch it right here — the sim would keep accelerating instead of braking.

Step 3 — Plant step. Why this step? The negative command drags down. Confirmed motion toward target.

Answer: , rev/s.

Verify: Symmetry sanity check against Example 1 — in Ex 1 error was ; here it's , so the command is exactly negated ( vs ). ✓ And decreased, correct for "too fast". ✓


Example 3 — Cell C: the zero / degenerate input

Step 1 — Error is zero. Why this step? The degenerate input is the one where the two inputs cancel. We must confirm the software forms exactly here — a subtraction bug or floating-point residue would show up as a tiny nonzero error, and this is where we'd catch it.

Step 2 — Command is zero. Why this step? Zero error should mean zero push. This is the degenerate input every controller must handle. A divide-by-error bug or a NaN would surface here.

Step 3 — Plant step. Why this step? We advance the plant even though the command is zero, precisely to reveal that "no command" does not mean "no motion" — the drag term still acts. This is the whole point of the example, so we must run the step rather than assume nothing happens.

Answer: rev/s (it drifts below target).

Verify: Plug the true equilibrium: for to stay put we need with , i.e. . Solving: . So the loop settles at , not 10 — confirming the offset. ✓


Example 4 — Cell D: degenerate hardware, (instant motor)

Step 1 — Inspect the ratio. As , this ratio . Why this step? The whole update is scaled by . A limiting input must be inspected symbolically, not just numerically.

Step 2 — Restate the command, then take a concrete small . First recompute the controller output exactly as in Example 1, because we start from the same state: Now run one plant step with the shrunken time constant: Why this step? We must show, not assume, where comes from — it is the same controller line from Example 1. A concrete degenerate value then shows the danger before it explodes: one step already jumps all the way to the target and would overshoot the true continuous answer.

Step 3 — Recognise the failure mode. When , one Euler step overshoots the true continuous answer, and when it reaches 2 the simulation sits on the edge of oscillating and blowing up. A "faster" (smaller ) motor needs a smaller to stay accurate.

Answer: As the discrete plant becomes unstable unless shrinks in step; the "instant motor" is a degenerate model SIL cannot faithfully simulate without refining the time step.

Verify: Stability of Euler for requires , i.e. . At : — exactly on the unstable boundary. ✓


Example 5 — Cell E: numerical instability, a simulation bug not a code bug

Step 1 — Compute the growth factor. Why this step? From Example 4 we know means unstable. We're deliberately past the cliff, so we expect the next steps to diverge.

Step 2 — Tick 1. Why this step? We take the very first Euler step with the oversized ratio to see how badly a single update overshoots — already blows past the sensible resting value.

Step 3 — Tick 2. Why this step? We feed the overshot back in. Because it overshot, the correction term flips sign and over-corrects, flinging negative. This shows the divergence is a feedback effect of the bad step size, not a one-off.

Step 4 — Tick 3. Why this step? Repeating once more shows the swing is growing (), not settling. Three ticks is the minimum needed to prove the amplitude is increasing rather than a lucky bounce.

Figure s02 — stable vs unstable Euler. Sketch of the layout: one set of axes, horizontal axis "tick number ", vertical axis "simulated speed (rev/s)". A mint curve with round markers climbs smoothly and levels off near 5 — this is the safe run (, ratio ). A coral curve with square markers zig-zags violently: its points sit at (tick 1), then (tick 2), then (tick 3), each swing bigger than the last — this is the divergent run (, ratio ) we just computed by hand. A legend distinguishes "stable" (mint) from "diverges" (coral). Watch the coral markers land exactly on our three computed numbers.

Figure — Software-in-the-Loop (SIL) simulation — all software, simulated hardware

Answer: — divergent oscillation caused by the solver, not the SUT.

Verify: The three values above, and predicting instability. ✓


Example 6 — Cell F: the real-world word problem

Step 1 — Recall the stopping-distance formula. Why this step? We use because it answers exactly "how far to reach zero speed under constant deceleration" — derived from energy: the car must shed kinetic-energy-per-mass at rate per metre.

Step 2 — Plug in (units: m/s and m/s²). Why this step? Substituting the actual numbers turns the abstract formula into the concrete distance we can compare against the gap. Units: . ✓

Step 3 — Compare to the gap. m but obstacle is at m. Margin m. Why this step? A margin is only meaningful as (available gap) minus (needed distance); a negative result is the unambiguous "collision" signal.

Answer: No — the car needs 75 m but has only 50 m; it fails by 25 m. The controller must brake earlier or harder.

Verify: Solve for the maximum safe speed at 50 m: m/s. Since , collision confirmed. ✓


Example 7 — Cell G: the exam twist ("SIL passed, hardware failed")

Step 1 — SIL command (fresh data ). Why this step? SIL modelled zero delay, so it uses the current true speed — this is the "ideal" command the code was verified against.

Step 2 — Hardware command (stale data ). Why this step? The real ADC hands over 2-tick-old data. The same code line runs, but its input is the stale , so we recompute with instead of to see what the hardware truly does.

Step 3 — Compare the two commands. Why this step? Subtracting isolates the pure effect of the delay — one full unit of extra, unwanted push — which is the number that quantifies the abstraction error.

Answer: SIL says ; real hardware says . The extra unit is the invisible timing bug SIL missed.

Verify: Difference , and hardware pushes twice as hard as SIL predicted. ✓


Wrap-up: the matrix, filled

Recall Which example covered each cell?

A→Ex1, B→Ex2, C→Ex3, D→Ex4, E→Ex5, F→Ex6, G→Ex7 ::: Every cell of the scenario matrix now has a fully worked, verified example.

Return to the parent topic.