Skip to content

Project — Diagnostic-Capable Body Controller

This project combines every module of Level 3 into one coherent design: a body control module (BCM) that manages doors, lighting, and a window lift, structured in AUTOSAR-style layers, running on a CAN FD network described by a real DBC, backed by an SBC watchdog, MPU-partitioned by integrity level, secure-booted, and reachable for diagnostics and calibration through the gateway you'd find in a production vehicle architecture. Nothing here is new protocol content — it is the architecture-level integration that a real automotive software team does after each subsystem works in isolation.

System architecture

┌───────────────────────────────────────────────────────────┐
│  Application SWCs (module 1)                                │
│  DoorControl | LightingManager | WindowLift | DiagManager    │
├───────────────────────────────────────────────────────────┤
│  RTE (generated glue)                                        │
├───────────────┬───────────────────────────────────────────┤
│  BSW Services  │  ECU Abstraction                             │
│  NvM, Dcm, Dem │  CanIf, PduR (routes Dcm<->UDS, XCP<->calib) │
├───────────────┴───────────────────────────────────────────┤
│  MCAL (RTD, module 2): Can, Port, Dio, Mcu, Fee, Lpuart      │
├───────────────────────────────────────────────────────────┤
│  MPU partitions (module 6): QM (lighting) | ASIL-B (doors)   │
├───────────────────────────────────────────────────────────┤
│  S32K3 + SBC (module 5, external watchdog + fault pin)       │
│  HSE (module 7: secure boot, signs every flashed image)      │
└───────────────────────────────────────────────────────────┘
      │ CAN FD (module 3)         │ LIN (module 4)
      ▼                            ▼
  Vehicle network,            Window motor,
  gateway to Ethernet          mirror actuator
  diagnostic bench (module 8)

Partitioning by integrity level

Door lock control interacts with occupant safety (an unintended unlock above a speed threshold, or a lock engaging on a child in a doorway, are real hazard scenarios); lighting control does not carry the same consequence. This project treats DoorControl as the higher-integrity partition and LightingManager as QM, enforced by the MPU from module 6:

/* Partition boundary enforced identically to module 6's pattern —
   DoorControl's RAM region and LightingManager's RAM region are
   mutually inaccessible; a bug in one cannot corrupt the other */
static const mpu_region_cfg_t bcm_mpu_regions[] = {
    { .start_addr = DOORCTRL_RAM_BASE, .end_addr = DOORCTRL_RAM_END,
      .master_id = MPU_MASTER_CORE, .read_enable = true,
      .write_enable = true, .execute_enable = false },
    { .start_addr = LIGHTING_RAM_BASE, .end_addr = LIGHTING_RAM_END,
      .master_id = MPU_MASTER_CORE, .read_enable = true,
      .write_enable = true, .execute_enable = false },
    { .start_addr = SHARED_MAILBOX_BASE, .end_addr = SHARED_MAILBOX_END,
      .master_id = MPU_MASTER_CORE, .read_enable = true,
      .write_enable = false, .execute_enable = false }, /* Lighting's view: read-only */
};

DoorControl publishes vehicle-speed-gated lock state into the shared mailbox; LightingManager (QM) can read it (e.g. to flash hazards on lock/unlock) but cannot write it — the same single-writer discipline from module 6, now applied for a concrete reason instead of an abstract example.

The CAN FD network (DBC-defined)

BO_ 1024 BCM_DoorStatus: 8 BCM
 SG_ FL_Door_Locked  : 0|1@1+  (1,0)    [0|1]      ""      Gateway
 SG_ FR_Door_Locked  : 1|1@1+  (1,0)    [0|1]      ""      Gateway
 SG_ Vehicle_Speed   : 8|16@1+ (0.01,0) [0|327.67] "km/h"  BCM

BO_ 1025 BCM_LightingStatus: 4 BCM
 SG_ Headlamp_State  : 0|2@1+  (1,0)    [0|3]      ""      Gateway
 SG_ Hazard_Active   : 2|1@1+  (1,0)    [0|1]      ""      Gateway

BO_ 1280 BCM_WindowCmd: 4 Gateway
 SG_ Window_Position_Req : 0|8@1+ (1,0) [0|100]    "%"     BCM

Vehicle_Speed is the interlock signal — DoorControl's auto-lock logic from module 1 reads it, and the DBC formalizes its scale exactly as module 3 requires: no hand-decoded bit offsets anywhere in application code, generated pack/unpack functions only.

Diagnostics, secure flashing, and calibration coexist on one gateway

/* PduR-style routing: the same CAN FD interface carries UDS diagnostic
   traffic (0x7E0/0x7E8, Level 2) and, in dev builds only, XCP calibration
   traffic (module 9) on separate CAN IDs — production builds compile
   XCP out entirely per module 9's concern about remote memory write */
