Skip to content

Decision register / Project 2 · Decision story

Decision Log & Lessons Learned28 decisions: 12 ADRs + 16 from lessons learned

Twelve architecture records and sixteen project lessons show what was decided, why alternatives lost, and what changed after testing.

Read first / How to read this page

Architecture Decisions (12 ADRs) are scored and tiered by impact. Decisions from Lessons Learned (16 entries) show what we learned and what we decided as a result. Expand any entry for details.

Total Decisions: 28
Architecture ADRs: 12 (scored)
Lesson Decisions: 16 (4 categories)

Architecture Decisions

12 ADRs

3 Architecture | 5 Design | 4 Process

From Lessons Learned

16 Decisions

Safety | Infra | Testing | Process

Scoring Model

Cost | Time | Safety | Resume

1-3 each, total /12

Top Score

ADR-005: 10/12

STM32G474RE MCU platform choice

Architecture Decisions

Filter by tier:
T45/12ADR-001: Structure docs under ASPICE process areas

Cost 1 | Time 1 | Safety 1 | Resume 2

Decision: Organize documentation under docs/aspice/ by process area (SYS, SWE, SUP, MAN, HWE)

Why it wins: File-based ALM in Git gives assessor-friendly structure at zero cost with full version control.

T44/12ADR-002: Keep master-plan.md as source baseline

Cost 1 | Time 1 | Safety 1 | Resume 1

Decision: Keep docs/plans/master-plan.md as the single strategic plan, with ASPICE execution plans as operational breakdowns

Why it wins: Master plan provides strategic context (architecture, BOM, demos) that execution plans don't cover.

T44/12ADR-003: Create central docs/research/ repository

Cost 1 | Time 1 | Safety 1 | Resume 1

Decision: Centralize all external references and research notes in docs/research/

Why it wins: Git-versioned research log keeps provenance with the codebase. Greppable and auditable.

T45/12ADR-004: Add MAN.3 live tracking set

Cost 1 | Time 1 | Safety 1 | Resume 2

Decision: Add progress dashboard, issue log, decision log, and gate readiness checklist to MAN.3

Why it wins: Markdown tracking files in-repo satisfy ASPICE evidence requirements and baseline with the code.

T110/12ADR-005: STM32G474RE Nucleo for 3 zone ECUs

Cost 2 | Time 2 | Safety 3 | Resume 3

Decision: Use 3x STM32G474RE Nucleo-64 boards for CVC, FZC, and RZC zone controllers. 3x FDCAN, 5x ADC, CORDIC+FMAC, 170 MHz Cortex-M4F.

Why it wins: Gains CAN-FD, extra ADCs for dual-sensor plausibility, and HW math accelerators vs F446RE. Arduino Mega disqualified (8-bit/8KB/no CAN).

T19/12ADR-006: AUTOSAR Classic layered BSW

Cost 1 | Time 2 | Safety 3 | Resume 3

Decision: Custom AUTOSAR Classic-inspired layered BSW: MCAL, EAL, Services, RTE — 16 modules, ~5,000 LOC shared across 3 STM32 ECUs.

Why it wins: 16+ automotive resume keywords, ASPICE SWE.2/SWE.3 compliance, HW abstraction for SIL testing. 10/10 resume impact vs 3/10 bare-metal.

T27/12ADR-007: POSIX SocketCAN for simulated ECU MCAL

Cost 1 | Time 2 | Safety 1 | Resume 3

Decision: POSIX SocketCAN API as MCAL layer for 3 simulated ECUs in Docker — 100% code reuse between physical and simulated ECUs.

Why it wins: Only option achieving 100% code reuse. Python-can destroys AUTOSAR portability story. Vector CANoe is $10K+.

T26/12ADR-008: BMW vsomeip for SOME/IP demo

Cost 1 | Time 1 | Safety 1 | Resume 3

Decision: vsomeip (COVESA/BMW) for service-oriented communication between simulated ECUs and Pi gateway.

Why it wins: 4-7 days for working SOME/IP demo with BMW/COVESA credibility. Custom reimplementation wastes 4-8 weeks.

T25/12ADR-009: Docker containers for simulated ECU runtime

Cost 1 | Time 1 | Safety 1 | Resume 2

Decision: Run 3 simulated ECUs (BCM, ICU, TCU) in Docker containers with virtual CAN (vcan/vxcan) interfaces.

Why it wins: Same 1-2 days as native processes but adds isolation, reproducibility, and CI/CD portability. QEMU costs 3-15x more setup.

