Supervisory mode logic for a power-split hybrid: requirements → Simulink/Stateflow model → fixed-point MISRA C 2012 — with the complete V&V evidence versioned in this repo.
▶ Try the interactive simulator — no install, runs in the browser.
Final project of the UFPE/Stellantis Postgraduate Technological Residency in Automotive Software Development (2026), building on a MathWorks HEV power-split example (see license note). The project began as team work; this baseline consolidates the per-author suites. My individual (Gustavo) contributions: adaptation of the MathWorks example into the reference model (supervisory logic and plant); scoping of the requirements document — elicitation and validation against the simulated model; the C architecture of the mode-logic module and its fixed-point rebuild for embedded targets; the original transition suites for the START, ICE, and HYBRID states; the model-level coverage report (line, branch, and MC/DC via Simulink Coverage), used to cross-check the gcc-14 code-level MC/DC; and the CI automation fixes (GitHub Actions build/test/MISRA pipeline).
This repository hosts the VMU (Vehicle Management Unit) baseline for a power-split hybrid electric vehicle (HEV). It brings together the system reference Simulink model with linked requirements, a manual C implementation of the supervisory mode logic, the final requirements document, an interactive web simulator, and a complete test, coverage, and CI infrastructure aligned with MISRA C 2012.
The VMU supervisory logic selects operating modes — STANDSTILL, EV, REGENB, START, ICE, HYBRID — based on driver power demand, vehicle speed, battery state of charge, and engine speed.
.
├── .github/workflows/
│ └── ci.yaml # Build, unit tests, and MISRA static analysis
├── Model/
│ ├── HEV_powersplit_adapted/ # Simulink reference model with requirements traceability
│ ├── HEV Powersplit_adapted_model_Document.pdf
│ └── Requirements.docx
├── inc/
│ └── mode_logic_team.h # Modular team-oriented API (current baseline)
├── src/
│ ├── mode_logic_team.c # C implementation of the supervisory logic
│ └── mode_logic_state_transitions.md
├── test/
│ ├── test_ev_transitions.c # EV-mode transition tests
│ ├── test_regenb_transitions.c # Regenerative-braking transition tests
│ ├── test_standstill_transitions.c # Standstill transition tests
│ ├── test_start_to_hybrid_ice_and_resets.c # START → HYBRID/ICE and reset paths
│ └── test_ice_hybrid_external_and_internal.c # ICE/HYBRID external + internal transitions
├── Test report/ # Versioned coverage artifacts (branch + MC/DC)
│ ├── README.md # Test-report tooling, layout, and how-to
│ ├── MCDC_matrix.md # Tests ↔ requirements ↔ MC/DC matrix
│ ├── summary.txt # Curated coverage summary
│ ├── branch_coverage_lcov/ # gcc-11 + lcov 1.14 (lines + branches)
│ ├── equivalence_live/ # Live chart↔C co-sim evidence (coverage + tm_report.pdf)
│ ├── mcdc_native_gcov14/ # gcc-14 -fcondition-coverage (native MC/DC)
│ └── mcdc_static_checker/ # mcdc-checker BDD tree-likeness check
├── doc/
│ ├── Requirements.pdf # Final requirements document
│ ├── ecu_fixed_point_memory_report.md # Fixed-point guidance for ECU software
│ └── section_4_transition_mapping_by_owner.md # Section 4 ownership and traceability
├── verification/
│ ├── simulink_c_equivalence.c # Replays the full Simulink stimulus through the C API
│ ├── equivalence_live/ # Live model↔C co-simulation harness (S-Function + Test Manager)
│ └── js_equivalence/ # Deterministic web-simulator ↔ C differential harness (state + MC/DC)
├── mode_logic.js # Supervisory logic — single source shared by the web sim + JS↔C harness
├── mode_logic_sim.html # Interactive web simulator (consumes mode_logic.js)
├── run_branch_coverage.sh # Branch/line coverage runner (gcc-11 + lcov)
├── run_mcdc_native.sh # Native MC/DC runner (gcc-14 + gcov-14 --conditions)
├── run_simulink_c_equivalence.sh # 265-row Simulink↔C behavioral equivalence check
├── misra.py # MISRA C 2012 verification script
├── MISRA_COMPLIANCE.md # Detailed MISRA rules and patterns
├── MISRA_QUICKSTART.md # Developer quick-start for MISRA
├── UnityExecution.md # Unity build/coverage instructions
└── README.md
Located in Model/HEV_powersplit_adapted/. It contains the supervisory model that the C baseline mirrors, together with requirements linked directly inside the model for traceability, plus overview material, scripts, image assets, and workflow support files. A standalone document describing the model is available at Model/HEV Powersplit_adapted_model_Document.pdf.
The current C implementation lives in:
src/mode_logic_team.cinc/mode_logic_team.h
The implementation provides:
- mode enumeration and I/O structures
- fixed-point input API for embedded use (
speed_dkph,p_dem_dkw,soc_q10000,weng_rpm) - integer threshold constants for transitions, aligned with the physical thresholds used by the Simulink model
- per-state handlers structured for traceability
- centralized output mapping for motor, generator, and ICE enables (no one-step delay)
- MISRA-oriented coding style:
constinputs, explicituint8_tbooleans, structured control flow
doc/Requirements.pdf— final requirements document.src/mode_logic_state_transitions.md— state names, threshold mapping, transition priorities, and expected behavior per mode.doc/section_4_transition_mapping_by_owner.md— Section 4 transitions mapped per responsible team member.doc/ecu_fixed_point_memory_report.md— fixed-point design guidance and its application to the VMU integer API.
The repository ships a transition-oriented test infrastructure built on the Unity framework. Tests are organized by the state-machine transition they exercise, rather than per author:
| Suite | File | Scope |
|---|---|---|
| Standstill transitions | test/test_standstill_transitions.c |
STANDSTILL entry/exit |
| EV transitions | test/test_ev_transitions.c |
Entries, exits, and guards for EV mode |
| Regen-B transitions | test/test_regenb_transitions.c |
Regenerative-braking transitions |
| START → HYBRID/ICE | test/test_start_to_hybrid_ice_and_resets.c |
Cranking path and reset behavior |
| ICE/HYBRID transitions | test/test_ice_hybrid_external_and_internal.c |
External and internal ICE/HYBRID transitions |
Note: the test files contain only
void test_*(void)functions — nomain(). The Unity test runner (main+RUN_TEST(...)calls) is generated automatically viaunity/auto/generate_test_runner.rbper test file. See Build and Run the C Tests below.
The Test report/ folder is the versioned home for coverage artifacts produced against src/mode_logic_team.c using the Unity tests under test/. Three independent toolchains are exercised:
- branch coverage with
gcc-11+lcov 1.14 - native MC/DC (Modified Condition / Decision Coverage) with
gcc-14(-fcondition-coverage) +gcov-14 --conditions - static MC/DC tree-likeness check with
mcdc-checker(Python + libclang-19)
Headline numbers (current state of the repo):
| Metric | Value | Source |
|---|---|---|
| Unity tests | 141 / 0 failures | 5 binaries combined |
| Functions | 100 % (41 / 41) | lcov |
| Lines | 97.28 % (286 / 294) | gcov-14 |
| Branches | 98.00 % (98 / 100) | lcov (lcov_branch_coverage) |
| C MC/DC condition outcomes | 100.00 % (86 / 86) | gcov-14 --conditions |
| Stateflow native MC/DC | 100.00 % (39 / 39) | Simulink Coverage R2026a |
| Simulink↔C equivalence (live co-sim) | 473 / 473 rows match (outputs + state) | S-Function co-simulation |
| Web-sim JS↔C differential | 473 / 473 rows match | js-c-equivalence CI job |
| Static MC/DC issues | 0 | mcdc-checker |
MC/DC is complete both under gcov-14 --conditions for the C implementation and under native Simulink Coverage for the Stateflow chart. See Test report/README.md for tool details and Test report/MCDC_matrix.md for the tests ↔ requirements mapping and per-decision MC/DC analysis.
mode_logic_sim.html is a standalone, dependency-free simulator for interactive exploration of the supervisory logic. Its transition logic now lives in mode_logic.js — a faithful atomic-predicate port of mode_logic_team.c that quantizes the physical inputs with the same to_u16/to_s16 rule and runs the integer guards unchanged (loaded as a plain script, so the page still opens from file:// with no build). That module is validated deterministically by verification/js_equivalence/: the ±1 LSB boundary stimulus (473 rows, reused from equivalence_live/) replays through the JS and matches the compiled C on state and outputs — 0 mismatches; MC/DC independence holds for every probed condition; and c8 branch coverage of the module is ~100%. This runs as its own CI job (js-c-equivalence), so the web simulator is now a CI-gated artifact. The authoritative gates remain the C implementation, the Unity tests, and the Simulink↔C / live-oracle equivalence.
Features:
- Real-time dashboard for the active mode and powertrain enables
- Animated gauges for vehicle speed (km/h) and engine speed (RPM)
- Telemetry charts: vehicle speed, power demand (
P_dem), state of charge (SOC), engine speed (wEng), and mode-transition history - Manual control panel for all state variables
- Pre-configured scenarios (EV, regenerative braking)
- Automated drive cycles (short cycle and 1-minute continuous cycle) with automatic phase display
- Transition history table with input/output values
Open it in any modern browser — no build step or server required.
- Open MATLAB.
- Navigate to
Model/HEV_powersplit_adapted. - Open
HEV_powersplit_adapted.slx. - See the local
README.mdinside the model folder for model-specific notes and the linked requirements view.
The full build and coverage flow is documented in UnityExecution.md. The transition test files contain only Unity test cases (void test_*(void)); the runner with main() is generated by Unity's helper script before compilation.
Place the Unity sources at unity/src/ (or clone the framework: git clone --depth 1 https://github.com/ThrowTheSwitch/Unity.git unity). The coverage scripts also accept UNITY_SRC_DIR and auto-detect /home/vmu/unity/src when unity/src is absent.
Two self-contained scripts at the repo root generate the runners, compile every test against src/mode_logic_team.c + unity.c, run the 5 binaries, and write the resulting artifacts directly into Test report/:
# Branch + line coverage (gcc-11 + lcov)
./run_branch_coverage.sh
# Native MC/DC (gcc-14 -fcondition-coverage + gcov-14 --conditions)
./run_mcdc_native.sh
# Simulink↔C behavioral equivalence (replays the 265-row MC/DC stimulus)
./run_simulink_c_equivalence.shThe supported order is (1) run_branch_coverage.sh → (2) run_mcdc_native.sh → (3) regenerate the static checker report if needed. See Test report/README.md for the prerequisite toolchain (gcc-11, gcc-14 from ppa:ubuntu-toolchain-r/test, lcov, libclang-19-dev, mcdc-checker) and the static-check command.
Note for Windows users: These scripts are intended to run in Linux/WSL and must use Unix line endings (LF). If a script was saved with Windows line endings (CRLF), Bash may fail with errors such as /usr/bin/env: 'bash\r': No such file or directory. Convert the scripts back to LF before running them.
Example:
sed -i 's/\r$//' run_branch_coverage.sh run_mcdc_native.sh
chmod +x run_mcdc_native.shUseful when you want to run just one transition suite (e.g. while debugging a specific test) or when the GCC 14 / lcov toolchain required by the scripts isn't available. This path skips the consolidated coverage report:
# 1. Generate the Unity runner (creates test/<TEST_FILE>_runner.c with main + RUN_TEST calls)
ruby unity/auto/generate_test_runner.rb test/<TEST_FILE>.c test/<TEST_FILE>_runner.c
# 2. Compile the suite together with the generated runner
gcc -std=c99 -Wall -Wextra \
--coverage -fprofile-arcs -ftest-coverage -O0 \
-Iinc -Iunity/src \
src/mode_logic_team.c \
unity/src/unity.c \
test/<TEST_FILE>.c \
test/<TEST_FILE>_runner.c \
-o test_runner
# 3. Run
./test_runnerThe project follows MISRA C 2012 to ensure reliability and safety, with automated verification on every pull request.
Status:
- Minimum: 0 high-severity violations
- Desirable: 0 high + 0 medium violations
- Goal: zero violations
Key guidelines applied:
constfor input parameters (Rule 8.13)- Unsigned-integer suffixes (Rule 10.1)
- Explicit type declarations (Rule 10)
- Structured control flow (Rules 15, 16) — including the Rule 15.5 single-exit pattern
- Static scope for internal functions (Rule 8.7)
References:
- MISRA_QUICKSTART.md — developer quick-start
- MISRA_COMPLIANCE.md — detailed rules and patterns
Local verification:
-
Install
cppcheck:# Windows winget install Cppcheck.Cppcheck # macOS brew install cppcheck # Linux (Ubuntu/Debian) sudo apt-get install cppcheck
-
Run static analysis with the project's MISRA addon (same scope and flags as CI — only
src/andinc/are checked;--inline-supprhonors the in-sourcecppcheck-suppressdirectives that document the public-API false positives):cppcheck --enable=all --addon=misra --inline-suppr \ --suppress=missingIncludeSystem -Iinc src/ inc/ # or use the bundled script python misra.pyNote: the
test/folder is intentionally excluded from MISRA — Unity macros and test patterns are not meant to be MISRA-compliant. -
Build with strict warnings:
gcc -Wall -Wextra -Werror -Iinc -o test_runner src/mode_logic_team.c test/<TEST_FILE>.c
GitHub Actions (.github/workflows/ci.yaml) runs on every pull request and on pushes to main, performing:
- Static analysis — cppcheck general checks plus a dedicated MISRA C 2012 pass. Because the Ubuntu
cppcheckpackage omits the Python addons, the workflow fetches them from the upstream cppcheck source (matching the installed version) and then runs the MISRA addon manually overcppcheck --dumpoutput, so any addon-internal Python error is visible in the log instead of being hidden behind a generic exit code. - Build and test — fetches Unity, generates a runner per test file with
unity/auto/generate_test_runner.rb, then compiles each transition suite againstsrc/mode_logic_team.c+unity.c+ the generated runner and runs the resulting binary.
PR checks and downloadable analysis reports are available under the workflow run's "Checks" / "Artifacts" tabs.
src/mode_logic_team.cis the single C implementation maintained onmain. Legacy per-author MC/DC suites and thePerson_E_Gustavo/shared_testsfolders were consolidated into the transition-oriented suites listed above.mode_logic_sim.htmlconsumesmode_logic.js(single source of truth), a faithful atomic-predicate port ofmode_logic_team.c. It is validated deterministically byverification/js_equivalence/(473-row ±1 LSB differential vs the compiled C + Python mirror, 0 mismatches on state and outputs; MC/DC independence; ~100% branch coverage) and gated by thejs-c-equivalenceCI job. The C implementation, the Unity tests, and the Simulink↔C equivalence check remain the authoritative source of truth.- Generated Simulink artifacts under
Model/HEV_powersplit_adapted/slprj/are intentionally not versioned; they are produced locally on first build. - The
Test report/folder is versioned on purpose (the.gitignorewhitelists it). Localunity/clones, raw.gcov/.gcda/.gcno/.infooutsideTest report/, and barecoverage_html/directories are ignored to keep CI clean. - The public-API entry points
ModeLogic_InitandModeLogic_Stepcarry inlinecppcheck-suppressdirectives formisra-c2012-8.7(Rule 8.7 — internal linkage) andunusedFunction. Both are false positives: the functions are consumed bytest/and external clients, so they cannot bestaticand are not actually unused.
Model content derived from MathWorks example assets is governed by its own license file under:
Model/HEV_powersplit_adapted/LICENSE.md