void PduR_RxIndication(uint32_t can_id, const uint8_t *data, uint8_t len)
{
    if (can_id == UDS_REQUEST_ID) {
        Dcm_RxIndication(data, len);
    }
#if (XCP_ENABLED == STD_ON)  /* never STD_ON in a production build */
    else if (can_id == XCP_REQUEST_ID) {
        Xcp_ProcessCommand(data, len);
    }
#endif
    else {
        CanIf_RxIndication(can_id, data, len); /* normal signal traffic */
    }
}

Reflashing this BCM in the field goes through Level 2's UDS bootloader, now gated by module 7's HSE signature check before any image commits — the same UDS 0x34/0x36/0x37 sequence, with a mandatory verify step inserted before flash-commit that was absent in the Level 2 version.

Safety mechanisms tying it together

  • SBC windowed watchdog (module 5) services on the 10 ms scheduler tick; a window violation or an unexpected MPU fault escalation both route to the SBC fault pin path, which is the one mechanism trusted to work even if the application core has locked up entirely.
  • MPU violation handling (module 6) on DoorControl's partition forces that task to a halted state and reports via Dem, rather than attempting recovery — consistent with the "never resume a task whose memory model is already inconsistent" rule from that module.
  • Secure boot (module 7) ensures the image running this whole stack was verified by the HSE before the application core ever executed a single instruction of it.

Cheat sheet

Layer Module Role in this project
Application/RTE 1 DoorControl, LightingManager, WindowLift SWCs
MCAL 2 RTD Can, Port, Fee drivers under the SWCs
Network 3 DBC-defined CAN FD frames for door/light/window signals
LIN 4 Window motor / mirror actuator as a LIN slave off the BCM
Watchdog 5 SBC windowed watchdog + fault pin, independent of the app core
Partitioning 6 MPU: DoorControl (higher integrity) vs LightingManager (QM)
Secure boot 7 HSE-verified image before application core release
Gateway 8 Optional Ethernet bridge to a diagnostic/test bench
Calibration 9 XCP in dev builds only, compiled out for production

How It Actually Works

A body controller integrating CAN FD, diagnostics, secure flashing, and calibration on one gateway is really a study in how independently-clocked hardware subsystems share the crossbar and interrupt fabric without stepping on each other. FlexCAN's message-buffer matching engine, the eDMA channels feeding LPUART/ADC data, and the HSE's crypto operations for secure flashing all run as separate hardware state machines with their own clock trees (as covered in the clocks module) — the CPU's job in this design is almost entirely to arbitrate software priorities (which task handles which completed hardware event first) rather than to drive any of these operations bit-by-bit itself.

Running secure flashing (an HSE-verified firmware update) concurrently with live CAN traffic and diagnostics works only because those operations don't actually contend for the same physical resource: HSE's crypto engine runs on its own isolated core, flash programming's high-voltage charge-pump operation is asynchronous once triggered (covered in the flash module) and doesn't stall the main core's bus access to other peripherals, and FlexCAN keeps receiving/transmitting via its own arbitration hardware regardless of what the main core is doing — the actual constraint is usually flash bus access contention (the main core can't execute code from a flash sector currently being erased/programmed), which is why bootloaders typically run the flash-write routine from RAM.

Partitioning this design by integrity level (as the surrounding text describes) is only meaningful if the MPU regions actually gate every relevant bus master's access — which circles back directly to the DMA/MPU gap discussed in the freedom-from-interference module: a body controller design has to explicitly verify which bus masters the chosen S32K variant's protection hardware actually covers, not assume MPU regions protect against every possible corruption path.

(Described from S32K reference manual chapters on FlexCAN, FTFC, and HSE, synthesized for this project; not measured on physical silicon in this course.)

Stretch goals

Extend the project past the baseline design. (1) Add a fourth SWC, SeatMemory, storing calibratable seat positions in an XCP-writable RAM page (module 9) that a bench tool can tune live, while keeping it fully isolated by the MPU from DoorControl's partition. (2) Implement the Ethernet gateway path from module 8: bridge BCM_DoorStatus onto a SOME/IP message on an Ethernet segment, and write the threat-boundary note module 8 asked for — is door status safety-relevant enough to need SecOC-style authentication before it crosses that gateway? (3) Simulate an MPU violation in LightingManager (a deliberate out-of-bounds write) under load, and confirm on a scope or via UART trace that the SBC watchdog window is unaffected — proving your two safety mechanisms are actually independent, not just independently documented. (4) Write the anti-rollback design from module 7 concretely for this BCM: define the version-counter storage location and the exact check your secure boot sequence performs before accepting an otherwise-validly-signed image.