T27/12ADR-010: Unity + CCS Test + pytest for testing

Cost 1 | Time 1 | Safety 3 | Resume 2

Decision: Unity (ThrowTheSwitch) for C unit testing, CCS built-in for TMS570, pytest for Python gateway/cloud.

Why it wins: Pure C, 2-4 hours setup vs 2-4 days gtest. CMock auto-generates mocks. CUnit is abandoned (2018).

T17/12ADR-011: CAN 2.0B at 500 kbps (no CAN FD)

Cost 1 | Time 1 | Safety 3 | Resume 2

Decision: CAN 2.0B at 500 kbps for all inter-ECU communication. TMS570 DCAN requires classic CAN. 35% bus utilization — no bandwidth pressure.

Why it wins: Zero-cost baseline. CAN FD adds 8-13 days for unneeded bandwidth. Ethernet requires replacing 3 MCU boards + 6-10 weeks.

T26/12ADR-012: AWS IoT Core + Timestream + Grafana

Cost 1 | Time 1 | Safety 1 | Resume 3

Decision: AWS IoT Core (MQTT) + Amazon Timestream (time-series DB) + Grafana for cloud telemetry, batched 1 msg/5sec for free tier.

Why it wins: ~$4-7/month with highest automotive credibility (Mercedes, VW, Toyota use AWS). Azure is 4-5x more expensive. Self-hosted lacks resume value.

Decisions from Lessons Learned

Filter by category:
SafetyLL-001: Execute security hardening in strict phase order

Decision: Always harden in order: emergency fixes → auth/abuse protection → access control → API hardening → monitoring → verification gate. Never skip to advanced features before basics are locked.

What we learned: Jumping to OAuth and reCAPTCHA before basic auth, rate limiting, and CSRF were solid caused significant rework across 10 phases.

  • Fail-closed pattern: reject on missing config, never silently skip auth
  • Use generic SMTP from day one — vendor SDK lock-in (Resend) cost multiple phases to remove
  • Audit marketing claims against code BEFORE launch — stats go stale, absolutes are wrong
InfrastructureLL-002: Test CI tool version first, not local

Decision: Always validate against the CI tool version (cppcheck 2.13) before assuming local results (2.17) will pass. Pin tool versions in CI config.

What we learned: cppcheck 2.13 (CI/Ubuntu apt) vs 2.17 (local/pip) had 6 incompatibilities that only surfaced in CI after pushing.

  • No # comments in suppressions.txt — cppcheck 2.13 can't parse them
  • .gitignore blocking suppressions.txt — use ! exception when ignoring directory patterns
  • 1,536 to 0 violations in 1 day with systematic triage: mandatory first, required next, advisory last
TestingLL-003: Use stdint.h types in host tests, not platform types

Decision: Use stdint.h fixed-width types (uint32_t) in all host-compiled test code. Never rely on AUTOSAR platform types that assume ILP32.

What we learned: LP64 (host) vs ILP32 (target): uint32 mapped to unsigned int on target but unsigned long on host, causing 99 type mismatch failures in CI.

  • Source inclusion pattern for Unity tests — include .c files, don't link; standard embedded TDD
  • Mock state must be cleared in setUp() — zero ALL mock globals before each test, no exceptions
  • Header guard collisions across test files (DEM_H, E2E_H) — prefix guards with TEST_ namespace
InfrastructureLL-004: Use 4-bit modular arithmetic for heartbeat alive counters

Decision: Heartbeat alive counter wraps at 4-bit (0-15) per AUTOSAR E2E Profile 1, not 8-bit. Use modular arithmetic and single monitor per ECU.

What we learned: Duplicate heartbeat monitors and 8-bit wrap assumption caused persistent UI flickering in the SIL demo dashboard.

  • Docker Compose v2: 'docker compose' (space) not 'docker-compose' (hyphen) — breaks scripts silently
  • NET_RAW capability required for CAN in containers — not obvious from error messages
  • RTE_MAX_SIGNALS too small causes silent init failure — validate signal count at startup
ProcessLL-005: Single source of truth for cross-document parameters

Decision: Define every shared parameter (CAN bit timing, sensor specs, pin assignments) in exactly one canonical document. All other docs reference, never copy.

What we learned: CAN bit timing (87.5% vs 80% sample point) was inconsistent across 4 docs. ACS723 sensitivity was wrong in 5 places.

  • CAN IDs most duplicated data — grep project-wide after any change to catch mismatches
  • MG996R servo: 180-degree, not 360-degree (Amazon BOM listing error) — always verify datasheet
  • Cross-doc review checklist catches what per-doc review misses
