2. Virtual validation landscape

Physical test coverage planning rarely starts from a blank sheet. By the time a coverage plan is drafted, the system under test already exists in several representations: a control model in a simulation tool, generated production code, a compiled binary on a target ECU, or a bench with real actuators and sensors. Each representation defines a different execution context, and the choice of context determines what can be stimulated, what can be observed, and therefore which coverage items are even measurable. This chapter maps the virtual validation landscape — MiL, SiL, PiL, and HiL — and shows how each context constrains and enables coverage planning.

Four contexts, one system under test

The standard taxonomy is defined by what executes the control logic and what surrounds it. In Model-in-the-Loop (MiL), the controller is an executable model (for example, a Simulink model) and the plant is a model; both run on a host PC. In Software-in-the-Loop (SiL), the controller is production C code compiled for the host, while the plant remains simulated. Processor-in-the-Loop (PiL) moves the controller code onto the target processor (or an instruction-set simulator) while the plant stays simulated. Hardware-in-the-Loop (HiL) places the real ECU — with its I/O drivers, operating system, and communication stacks — against a real-time simulated plant with emulated electrical interfaces.

The critical insight for coverage planning is that these are not four interchangeable ways to "run the same tests." They differ in three dimensions that directly affect coverage: stimulus fidelity (what signals can be injected and at what rate), observability (which internal states and branches can be instrumented), and timing determinism (whether execution is simulated time, host time, or real time). A coverage goal that is trivially achievable in MiL may be unmeasurable in HiL, and vice versa.

Virtual validation contexts and their coverage observability Four execution contexts (MiL, SiL, PiL, HiL) shown as columns, each with controller representation, plant representation, timing base, and the coverage types that are measurable versus constrained. MiL SiL PiL HiL Controller Plant Timing base Coverage observable Coverage constrained Executable model Plant model Simulated time Statement, branch, MC/DC on model, state & transition No target timing, no I/O driver paths Production C code Plant model Host time Statement, branch, MC/DC on code, function coverage Compiler optimizations may merge branches Code on target CPU Plant model Target clock Code coverage via on-chip trace, timing behavior Trace buffer depth limits long runs Real ECU + OS + I/O Real-time plant sim Real time Interface & signal coverage, end-to-end scenario coverage Internal code coverage often not accessible Increasing integration fidelity → (and increasing constraint on internal observability)

What each context lets you measure

In MiL, the model is fully instrumented. Coverage tools can report statement, branch, and condition coverage on the model itself, plus state and transition coverage for Stateflow or similar state machines. Because time is simulated, you can run thousands of scenarios in minutes and inject any signal without electrical constraints. This makes MiL the natural home for algorithmic coverage: have all operating modes been entered? Have all transition guards been evaluated both true and false? Have all saturation and anti-windup branches been exercised?

SiL shifts the target to production code. Coverage is now measured on the compiled C (or C++) implementation, typically with gcov/llvm-cov or a commercial tool. This is where you discover that the model and the code are not the same object: compiler optimizations may merge branches, hand-written integration code adds paths the model never had, and fixed-point quantization introduces new decision outcomes. A branch that was reachable in MiL may become unreachable in SiL, or a new defensive branch may appear that no functional stimulus can reach. Coverage planning must therefore treat SiL as the authoritative context for code-level structural coverage.

PiL keeps code coverage but moves execution to the target processor. On-chip trace (ETM, NEXUS, or similar) or instrumented builds provide the measurement. The value here is timing: you can observe whether worst-case execution paths are actually taken, and whether interrupt-driven branches behave as designed. The constraint is trace bandwidth and buffer depth — long scenarios may overflow the trace and silently lose coverage data, so runs must be segmented.

HiL is where internal code coverage usually becomes inaccessible. The ECU runs unmodified production software, often without instrumentation, and the test system sees only the electrical interface. Coverage planning in HiL therefore shifts to interface and scenario coverage: every sensor range, every bus message and signal value, every fault-injection state, every operating-mode transition observable at the I/O boundary. This is not a lesser form of coverage — it is the coverage that matters for integration and safety validation, and it must be planned with the same rigor.

Planning coverage across the V-model

The practical consequence is a layered coverage strategy rather than a single number. A defensible plan assigns each coverage objective to the context where it is both measurable and meaningful:

Coverage objectivePrimary contextWhy
State / transition coverage of control logicMiLFull model instrumentation, fast simulated time
Statement, branch, MC/DC on production codeSiLCode is the object under test; host coverage tools
Timing-path and interrupt branch coveragePiLTarget clock and on-chip trace
I/O range, bus signal, fault-injection coverageHiLOnly the electrical boundary is observable
End-to-end scenario coverageHiLReal-time closed loop with real interfaces

Two failure modes recur when this layering is ignored. The first is context mismatch: a team reports 95% branch coverage from MiL and treats the code-level requirement as satisfied, when the production code has never been measured. The second is unreachable-target chasing: a team tries to hit 100% MC/DC in HiL, where internal branches are invisible, and burns bench time on an objective the context cannot support. Both are planning errors, not testing errors.

Rule of thumb: assign a coverage objective to the earliest context in which it is measurable, then re-verify the subset that integration can perturb (quantization, scheduling, I/O) in the later context. Coverage flows left-to-right; confidence flows right-to-left.

A worked planning example

Consider a battery-management slave ECU with a state machine (Init → Standby → Charging → Balancing → Fault) and a CAN interface. A coverage plan might specify:

  • MiL: all 5 states entered, all 11 transitions taken, all guard conditions evaluated true and false — target 100% state/transition coverage, achieved with a scenario sweep over voltage, temperature, and current setpoints.
  • SiL: 100% statement and branch, ≥90% MC/DC on the balancing and fault-detection modules, measured with gcov; any unreachable branch documented with a justification (defensive code, hardware interlock).
  • PiL: verify that the fault-detection ISR path is exercised under target timing; confirm no trace overflow by segmenting the run into 30-second windows.
  • HiL: every CAN signal exercised across its valid range plus out-of-range and timeout faults; every state transition observed at the bus; fault-injection coverage for open-load, short-to-ground, and over-temperature.

Each row has a concrete measurement method, a target, and a context in which that target is achievable. That is what a coverage plan looks like when it is grounded in the virtual validation landscape rather than in a single tool's report.

Common pitfall: coverage percentages from different contexts are not comparable. A 90% branch figure from SiL and a 90% scenario figure from HiL measure different things on different objects. Report them separately, with the context and tool named, or the plan will be misread as a single quality metric.

The next planning decision — how to translate these context-specific objectives into concrete test cases and stimulus — depends on understanding what each context can inject and observe at its boundary, which is the subject of the following chapter.