TestingLL-006: Enforce test-first with pre-commit hook for all BSW modules

Decision: Block writes to firmware .c source files unless corresponding test_*.c exists. Enforce AUTOSAR BSW layer build order: MCAL, ECUAL, Services, RTE.

What we learned: Without enforcement, developers skip tests under time pressure. Hook-enforced TDD produced 16 modules with 1,067 tests and zero skipped modules.

  • Table-driven state machines and dual sensor plausibility patterns proven across all BSW modules
  • E2E as standalone module: dependency-free, reusable across physical and simulated ECUs
  • Phase-based execution with status table keeps momentum and prevents scope creep
ProcessLL-007: Use HITL-LOCK markers for human-AI co-development reviews

Decision: Protect human-authored review comments with HITL-LOCK markers. Use Why/Tradeoff/Alternative structure. Date every review comment for CM traceability.

What we learned: AI reformatted and moved human review comments without markers. 443+ HITL comments across 29 docs would be unmanageable without protection.

  • HITL-LOCK markers are immutable once set — AI must never edit, reformat, or delete locked content
  • Per-requirement lessons-learned files (SYS-NNN) keep insights linked to their source
  • Consolidated lessons-learned folder better than scattered per-doc files
ProcessLL-008: Use file-based ALM over heavyweight tools

Decision: Git + Markdown covers 90% of DOORS/Polarion/Jama functionality at 0% licensing cost. ADR format with 4-dimension scoring enables cross-decision comparison.

What we learned: 7-ECU hybrid architecture (4 physical + 3 simulated) proved that CAN bus doesn't distinguish hardware from Docker — file-based tools scale the same way.

  • Decision audit script catches undocumented decisions via heuristic grep
  • Tier assignment (T1-T4) follows from scores, not arbitrary classification
  • Zonal architecture decision driven by resume value + learning opportunity, not just technical merit
SafetyLL-009: Use GND-only fault injection for budget HIL setups

Decision: Skip VBAT fault injection on budget hardware — GND-only + open-circuit covers ~70% of fault modes safely. Add preemptive Zener protection on every channel.

What we learned: VBAT fault injection on unprotected Nucleo boards risks destroying the MCU. A EUR 0.10 Zener diode prevents a EUR 16 board replacement.

  • DIY HIL is legitimate beyond hobby tier — 7-ECU AUTOSAR with 1,067 tests is industry-relevant
  • Relay-based fault injection matrix: MUX topology reduces relay count from N*M to N+M
  • Needle-bed adapter pattern from professional HIL benches scales down to budget hardware
SafetyLL-010: Document shared SPI bus as CCF in Dependent Failure Analysis

Decision: 1oo2D on shared SPI bus is acceptable if detection path exists (CRC + plausibility → safe state). Shared bus failure is a detected multi-point fault. Always document in DFA.

What we learned: Initial design assumed SPI bus independence. HITL review revealed shared SPI is a Common Cause Failure that must be explicitly analyzed.

  • Both CRCs failing simultaneously = detected fault, not undetected — makes the safety argument
  • DMA/driver complexity is not a valid argument against SPI1+SPI2 — config is trivial copy-paste
  • Separate SPI buses justified only if ASIL decomposition requires proven independence
ProcessLL-011: Block CI merge on traceability gaps, not just warn

Decision: CI must block merge on traceability gaps — advisory checks are universally ignored. Stub scripts that pass CI give false confidence; build tooling properly or skip entirely.

What we learned: 4 broken requirement links and 6 untested SWRs were invisible until automated traceability checking found them.

  • trace-gen.py validates SG → FSR → TSR → SSR → code → test chain completeness
  • Orphan requirements (no implementation) and orphan tests (no requirement) both caught
  • Traceability report generated on every CI run — assessors can verify at any time
SafetyLL-012: Build safety case top-down from HARA, never bottom-up

Decision: Always build HARA → Safety Goals → FSR → TSR → SSR. Bottom-up produces rationalization, not defense. FTTI must be derived from mechanisms, not guessed.

What we learned: Attempting to write SSRs before completing HARA led to requirements that didn't trace to any safety goal — wasted effort.

  • S3/E4/C3 = ASIL D for drive-by-wire — the matrix numbers are unambiguous, no debate needed
  • Safety goals need FTTI justification: detect_time + react_time derived from concrete mechanisms
  • GSN (Goal Structuring Notation) makes the argument structure visible and auditable
InfrastructureLL-013: Design HAL for testability — it enables SIL for free

Decision: HAL abstraction designed for unit testing is the same abstraction that enables SIL simulation. SocketCAN uses identical API for vcan and real CAN — swap by interface name only.

What we learned: POSIX SocketCAN backend was added for SIL, but turned out to be the exact same abstraction boundary we needed for host-based unit tests.

  • POSIX backend files are MISRA-exempt — blanket suppress rules 21.5, 21.6, 21.8, 21.10, 17.7 from day one
  • vcan module auto-load + error clearing on container restart prevents stale CAN state
  • 100% firmware code reuse between physical ECU and Docker container validates the abstraction
InfrastructureLL-014: Make fault injection sequences deterministic and state-aware

Decision: Every fault injection must follow a defined inject → detect → react → recovery sequence. DTC broadcasts need arbitration backoff. ML anomaly baselines vary per operating state.

What we learned: Random fault injection produced unreproducible results. ML anomaly scores stuck at baseline because thresholds didn't account for different operating states.

  • DTC broadcast needs randomized 0-50ms backoff to prevent CAN bus flooding from simultaneous DTCs
  • E-STOP event spam on reset — need proper fault clear sequence before re-enabling
  • Controller-viewer lock pattern prevents multi-user conflicts in live demo
TestingLL-015: Run SIL integration tests nightly — mocks miss real integration bugs

Decision: SIL tests catch integration issues mocks cannot: ECU-to-ECU communication, E2E protection, fault state transitions. Run nightly in CI. Unknown test step actions must fail loudly.

What we learned: COM TX/RX bridge timing issues and E2E sequence counter mismatches were invisible in unit tests — only surfaced in multi-ECU SIL runs.

  • COM TX/RX bridge is the most critical SIL component — build and test it before writing scenarios
  • Fail-closed applies to test infrastructure too — unknown step actions must error, not skip
  • Plant simulator thermal model needs tuning per operating point, not one-size-fits-all
InfrastructureLL-016: Verify every component against manufacturer datasheet before ordering

Decision: Never trust marketplace listings — always download and verify the manufacturer datasheet for every parameter. Include full part number including variant suffix in BOM.

What we learned: MG996R listed as 360-degree was actually 180-degree. ACS723-05A vs ACS723-20A have 4x different sensitivity (400 vs 100 mV/A). Both caused rework.

  • SOT-23 components need breakout boards — check package vs assembly method before ordering
  • Lead time for automotive-grade components can be 8-12 weeks — order early, prototype with commercial grade
  • BOM must include: full part number, manufacturer, distributor link, datasheet link, package type

Scoring Model

Dimension1 (Low)2 (Medium)3 (High)
Cost< $50$50-$500> $500
Time< 1 week1-4 weeks> 4 weeks
SafetyQMASIL A-CASIL D
ResumeGenericIndustry-relevantTop keyword

Decision Process

Captured

  • Problem and constraints documented
  • At least 2 alternatives listed with effort estimates

Evaluated

  • Cost/Time/Safety/Resume scored (1-3 each)
  • Tradeoff rationale written with explicit comparison

Baselined

  • Approved with ADR-NNN identifier and tier classification
  • Linked to implementation evidence in code and docs

Why This Log Is Strong

Every decision has 2 alternatives with effort estimates
  • Not just 'we chose X' — each ADR shows what was rejected and why
  • Effort in hours + dollars makes tradeoffs concrete
  • Auditable by any ASPICE assessor or interviewer
4-dimension scoring enables cross-ADR comparison
  • Cost, Time, Safety, Resume consistently scored 1-3
  • ADR-005 (MCU, 10/12) vs ADR-003 (research repo, 4/12) shows clear priority ranking
  • Tier assignment (T1-T4) follows from scores — not arbitrary
Decisions trace to execution evidence
  • ADR-006 (AUTOSAR BSW) traces to 16 implemented modules and 195 tests
  • ADR-005 (STM32G474RE) traces to 6 CVC SWCs and 88 tests
  • 16 lesson-derived decisions link experience back to adopted project standards

Score register / ranked ADRs

Top Scoring Decisions

ADR-005

10/12

STM32G474RE MCU platform

ADR-006

9/12

AUTOSAR Classic layered BSW

ADR-007

7/12

SocketCAN simulation MCAL

ADR-010

7/12

Unity + CCS + pytest testing

ADR-011

7/12

CAN 2.0B at 500 kbps

Related Portfolio Pages

Use this summary together with the Safety Platform page for a complete project overview.