Home Learning Paths ECU Lab Assessments Interview Preparation Pricing Log In Sign Up
Log In Sign Up
Diagnostics

Flash Bootloader Development

Develop production-grade flash bootloaders for automotive ECUs. Learn UDS-based reprogramming sequences, security access, memory management, and dual-bank strategies.

26 chapters
20.0 hrs reading
5 modules

Overview

The flash bootloader is one of the most critical and security-sensitive pieces of software in any ECU. It manages the complete software update process and must work flawlessly every time - a failed update can brick an ECU.

This course teaches you to develop bootloaders from scratch: UDS reprogramming sequences, flash driver integration, security access mechanisms, CRC verification, dual-bank strategies, and rollback protection.

You'll implement a complete bootloader that handles the full update lifecycle including pre-programming checks, memory erasure, data transfer with flow control, integrity verification, and safe activation.

Course Modules

1
Bootloader Architecture
5 chapters • 4.0 hrs reading
Bootloader Role & Memory LayoutFREE PREVIEW 40 min read
▸ FBL role in ECU lifecycle: ROM reset vector at 0x00000000 (ARM Cortex-M) jumps to bootloader; FBL checks application validity (CRC in AppHeader); enters reprogramming mode if: (a) NvM BootFlag=0xA5A5A5A5, (b) dedicated GPIO pin asserted, (c) no valid app; AUTOSAR FBL architecture: Sec Module (security) + FBL core + downloadable Flash Driver + Memory Abstraction; HIS (Hersteller Initiative Software) memory driver standard used by BMW/VW/Daimler/Bosch; programming counter in NvM checked against max allowed (0xFF)
▸ Linker script memory regions (S32K144 example): BOOTLOADER (rx): ORIGIN=0x00000000, LENGTH=32K; APPLICATION (rx): ORIGIN=0x00008000, LENGTH=224K; BOOT_PARAMS NvM: 0x00003E00–0x00003FFF (BootFlag, programming counter, CRC); RAM: 0x1FFF8000 LENGTH=32K; FLASH_DRIVER download area: RAM at 0x1FFFC000 (8 KB for HIS driver binary); AppHeader at 0x00008000: {MagicWord=0xA5A5A5A5, SWVersion, CRC32, SignatureLen, SignatureData}
▸ Boot decision logic: startup: disable watchdog, init PLL, init SRAM; read BootFlag at NvM 0x00003E00; if BootFlag==0xA5A5A5A5 → programming mode; else: check AppHeader.CRC32 over [0x00008010–0x0003FFFF]; if valid: jump to app via: typedef void (*pFunc)(void); pFunc App = (pFunc)(*(uint32_t*)(0x00008004)); App(); (ARM: vector table offset 4 = Reset_Handler); AUTOSAR SecM_VerifySignature() for signed apps (ECDSA-P256 ≈ 100 ms, or <5 ms with SHE/HSM)
▸ Memory protection during reprogramming: ARM MPU disables bootloader region from app writes; S32K144 FTFC FPROT registers: FPROT3[7:0]=0x01 → protect 2 KB at 0x00000000; programming counter in NvM (block 0x0001): incremented each attempt, checked against MAX (e.g., 0xFF); AUTOSAR FBL: Fbl_GetProgAttempts() / Fbl_SetProgAttempts(); HIS FBL checks FblCheckBootVect() + FblCheckBackupVector(); production: JTAG locked after manufacturing via FSEC register (MEEN=0b10=disable)
Boot Sequence & Application JumpFREE PREVIEW 45 min read
▸ ARM Cortex-M boot sequence: CPU reads MSP from 0x00000000, PC from 0x00000004 on reset; Reset_Handler() initializes: clock (SCG_RCCR for S32K144 80 MHz PLL), SRAM (BSS zero, .data copy), watchdog (WDOG_CNT=0xD928C520 unlock, WDOG_TOVAL=1000 for 1 s); MPU_CESR=0 disable MPU; S32K144 SCG DIVCORE=1; then FBL main(); boot time to main() ≈ 5 ms; whole FBL startup including CRC check ≈ 50 ms (without HSM)
▸ Application validity check: AppHeader at 0x00008000: {MagicWord=0xA5A5A5A5, SWVersion, CRC32 over [0x00008010–0x0003FFFF], SignatureLen, SignatureData}; AUTOSAR SecM_VerifyChecksum(FBL_CHECKSUM_PARAMETER, &result) computes CRC-32; flashMagicPattern list defines checked ranges; ECDSA-P256 verify ≈ 100 ms on Cortex-M4 at 80 MHz; with SHE HSM: 1–5 ms; if MagicWord != 0xA5A5A5A5 → stay in FBL; if CRC mismatch → stay in FBL and report DEM event
▸ Application jump sequence: step 1: __disable_irq(); step 2: deinit FBL peripherals (CAN, LPSPI); step 3: set VTOR: SCB->VTOR = 0x00008000; step 4: load new MSP: __set_MSP(*(uint32_t*)0x00008000); step 5: jump: (*(void(*)(void))(*(uint32_t*)(0x00008004)))(); application startup: NVIC_SetPriorityGrouping(), peripheral init, RTOS start; critical: forgetting VTOR → HardFault when app enables interrupts; common error: not deiniting FBL CAN → CAN interrupt fires with wrong handler address
▸ Shared memory and reset flags: BootFlag in no-init SRAM (SHARED_RAM at 0x1FFFF000, __attribute__((section(".noinit"))): preserved across soft reset but not power-off; or NvM block (Fee_Write(FEE_BLOCK_BOOT_FLAGS, &flag)); application sets BootFlag before calling EcuM_GoDown() → reset → FBL detects flag → stays in FBL; HIS FBL: FBL_ENABLE_STAY_IN_BOOT flag in NvM; FblCheckBootVect() validates shared data integrity; FBL_START_WITH_APPL_ALLOWED checks if conditions permit app start
Reprogramming Session Flow 50 min read
▸ Full UDS reprogramming sequence (11 steps): 1. DSC extendedSession (0x10 0x03); 2. CommunicationControl disableRxAndTx (0x28 0x03 0x01); 3. SecurityAccess 0x27 0x01 (requestSeed) + 0x27 0x02 (sendKey); 4. WriteDataByIdentifier fingerprint 0xF101 (timestamp, tester ID, SW version); 5. DSC programmingSession (0x10 0x02 → resets to FBL); 6. RoutineControl erase (0x31 0x01 0xFF00 + address + length); 7. RequestDownload (0x34 + address + size); 8. TransferData (0x36) loop; 9. RequestTransferExit (0x37); 10. RoutineControl check dependencies (0x31 0x01 0xFF01); 11. ECUReset (0x11 0x01)
▸ Programming session timing: P2_boot=25 ms; P2*_boot=5000 ms; S3server_boot=5000 ms (shorter than application); app→FBL: app receives 0x10 0x02, ComM_DCM_ActiveDiagnostic(), set NvM BootFlag, NvM_WriteAll(), EcuM_GoDown() → Mcu_PerformReset; FBL starts, detects flag, stays in FBL; FBL responds to 0x10 0x02 before reset (or suppresses response - OEM-specific); S3server: TesterPresent 0x3E 0x80 required every 4 s; ControlDTCSetting 0x85 0x02 disables DTC storage during flashing
▸ ECU programming fingerprint (Programmierfingerabdruck): written before programming via DID 0xF101 (SW part number), 0xF180 (boot SW ID), 0xF18A (System Supplier ECU SW Version); includes: programming date (UTC), tester serial number, ECU hardware number; stored in NvM block (size=32 B); AUTOSAR DCM DcmDspDidControl: Dcm_ProcessWriteFingerprint(); BMW uses 0xF101 + 0xF15B; VW/Audi uses 0xF18C + 0xF19E; verified by application post-restart; OEM may reject ECU without valid fingerprint at EOL test
▸ CAN TP configuration for maximum throughput: ISO 15765-2 flow control: FC.BS=0 (unlimited blocks), FC.STmin=0 ms; AUTOSAR CanTp: CanTpNBr=20 ms, CanTpNAr=20 ms, CanTpNAs=25 ms; padding: 0x55 in unused CAN bytes; 500 kbit/s CAN, TP overhead ≈ 2 frames/PDU → net ~50 KB/s; 224 KB application ≈ 45 s + erase time; DoIP: net ~10 MB/s → 224 KB in <1 s; AUTOSAR FBL: BS=0 (no FC ACK) + STmin=0 for max speed; CanTp physical addressing only in FBL (no functional addressing for multi-frame)
Flash Driver Architecture 45 min read
▸ HIS memory driver standard: Hersteller Initiative Software flash driver interface, used by BMW/Daimler/VW/Bosch; driver is a downloadable .o binary loaded to RAM during programming (not stored in FBL flash - avoids FBL change for MCU variants); HIS interface: MemDriver_InitSync(void*), MemDriver_REraseSync(tMemDrvParameter*), MemDriver_RProgramSync(tMemDrvParameter*), MemDriver_DeinitSync(void*); tMemDrvParameter: {address, length, *data}; download via RequestDownload to RAM: address=0x1FFFC000, length=driver binary size
▸ RAM execution requirements: driver must be position-independent code (PIC) or loaded at fixed RAM address; linker: __attribute__((section(".ramfunc"))); no flash access during erase (bus fault if CPU reads flash while FTFC erasing); cache flush before RAM execution: SCB_CleanDCache(); SCB_InvalidateICache(); AUTOSAR FBL: FlashDriver_Init() → FlashDriver_Erase() → FlashDriver_Write() → FlashDriver_DeInit(); driver authentication: CRC-32 over RAM buffer validated before execution to prevent corrupted driver
▸ FTFC flash controller (S32K144): FTFC_FSTAT register: CCIF bit7 (command complete), ACCERR bit5 (access error), FPVIOL bit4 (protection violation), MGSTAT0 bit0 (margin read error); FTFC_FCNFG: EEERDY, RAMRDY; erase sector (0x09): FCCOB0=0x09, FCCOB1:3=sector address; launch: FTFC_FSTAT=0x80; poll CCIF; check ACCERR+MGSTAT0; sector size: 4 KB P-Flash; write phrase (0x07): 8 bytes aligned; S32K144 write speed: 8 B per 100–300 μs → 50 KB/s max write rate; erase time: 200–400 ms/sector (4 KB)
▸ Downloadable vs fixed flash driver: downloadable (HIS): flexible for MCU variants, no FBL update needed; fixed: simpler, baked into FBL binary; Vector vFlash tool: uploads HIS driver + S19/Intel HEX data via CDD; Lauterbach TRACE32: BDM/JTAG flash programming bypasses FBL; S19 format: :LLAAAATT[DD...]CC (Motorola S-Record, types 0=header, 1/2/3=data, 7/8/9=end); AUTOSAR FBL FlashWrite: FBL_MEMDRV_SEGMENT_SIZE for write granularity; RAM driver download sequence: 0x34→0x36(×N)→0x37 to RAM address, then validate CRC, then use for application programming
Hands-On: Basic Bootloader Skeleton 55 min read
▸ Project setup: NXP S32K144 EVB + S32DS IDE; linker script: MEMORY { BOOTLOADER(rx) ORIGIN=0x00000000 LENGTH=32K; APPLICATION(rx) ORIGIN=0x00008000 LENGTH=224K; SHARED_RAM(rwx) ORIGIN=0x1FFFF000 LENGTH=256 }; startup.S: load .data from flash to SRAM, zero .bss; main(): system_init(), wdog_disable(), clock_init_80MHz(), fbl_main(); BootFlag at 0x1FFFF000: if(*(uint32_t*)0x1FFFF000 == 0xA5A5A5A5U) stay_in_fbl(); else check_app_and_jump()
▸ CAN TP init for FBL: LPSPI/LPUART not used; CAN only for UDS; can_init(CAN0, 500000); iso_tp_init(rx_id=0x7E0, tx_id=0x7E8); main receive loop: while(!prog_complete) { if(CanRx_Available()) process_uds_frame(); wdog_refresh(); }; TesterPresent handling: 0x3E 0x80 → no response; 0x3E 0x00 → respond 0x7E 0x00; S3server timer: reset on each received message; if timer expires → Mcu_PerformReset(); CAN padding: unused bytes filled with 0x55 (per OEM requirement)
▸ Minimal UDS dispatcher: uint8_t uds_handle(uint8_t* req, uint16_t len) { switch(req[0]) { case 0x10: return handle_DSC(req); case 0x11: return handle_ECUReset(req); case 0x27: return handle_SecurityAccess(req); case 0x31: return handle_RoutineControl(req); case 0x34: return handle_RequestDownload(req); case 0x36: return handle_TransferData(req); case 0x37: return handle_RequestTransferExit(req); case 0x3E: return handle_TesterPresent(req); default: send_NRC(req[0], 0x11); return; } }; positive response: req[0]+0x40; negative: 0x7F+SID+NRC; CRC-32 via S32K144 CRC0 module (CRC0_CTRL=0x01000000, seed=0xFFFFFFFF)
▸ Testing: TRACE32: FLASH.Erase ALL; FLASH.Program boot_skeleton.elf; break at check_app_and_jump() → inspect SHARED_RAM BootFlag; CANoe CAPL: send 0x10 0x02 → expect 0x50 0x02; send 0x11 0x01 → ECU resets; verify CAN goes quiet after 0x28 0x03; measure P2 latency with oscilloscope (CAN TX pin): expected <25 ms from request to response; common issues: (1) VTOR not set → HardFault after app jump; (2) stack corrupted after jump → update MSP before PC jump; (3) wdog timeout during erase → refresh in erase loop; (4) TX ID swapped with RX ID → no response visible
2
UDS Reprogramming Services
6 chapters • 4.8 hrs reading
DiagnosticSessionControl & ECUReset 40 min read
▸ DiagnosticSessionControl (0x10): sub-functions: 0x01=defaultSession, 0x02=programmingSession (FBL only), 0x03=extendedDiagnosticSession (app only); positive response: 0x50 + sub-function + P2ServerMax_msHigh + P2ServerMax_msLow + P2StarServerMax_msHigh + P2StarServerMax_msLow; P2_default=25 ms, P2*_default=5000 ms; P2_boot=25 ms; AUTOSAR DCM DcmDspSessionRow per session; DcmDspSession.DcmDspSessionP2ServerMax=25ms; DcmDspSession.DcmDspSessionS3ServerTimeoutPeriod=5000ms
▸ App→FBL transition via 0x10 0x02: app receives 0x10 0x02; DCM calls Dcm_SessionChangeIndication(oldSession=default, newSession=programming); app: ComM_DCM_ActiveDiagnostic(); Fee_Write(FEE_BLOCK_BOOT_FLAGS, &bootFlag); NvM_WriteAll(); EcuM_GoDown() → OS shutdown → Mcu_PerformReset(); FBL detects BootFlag → stays in FBL; response options: send 0x50 0x02 before reset (delay P2ServerMax then reset), or suppress response (OEM-specific); AUTOSAR DcmDspSessionForBoot: DcmBootloaderReset = TRUE
▸ ECUReset (0x11): sub-functions: 0x01=hardReset (Mcu_PerformReset or WDOG force), 0x02=keyOffOnReset (simulated power cycle), 0x03=softReset (SCB->AIRCR=0x05FA0004 SYSRESETREQ); after successful programming: FBL sends 0x51 0x01, disables interrupts, triggers reset; S32K144 hard reset: WDOG_CNT=0xD928 then 0xC520 to unlock, then WDOG_TOVAL=1 → immediate reset; BootFlag cleared (0x00000000) before reset → FBL checks app validity → jumps to new app; verify: new app CAN frame appears within 1 s
▸ Response Pending (NRC 0x78): long operations (erase, CRC) exceed P2ServerMax (25 ms); DCM sends 0x7F + SID + 0x78 before timeout; repeat every 25 ms up to P2* (5000 ms); S32K144: sector erase ≈ 400 ms × 56 sectors = 22.4 s → multiple 0x78 responses; AUTOSAR DCM: Dcm_ProcessRoutineControlStartByIdentifier() returns DCM_E_PENDING; Dcm_MainFunction() auto-sends 0x78; interval = DcmTimStrP2ServerMax (25 ms); Vector vFlash handles 0x78 auto-retry; Lauterbach: FLASH.NOP command during 0x78 wait
SecurityAccess - Seed & Key Exchange 50 min read
▸ SecurityAccess (0x27) protocol: odd sub-function = requestSeed, even = sendKey; level 0x01/0x02 = standard programming access; requestSeed (0x27 0x01): ECU responds 0x67 0x01 + 4–8 byte seed (TRNG or hardware RNG); if already unlocked: seed = 0x00000000; sendKey (0x27 0x02): tester sends 0x27 0x02 + computed key; NRC 0x35=invalidKey; NRC 0x36=exceededNumberOfAttempts (after 3 failures); NRC 0x37=requiredTimeDelayNotExpired (10 min lockout); seed validity timeout: 10 s (DcmDspSecuritySeedTimer)
▸ Key derivation algorithm: OEM-specific; common: AES-128-ECB: key = AES128_Encrypt(seed XOR constant_C1, secret_key_K); K stored in SHE KEY_1 or HSE key slot; alternative: HMAC-SHA256(seed, K); or Seed-Key DLL (SeedKeyDLL.dll / libseedkey.so) supplied by OEM; AUTOSAR DCM: DcmDspSecurityCompareKeyFnc = Csm_MacVerify(); DcmDspSecurityGetSeedFnc = Csm_RandomGenerate(); S32K14x SHE: Cry_SheRngGenerate(seed, 16) → 128-bit random seed; Cry_SheEncryptEcb(seed, KEY_1, output) → key computation on tester
▸ HSM seed generation: S32K14x CSEc (Cryptographic Services Engine) commands via FTFC CCOB: CMD_RND → 128-bit random number (seed); CMD_ENCRYPT_ECB → AES-128 encryption (key computation); CMD_VERIFY_MAC → CMAC verify; K provisioned during manufacturing into SHE KEY_1 via CMD_LOAD_KEY (requires M1M2M3 protocol: UID + encrypted key); AUTOSAR CryDrv_SHE: Cry_SheRngGenerate(buffer, 16); abstracted via Csm_RandomGenerate(jobId, resultBuffer, &resultLength); seed security: each seed unique (never repeated), expires in 10 s
▸ Lockout counter and delay management: DcmDspSecurityNumAttDelay = 3 (lockout after 3 wrong keys); DcmDspSecurityDelayTime = 600 s (10 min); lockout state: NvM block (FailedAttemptCounter + LockoutStartTime), size=8 B; on each invalid key: increment counter, Fee_Write(); if counter ≥ 3: set lockout, store timestamp; NRC 0x37 if locked; reset counter on success; NvM block: FEE immediate write (FEE_WRITE_CYCLE_COUNT); AUTOSAR DCM: DcmDspSecurityAtemptCounterFnc reads/writes NvM; programming tool: Vector vFlash SecurityAccess plugin loads SeedKeyDLL and retries automatically on NRC 0x35
RequestDownload & TransferData 55 min read
▸ RequestDownload (0x34) format: dataFormatIdentifier (1B: high nibble=compression, low nibble=encryption; 0x00=raw, 0x11=compressed+encrypted) + addressAndLengthFormatIdentifier (1B: high nibble=sizeBytes, low nibble=addrBytes) + memoryAddress + memorySize; example: 0x34 0x00 0x44 0x00008000 0x00038000 (address=0x8000, size=0x38000=224 KB); positive response: 0x74 + 0x20 + maxBlockLength (2B, e.g., 0x0402=1026 bytes max per TransferData); FBL validates address range against allowed memory table before proceeding
▸ TransferData (0x36) protocol: request: 0x36 + blockSequenceCounter (1B, starts at 0x01, wraps 0xFF→0x00) + data bytes; block size ≤ maxBlockLength; positive response: 0x76 + blockSequenceCounter; wrong counter → NRC 0x73 (wrongBlockSequenceCounter); double-buffer: FBL receives block N+1 into buffer B while writing block N from buffer A to flash (ping-pong); last block may be shorter than maxBlockLength; pad last phrase to 8-byte alignment with 0xFF; FBL accumulates CRC-32 over all received bytes for later verification
▸ Flash write implementation: on 0x36 receipt: validate blockSequenceCounter; memcpy data to write buffer; FlashDriver_RProgramSync(¶m) where param.addr=currentWriteAddr, param.len=blockLen, param.data=buf; advance currentWriteAddr += blockLen; FTFC write: FCCOB0=0x07, FCCOB1:3=address, FCCOB4:B=8-byte data; launch FTFC_FSTAT=0x80; poll CCIF; check ACCERR+MGSTAT0; error → NRC 0x72 (generalProgrammingFailure); validate: received bytes = memorySize from 0x34; address range stays within RequestDownload window
▸ RequestTransferExit (0x37): signals end of transfer; FBL validates: (1) total bytes received == memorySize from 0x34, (2) accumulated CRC-32 == expected (from AppHeader or routine parameter); positive response: 0x77; mismatch → NRC 0x72; after 0x37: memory region marked "written, pending verification"; multiple segments: repeat 0x34→0x36(×N)→0x37 for each S19 data segment (e.g., code at 0x8000 + calibration at 0x3F000); FBL must track which segments received for dependency check; AUTOSAR FBL: FblMemTransferExit() finalizes write pointer
RoutineControl - Erase, Verify, Checksum 45 min read
▸ RoutineControl (0x31) framework: sub-functions: 0x01=startRoutine, 0x02=stopRoutine, 0x03=requestRoutineResults; routine IDs: 0xFF00=EraseMemory, 0xFF01=CheckProgrammingDependencies, 0x0202=CheckMemory (CRC), 0x0203=CheckProgrammingPreConditions, 0x0F00=FlashToolInterface; request: 0x31 + sub-function + routineId (2B) + optional params; erase example: 0x31 0x01 0xFF00 0x00008000 0x00038000; response: 0x71 0x01 0xFF00 + routineInfo (status byte); response pending (0x78) during long erase
▸ EraseMemory (0xFF00): validates address/length against erasable regions (bootloader protected); computes sector count = length / 4096; FlashDriver_REraseSync() per sector; erase verification: optional blank check (read sector, verify all 0xFF); FTFC: FTFC_FSTAT.ACCERR and MGSTAT0 checked after each sector; AUTOSAR FBL: FblMemErase(address, length) → registered flash driver; kFblMemStatus_Ok on success; protection check: FblLogicalBlockTable validates address; duration: 56 sectors × 400 ms = 22.4 s → multiple 0x78 NRC responses
▸ CheckProgrammingDependencies (0xFF01): verifies: (1) SW version compatibility (from AppHeader), (2) hardware variant matches SW, (3) all required segments received (segment counter == expected); BMW SecM: Sec_VerifyChecksum() validates all logical blocks; VW: check ApplicationDataValid NvM flag; routine result: 0x31 0x03 0xFF01 + status (0x00=OK, 0x01=error); if error: ECU stays in programming session, app not startable; must reflash or fix dependency; AUTOSAR FBL FBL_ReadCompatibilityList() verifies interface version constraints
▸ CheckMemory / CRC verification (0x0202): recomputes CRC-32 over all programmed memory ranges; compares to expected CRC in AppHeader or routine input parameter; match: sets AppHeader.MagicWord = 0xA5A5A5A5; mismatch: NRC 0x72; S32K144 CRC0 hardware: CRC0_CTRL=0x01000000 (CRC-32), seed=0xFFFFFFFF, polynomial=0xEDB88320; 1 MB CRC at 80 MHz ≈ 12 ms; routine result: 0x71 0x03 0x0202 + result (0x00=pass, 0x01=fail); programming sequence proceeds to ECUReset only after successful 0x0202; AUTOSAR SecM_VerifyChecksum(FBL_CHECKSUM_PARAMETER, &result)
Error Handling & NRC Management 35 min read
▸ NRC taxonomy for reprogramming: 0x11=serviceNotSupported (wrong session); 0x12=subFunctionNotSupported; 0x13=incorrectMessageLength; 0x22=conditionsNotCorrect (session mismatch); 0x24=requestSequenceError (0x36 before 0x34, or 0x37 before all blocks); 0x31=requestOutOfRange (address outside allowed region); 0x33=securityAccessDenied; 0x35=invalidKey; 0x36=exceededNumberOfAttempts; 0x37=requiredTimeDelayNotExpired; 0x72=generalProgrammingFailure (flash write error); 0x73=wrongBlockSequenceCounter; 0x78=requestCorrectlyReceivedResponsePending; 0x92=voltageTooHigh; 0x93=voltageTooLow
▸ State machine enforcement: FBL UDS states: IDLE→SESSION_EXTENDED→SECURITY_UNLOCKED→PROGRAMMING_SESSION→ERASED→DOWNLOADING→DOWNLOADED→VERIFIED; each service validates current state before executing; wrong-order errors: 0x36 before 0x34 → NRC 0x24; 0x37 before all bytes received → NRC 0x24; 0x31 0xFF01 before 0x37 → NRC 0x24; AUTOSAR DCM: session-dependent services enforced via DcmDslSessionControl; DcmDspSecurityRow enforces security level per service; NRC 0x33 if 0x34 attempted without SecurityAccess
▸ Flash programming error recovery: FTFC ACCERR (access error): clear FSTAT=0x20; retry once; 3 failures → NRC 0x72; FPVIOL (protection violation): check FPROT registers - protected sector attempted; MGSTAT0 (marginal read): copy data to adjacent sector, mark original bad in NvM bitmap; NRC 0x72 response: host must re-erase affected sector and retry from 0x34; AUTOSAR FBL FblMemRetryOnError() policy; post-write verify: Fbl_MemProgramVerify() reads back each 8-byte phrase; voltage monitoring: ADC reads VDD; if <4.5 V → abort with NRC 0x93; Dem_SetEventStatus(FBL_E_PROGRAM_ERROR) logs programming failure
▸ Watchdog management during programming: AUTOSAR FBL: FblLookForWatchdog() called every 10 ms in main loop; S32K144 WDOG refresh: WDOG_REFRESH = 0xA602 then 0xB480 within 20 bus cycles; window watchdog (WDOG_CS.WINEN): refresh only in last 25% of timeout; recommended FBL watchdog timeout: 2 s; erase loop: call FblLookForWatchdog() between each sector erase; download loop: called between blocks; S3server timeout (5 s without TesterPresent): Mcu_PerformReset() exits FBL; AUTOSAR DCM: Dcm_TpTxConfirmation resets S3server timer on each message
Hands-On: Complete UDS Sequence 65 min read
▸ Lab setup: Vector CANoe + VN1630 + S32K144 EVB with FBL; create CANoe config: CAN 500 kbit/s; add Diagnostic Console + CDD (FBL services); CAPL script: on key 'P' { DiagSendRequest(0x10,0x03); Wait(100); DiagSendRequest(0x28,0x03,0x01); Wait(100); DiagSendRequest(0x27,0x01); ...} or use Vector vFlash (project: target=S32K144, CDD=FBL.cdd, flash file=app.s19); vFlash automates: Connect → Open Session → Security Access → Erase → Program → Verify → Reset
▸ SecurityAccess test: capture seed from 0x67 0x01 response; compute key via SeedKeyDLL.dll: LoadDll("SeedKeyDLL.dll"); SeedKeyCalculation(seed_4B, &key_4B); send 0x27 0x02 + key; expect 0x67 0x02; test wrong key: 0x27 0x02 + 0x00000000 → 0x7F 0x27 0x35 (NRC invalidKey); after 3 failures: 0x7F 0x27 0x36 (exceededAttempts); wait lockout period (reduce DcmDspSecurityDelayTime to 10 s for test); CAPL: SeedKey() function loads DLL via LoadDll() COM call; measure seed-key RTT: target <100 ms
▸ Programming sequence execution: vFlash step log: (1) DSC extended → 0x50 0x03; (2) SecurityAccess level 1 → 0x67 0x02; (3) Erase 0xFF00 → 0x78×(22s/25ms≈880 times) → 0x71 0x01 0xFF00 0x00; (4) Program 224 KB → 224 TransferData blocks → 0x71 0x01 0x0202 0x00 CRC pass; (5) CheckDependencies → 0x71 0x01 0xFF01 0x00; (6) ECUReset → 0x51 0x01; new app starts; measure total time: expected 30–45 s; after reset: send 0x10 0x01 → app responds 0x50 0x01 (proof new app running)
▸ Error injection and robustness: power loss during erase step 3 → reconnect → FBL active (BootFlag still set), app invalid (MagicWord=0xFFFF) → reflash succeeds; CRC corruption: modify 1 byte in S19 → 0x0202 returns NRC 0x72; wrong block sequence: send 0x36 0x03 before 0x36 0x02 → NRC 0x73; session timeout: wait 6 s without TesterPresent → ECU resets; TRACE32 post-flash verify: d.load.s19 app.s19 /VERIFY → compare flash vs S19 byte-by-byte; voltage drop test: reduce supply to 10 V during programming → FBL aborts with NRC 0x93 (voltageTooLow)
3
Flash Memory Management
5 chapters • 3.8 hrs reading
Flash Memory Technology & Constraints 45 min read
▸ Flash memory types in automotive: NOR flash (XIP - execute-in-place, word-addressable reads, sector-level erase): S32K144 has 256 KB P-Flash; program time: 100–500 μs/word; erase: 200–400 ms/sector (4 KB); endurance: 10,000–100,000 P/E cycles; retention: 20 years at 85°C; EEPROM emulation via FlexNVM (D-Flash): 64 KB + 4 KB FlexRAM; random write granularity: 4 bytes; NAND flash: block erase 128 KB, page write 4 KB, requires wear leveling + ECC; not used for code execution (NOR preferred for automotive ECU boot code)
▸ Flash cell physics and degradation: floating gate NOR: control gate + floating gate store charge; write: Fowler-Nordheim tunneling; erase: FN tunneling reverse; charge trap layer (CTF) more reliable; degradation: oxide wear from P/E cycles → threshold voltage shift → bit errors; hard error: uncorrectable even with ECC; S32K144 FTFC ECC: 1-bit correction (SEC) + 2-bit detection (DED) per 32-bit phrase; ECC uses extra 4 bits/phrase; NvM (FlexNVM): 500,000 P/E cycle endurance; temperature acceleration: Arrhenius model - each 10°C increase halves retention lifetime; automotive: must meet 125°C + 20-year retention
▸ Erase constraint management: flash must be erased (all bits = 1) before programming (can only write 1→0, not 0→1); erase sets all bits to 0xFF; full sector erase required even for partial update; sector alignment: address must be sector-aligned (addr & ~0xFFF for 4 KB); blank check: verify all 0xFF before programming; FTFC blank check: ProgramCheck command verifies phrase is erased; if previously programmed byte written again without erase: overprogramming → cell damage; FTFC FPVIOL flag if writing to protected sector; AUTOSAR FBL: FblLogicalBlockTable.Attr defines PROTECTED sectors
▸ ECC error detection and diagnostics: S32K144 FTFC: FTFC_FERSTAT.DFDIF (double-bit fault) / SFDIF (single-bit fault); bus fault on double-bit error during read; single-bit auto-corrected; NvM ECC events: AUTOSAR Dem FblECCErrorHandling(); flash health monitoring: read FERSTAT each ignition cycle; program/erase counter per sector in NvM; at 80% endurance limit: DEM event NvM_E_LOSS_OF_REDUNDANCY; MBIST: some MCUs support flash built-in self-test; post-programming read-back: verify each 8-byte phrase written correctly via FTFC VerifyBackdoor; AUTOSAR FBL: Fbl_MemProgramVerify() post-write
Sector Erasure & Write Alignment 40 min read
▸ Sector erase boundary calculation: start_sector = (address / SECTOR_SIZE) × SECTOR_SIZE; end_sector = ((address + length - 1) / SECTOR_SIZE) × SECTOR_SIZE; iterate sector-by-sector; example: erase 0x8000–0x3FFFF (224 KB): sectors 0x8000, 0xA000, ...0x3E000; count = (0x38000 / 0x1000) = 56 sectors × 400 ms = 22.4 s; AUTOSAR FBL FblMemErase() validates start/end against FblLogicalBlockTable before erasing; erase verification: read sector post-erase, all bytes == 0xFF; if not blank: hardware fault or protection issue
▸ Write alignment and granularity: FTFC phrase size = 8 bytes; all writes must be 8-byte aligned; unaligned address → ACCERR flag; alignment algorithm: if (startAddr & 7) { padLen = startAddr & 7; create aligned phrase: [0xFF×padLen] + data; write at (startAddr & ~7) }; FTFC write: FCCOB0=0x07, FCCOB1:3=address, FCCOB4:B=8 bytes; launch FSTAT=0x80; poll CCIF; check ACCERR+MGSTAT0; S32K144 write speed: 8 B per 100–300 μs → max 50 KB/s write rate; 224 KB write time = 224000/50000 ≈ 4.5 s; total (erase + write) ≈ 27 s
▸ Sector protection and write locking: FTFC FPROT0–FPROT3 registers: each bit protects 2 KB of P-Flash; FPROT3=0x01 → protect 2 KB at 0x00000000 (bootloader); protection bits written once (sticky until mass erase or JTAG backdoor); per-sector protection: FDPROT for D-Flash (FlexNVM); write to protected sector → FTFC_FSTAT.FPVIOL; JTAG backdoor: write 0xFFFFFFFF to 0x0000040C (FSEC unsecure bit); FOPT register: bit 7=NMI_DIS, bit 2=FAST_INIT, bit 0=LPBOOT; production: FSEC=0xFE (secure=0b10, backdoor disabled=0b01 in MEEN bits)
▸ Failed erase/write recovery: ACCERR during erase: clear FSTAT=0x20; retry; 3 failures → mark sector bad in NvM BadSectorBitmap (1 bit/sector); skip bad sector; adjust write pointer; MGSTAT0 after write: marginal read failure; FBL: copy data to adjacent sector, mark original bad; AUTOSAR FBL FblMemRetryOnError() policy; bad sector NvM block (end of flash: BadSectorBitmap, 1 bit/sector = 56 bits for 224 KB); TRACE32 verify: FLASH.ERASEVERIFY; d.compare A:0x8000--0x9FFF 0xFF (blank check); oscilloscope on VDD during erase: voltage dip ≤50 mV (power delivery requirement per OEM spec)
Dual-Bank & A/B Update Strategies 50 min read
▸ Dual-bank architecture: Bank A (active) + Bank B (inactive); flash new SW into Bank B while Bank A runs; on confirmation: swap banks atomically; hardware support: NXP S32G: PFLASH0 (4 MB Bank 0) + PFLASH1 (4 MB Bank 1); bank swap via PFLASH_PFCBLK_SPELOCK and PFCBLK_PFAPR registers; Renesas RH850: code flash block 0/1 with swap flag in option setting memory; S32K144 is single-bank (256 KB, one bank); S32K3 series: up to 4 MB with hardware block swap; bank swap atomic at reset (no partial state)
▸ A/B indicator NvM block: {BootBank=0/1, AppAValid=0/1, AppBValid=0/1, AppAVersion, AppBVersion} at fixed NvM address; FBL reads BootBank at startup; if B valid and preferred: set PFLASH bank select → B mapped to 0x00000000; after B programmed and verified: set AppBValid=1; bank swap happens at next reset; confirm/rollback timer: if new SW doesn't call confirmation API within 30 s → FBL reverts to Bank A on next reset; both banks maintain valid state at all times (never erase A until B confirmed)
▸ Confirmation and rollback: OEM confirmation: RoutineControl 0x31 0x01 0xDF00 from application, or NvM flag write; AUTOSAR AP UCM: ara::ucm::PackageManager::Finish() triggers confirmation; if confirmation received: mark Bank B permanent; if timeout: FBL reverts → BootBank=0 → Bank A starts → Bank B marked for erasure; UCM ProcessingState: IDLE→TRANSFERRING→TRANSFERRED→VERIFYING→READY→ACTIVATING→ACTIVATED or ROLLEDBACK; critical: confirmation write must be atomic (single Fee_Write phrase)
▸ Single-bank vs dual-bank trade-offs: single-bank: ECU offline during programming (workshop update only); simpler FBL; no dual-bank hardware needed; no fallback (failed update = bricked ECU temporarily); traditional CAN FBL; dual-bank: ECU functional during OTA download to inactive bank; required for AUTOSAR AP OTA (ISO 22900, UCM); automatic rollback on failure; requires 2× flash size; linker scripts: separate MEMORY regions BANK_A and BANK_B; additional FBL complexity for bank management; S32K3 dual-bank linker: FLASH_A: ORIGIN=0x00400000 LENGTH=2M; FLASH_B: ORIGIN=0x00600000 LENGTH=2M
Wear Leveling & Endurance 35 min read
▸ Code flash endurance: automotive P-Flash (NOR): 10,000 P/E cycles at 125°C (AEC-Q100 Grade 0); OTA update frequency: 2–4 updates/year × 20-year life = 40–80 cycles → well within 10,000 limit; production EOL = cycle 1; code flash wear leveling usually not needed; EEPROM emulation (FlexNVM D-Flash): higher write frequency (NvM DTC data, parameters) → wear leveling required; FlexNVM endurance: 500,000 P/E cycles; S32K144 FlexNVM: 64 KB D-Flash + 4 KB FlexRAM; phrase = 4 bytes (smaller than P-Flash 8 bytes)
▸ AUTOSAR FEE wear leveling: FEE (Flash EEPROM Emulation) manages D-Flash sectors; logical blocks mapped to physical sectors via write pointer; 2 sectors minimum (active + reserve); on active sector full: copy valid data to reserve, mark old sector invalid; FEE block format: {blockStatus byte, blockData, CRC}; FEE_BlankCheck() before write; logical address virtual (physical varies); FEE write cycle tracked per sector; AUTOSAR NvM: NvM_WriteBlock() → Fee_Write() → Fls_Write() (FlexNVM driver); transparent to application layer; Fee_GetStatus() returns sector usage percentage
▸ Flash endurance monitoring: NvM ProgramCounterBlock: uint32_t program_count incremented per full programming; compare to ECU_MAX_PROGRAM_EVENTS (e.g., 50); if ≥ max: DEM event NvM_E_LOSS_OF_REDUNDANCY; read via UDS ReadDataByIdentifier 0xF0F8 = ProgramAttempts; DID 0xF186 = programmingStatus; field monitoring: OBD-II PID or custom RDBI; FEE wear: Fee_GetStatus() sector_usage_pct; trigger maintenance if >80%; program/erase cycle test in manufacturing MBIST; AUTOSAR FBL: Fbl_GetProgAttempts() returns counter; Fbl_SetProgAttempts() increments atomically in NvM
▸ Retention testing and validation: AEC-Q100 Grade 0: data retention 20 years at 85°C ambient; retention after P/E cycling degrades faster; Arrhenius model: t_retention = A × exp(Ea/kT), Ea ≈ 1.1 eV; accelerated test: 150°C bake for 168 h ≈ 20 years at 85°C; after N P/E cycles (100, 1000, 10000): bake at 150°C for 168 h, read back all bytes, check ECC single/double-bit errors; acceptance: zero hard errors; AUTOSAR FBL post-programming soak: read AppHeader after 24 h (FOTA field validation); TRACE32: d.compare A:0x8000--0x3FFFF <expected binary> after bake test
Hands-On: Flash Driver Implementation 60 min read
▸ HIS flash driver template: FlashDrv.c for S32K144 FTFC; MemDriver_InitSync(void* dummy) { FTFC_FSTAT = 0x30; /* clear ACCERR+FPVIOL */ }; MemDriver_REraseSync(tMemDrvParameter* param) { uint32_t sector = param->address & ~0xFFF; FTFC_FCCOB0=0x09; FTFC_FCCOB1=(sector>>16)&0xFF; FTFC_FCCOB2=(sector>>8)&0xFF; FTFC_FCCOB3=sector&0xFF; FTFC_FSTAT=0x80; while(!(FTFC_FSTAT&0x80)); if(FTFC_FSTAT&0x30) return kMemDrv_Failed; return kMemDrv_Ok; }; compile: -mthumb -march=armv7e-m -O2 -fPIC (position-independent for RAM)
▸ Write phrase implementation: MemDriver_RProgramSync: while(len>=8) { FTFC_FCCOB0=0x07; FTFC_FCCOB1=(addr>>16)&0xFF; FTFC_FCCOB2=(addr>>8)&0xFF; FTFC_FCCOB3=addr&0xFF; copy 8 bytes to FCCOB4..FCCOBB registers; FTFC_FSTAT=0x80; while(!(FTFC_FSTAT&0x80)); if(FTFC_FSTAT&0x30) return kMemDrv_Failed; addr+=8; src+=8; len-=8; }; phrase must be 8-byte aligned; FCCOB4=data[0], FCCOB5=data[1]...FCCOBB=data[7]; read FCCOB as byte-addressed registers; cache flush before first RAM execution: SCB_CleanDCache(); SCB_InvalidateICache()
▸ Download flash driver via UDS: RAM buffer at 0x1FFFC000 (8 KB); host: RequestDownload(0x34, addr=0x1FFFC000, size=driver_binary_size); TransferData blocks; RequestTransferExit; FBL CRC validation: CRC-32 over RAM buffer must match expected (embedded in driver at offset 0); jump to driver: MemDriver_InitSync = (void(*)(void*))(0x1FFFC000 + INIT_OFFSET); call MemDriver_InitSync(NULL); then ready for application download; driver binary size: typically 2–8 KB for S32K144 FTFC driver
▸ Testing and verification: TRACE32 breakpoint: a.break MemDriver_REraseSync; step through FTFC erase command; verify FSTAT.CCIF goes low then high; no ACCERR; TRACE32 blank check: d.compare A:0x8000--0x9FFF 0xFF; program verify: d.load.s19 app.s19 /VERIFY; measure erase time: GPIO toggle before/after FSTAT=0x80 and CCIF poll - oscilloscope; write speed: 224 KB / (56 sec × 400ms + write_time) ≈ 5 KB/s effective (erase-dominated); Vector vFlash profiling: timing log shows per-step duration (erase: 22 s, program: 4 s, verify: 1 s)
4
Security & Integrity
5 chapters • 4.0 hrs reading
Secure Boot Chain 50 min read
▸ Chain of trust: Level 0 = hardware root (OTP fuses, SHE/HSM, Secure Boot ROM); Level 1 = Boot ROM verifies FBL signature before CPU executes FBL; Level 2 = FBL verifies application signature before jump; Level 3 = app verifies runtime modules; hardware root: S32K3 HSE (Hardware Security Engine) verifies FBL image via RSA-4096 or ECDSA-P384; if verification fails: halt, no jump to unverified code; ISO 21434 requirement: SW integrity mandatory for safety-critical ECUs; ISO 26262 ASIL-D: secure boot required for powertrain/braking ECUs
▸ Boot ROM and HSE integration: S32K3 HSE firmware stored in dedicated HSE flash (inaccessible from CPU); Boot ROM on power-on: reads HSE_FW, authenticates via OTP root key; HSE verifies FBL: FblHeader at 0x00000000 contains: magicWord + SWVersion + CertificateLength + CertificateData + FW_SHA256_hash + RSA-4096 signature; HSE verifies signature using OTP public key; valid: Boot ROM releases CPU reset → FBL executes; invalid: halt; NXP hse_demo_app: demonstrates HSE_SRV_ID_VERIFY_MAC, HSE_SRV_ID_RSA_VERIFY services; AUTOSAR KeyM: key hierarchy for OEM keys
▸ FBL application signature verification: CheckProgrammingPreConditions (0x31 0x01 0x0203): verify prerequisites (voltage OK, comm mode); after TransferExit and CheckMemory: SecM_VerifySignature(FBL_SEC_PARAM, &sigResult); ECDSA-P256: 64-byte signature (r,s) appended to S19 as last record; verify: SHA-256 of app code → ECDSA-P256 verify with OEM public key stored in FBL secure region; if valid: write AppHeader.MagicWord = 0xA5A5A5A5; AUTOSAR SecM → Csm_SignatureVerify(); key stored in SHE KEY_1 (update-locked at manufacturing)
▸ Security failure handling: failed verification: do NOT execute app; set NvM flag AppSignatureInvalid; enter secure state; send NRC 0x72; Dem_SetEventStatus(FBL_E_APPLICATION_SIGNATURE_FAILURE, DEM_EVENT_STATUS_FAILED); power-cycle only exits secure state; AUTOSAR SHE: Cry_SheSecureBoot() returns E_NOT_OK → FBL stays in bootloader; HSE: if Debug Challenge Response fails → HSE resets MCU (permanent lockout possible); audit log: store failure event (timestamp, VIN, error code) in NvM; hardware monotonic counter (HSE_SRV_ID_OEM_APP_BOOT_CFG_ATTR): prevents software downgrade
Code Signing & Signature Verification 45 min read
▸ Signing toolchain & algorithms: OpenSSL CLI - generate key pair: openssl ecparam -name prime256v1 -genkey -noout -out oem_private.pem; extract public key: openssl ec -in oem_private.pem -pubout -out oem_public.pem; sign firmware: openssl dgst -sha256 -sign oem_private.pem -out app.sig app.bin; verify: openssl dgst -sha256 -verify oem_public.pem -signature app.sig app.bin; algorithms: ECDSA-P256 (64-byte sig r||s), ECDSA-P384 (96-byte), RSA-4096 PKCS#1 v1.5 (512-byte), CMAC-AES-128 (16-byte, symmetric); SHA-256 hash of code region (e.g., 0x00008000–0x0003FFFF) computed before signing
▸ Update package format & signed image structure: S19 (Motorola SREC): last S3 record = signature; binary format: app.bin + 4-byte sig_len + sig_bytes + AppHeader { MagicWord=0xA5A5A5A5, HWVersion, SWVersion[4], SignatureAlgo (0x01=ECDSA-P256, 0x02=RSA-4096), SignatureLen, SignatureOffset, CodeHash[32] }; OTA package (.uds or .fup): ZIP/TGZ containing firmware.bin + firmware.sig + manifest.json (VIN_mask, ECU_address, SW_version, download_checksum); manifest.json signed separately with OEM root cert; manifest.json fields: target_logical_address, required_hw_version, previous_sw_version (for anti-rollback), compression (zlib), block_size
▸ On-ECU verification flow: FBL receives app data via UDS TransferData; after TransferExit: (1) Csm_HashStart(CSM_JOB_SHA256) → Csm_HashUpdate(codeRegion, len) → Csm_HashFinish(digest[32]); (2) Csm_SignatureVerify(CSM_JOB_ECDSA_P256, digest, sig, pubKey) → returns E_OK or E_NOT_OK; AUTOSAR CSM API: CsmConf/CsmJob/CsmKey linked via EcucRef; crypto driver backend: Cry_HwAesCmacVerify() on SHE, or HSE_SRV_ID_ECDSA_VERIFY on S32K3 HSE; timing: S32K3 HSE hardware ECDSA-P256 verify < 5 ms; SHA-256 hardware acceleration: 50 MB/s on S32K3; reject if E_NOT_OK: NRC 0x72, clear AppHeader.MagicWord
▸ Production signing infrastructure & key management: HSM signing server: AWS CloudHSM or Thales Luna HSM; OEM root key in hardware HSM, never exported; CI/CD signing pipeline: build artifacts uploaded to signing service → HMAC request authentication → HSM signs → signed artifact returned; key hierarchy: OEM root → intermediate cert → ECU signing key (per platform); key rotation: new public key provisioned to FBL via KeyM_SetKey(); certificate revocation: CRL stored in FBL NvM block; production: JTAG-locked after manufacturing, OEM public key written to OTP fuses via NXP HSE provisioning tool (hse_host_demo); audit trail: every signing event logged with firmware hash + timestamp + HSM operator ID
Anti-Rollback Protection 40 min read
▸ Anti-rollback concept & attack vectors: rollback attack - attacker re-flashes an older, vulnerable firmware version (e.g., with known CVE); defeated by storing minimum required SW version in tamper-proof storage; SW version encoding: Major.Minor.Patch encoded as uint32 (e.g., 0x00010203 = 1.2.3); NvM block FBL_SW_VERSION_MIN (uint32, written once per OTA campaign, never decremented); rollback check in FBL: if (AppHeader.SWVersion < NvM_FBL_SW_VERSION_MIN) → NRC 0x72; AUTOSAR: stored in Fee/Eeprom block with CRC and redundant copy; ISO 21434 TARA: downgrade = Threat ID CSF-15, countermeasure = monotonic version counter
▸ Monotonic counter implementation: hardware counters: S32K3 HSE: HSE_SRV_ID_GET_ATTR(HSE_APP_BOOT_CFG_ATTR → BOOT_SEQ_CNT); Infineon AURIX TC3xx: HSM provides monotonic counter via SHE extension; STM32: flash OTP bytes (0x1FFF7800) - each bit burned once; NXP SNVS: Secure Non-Volatile Storage monotonic counter; software (NvM-based): uint32_t rollback_counter stored in two redundant NvM blocks with vote logic; increment protocol: OTA campaign → Backend sends new firmware with min_version field → FBL checks → if version OK → flash → increment counter → confirm; never decrement: if brick during increment → recovery via challenge/response protocol with OEM backend
▸ FBL-side rollback check sequence: UDS RoutineControl 0x31 0x01 0x0203 (CheckMemory): after CRC OK, before writing AppValid flag; read AppHeader.SWVersion (uint32 at AppHeader+8); read NvM MIN_SW_VERSION block; if AppHeader.SWVersion < MIN_SW_VERSION: Dcm_SendNrc(NRC_0x72); Dem_SetEventStatus(FBL_E_ROLLBACK_ATTEMPT, FAILED); do NOT write AppHeader.MagicWord; send DTC 0xD01234 (customer-defined rollback DTC); FBL stays in programming mode waiting for valid image; backend must bundle firmware + updated MIN_SW_VERSION in same campaign; counter update: only after successful Routine 0xFF00 CheckProgrammingDependencies returns OK
▸ Edge cases & recovery: bricked update scenario: power loss during counter increment → counter in indeterminate state → FBL reads both redundant NvM copies, takes max value (conservative); factory reset: OEM service mode (SecurityAccess level 0x05/0x06) allows reset of MIN_SW_VERSION counter - only via OEM dealer tool + signed authorization token; field repair: if ECU shipped with wrong counter - use "golden recovery image" signed with factory key (different key slot); multi-ECU system: FOTA master must maintain per-ECU version table; version mismatch DID 0xF189 (ECU Software Number) used to verify post-update; AUTOSAR UDS: service 0x2E 0xF18E writes Software Fingerprint after successful rollback-protected update
HSM Integration for Bootloader 45 min read
▸ HSM hardware architectures: SHE (Secure Hardware Extension, HIS spec): 16 key slots (SECRET_KEY, MASTER_ECU_KEY, BOOT_MAC_KEY, KEY_1–KEY_10, RAM_KEY); 128-bit AES-ECB/CBC/CMAC; Boot MAC: CMAC-AES-128 over app code region, stored in BOOT_MAC_KEY slot; Cry_SheSecureBoot(startAddr, length) → returns SHE_ERC_NO_ERROR or SHE_ERC_SEQUENCE_ERROR; HSM (Hardware Security Module - full, e.g., S32K3 HSE): dedicated Cortex-M3/M7 core, runs HSE firmware; isolated flash region; exposes mailbox IPC to host CPU; capabilities: RSA-4096, ECDSA-P256/P384, AES-128/256, SHA-256/384/512, TRNG, monotonic counters, key management
▸ S32K3 HSE IPC integration: HSM2HTF/HTF2HSM mailbox registers; host sends HSE_SRV_DESC (service descriptor) → HSE processes → returns response via MU (Message Unit); HSE service IDs relevant to FBL: HSE_SRV_ID_HASH (SHA-256 of code), HSE_SRV_ID_ECDSA_VERIFY (verify signature), HSE_SRV_ID_AES_GCM_ENC/DEC (encrypt update package), HSE_SRV_ID_OEM_BOOT_CFG (monotonic counter, secure boot config), HSE_SRV_ID_KEY_IMPORT (provisioning); MU channels: CH0–CH3; FBL code: HSE_Send(MU_CH0, &hseReq); while(HSE_IsBusy(MU_CH0)); result = HSE_Read(MU_CH0, &hseResp); error codes: HSE_SRV_RSP_OK (0x55A5AA33), HSE_SRV_RSP_NOT_ALLOWED (0xAA55A533); typical SHA-256 + ECDSA-P256 verify < 5 ms on S32K344
▸ Key provisioning workflow: manufacturing line: HSE lifecycle state = CUST_DEL (customer delivery); NXP HSE host demo tool or custom HSE provisioning app; steps: (1) HSE_SRV_ID_FORMAT_KEY_CATALOGS: define key catalog (NVM key slots); (2) HSE_SRV_ID_IMPORT_KEY: import OEM ECDSA public key (keyType=HSE_KEY_TYPE_ECC_PUB, keyBitLen=256, keyInfo.keyFlags=HSE_KF_USAGE_VERIFY); (3) Advance lifecycle to OEM_PROD: HSE_SRV_ID_CHANGE_LIFE_CYCLE; (4) Lock JTAG debug: HSE_SRV_ID_SET_ATTR(HSE_DEBUG_AUTH_MODE_ATTR); post-provisioning: public key immutable in HSE NVM; SHE: CMD_LOAD_KEY with M1/M2/M3/M4/M5 protocol (AES-based key update protocol); MASTER_ECU_KEY loaded first, then other keys derived
▸ AUTOSAR Crypto Stack integration: CryptoDriver (CryDrv) → CryptoInterface (CryIf) → CryptoServiceManager (CSM) → SecM (Security Module); AUTOSAR CSM job config: CsmJob_FblVerifySignature { CsmKeyRef → CryKey_OEM_PubKey; CsmPrimitiveRef → CsmPrimitive_EcdsaP256Verify }; CSM call: Csm_SignatureVerify(jobId, mode, dataPtr, dataLen, sigPtr, sigLen, resultPtr); CryDrv implementation: CryDrv_EcdsaP256Verify() → HSE IPC call; key handle: Crypto_KeyElementSet(keyId, CRYPTO_KE_SIGNATURE_KEY, pubKeyDer, 91); error mapping: HSE_SRV_RSP_VERIFY_FAILED → CRYPTO_E_VER_NOT_OK → Csm returns E_NOT_OK → SecM returns SECM_NOT_OK → FBL writes DEM event + NRC 0x72; FBL_E_APPLICATION_SIGNATURE_FAILURE DTC stored in Dem for field traceability
Hands-On: Secure Update Implementation 60 min read
▸ Lab setup & toolchain: S32K344-EVB or QEMU/Renode emulator; GCC ARM: arm-none-eabi-gcc -mcpu=cortex-m7 -Os; OpenSSL for host-side signing; python-udsoncan + python-doip for UDS client; TRACE32 for debug; lab steps: (1) generate ECDSA-P256 key pair with OpenSSL; (2) build FBL with SecM_VerifySignature() stub replaced by HSE call; (3) build application with AppHeader including SWVersion=0x00010001; (4) sign app: openssl dgst -sha256 -sign oem_priv.pem app.bin > app.sig; (5) assemble update package: cat app.bin app.sig > signed_app.bin; verify host-side: openssl dgst -sha256 -verify oem_pub.pem -signature app.sig app.bin → OK
▸ UDS reprogramming sequence with security: python UDS client: conn = IsoTPSocket('can0', txid=0x7E0, rxid=0x7E8); client = Client(conn); client.change_session(DiagnosticSessionControl.Session.programmingSession); seed = client.request_seed(0x01).security_seed; key = compute_key_aes128(seed, SECRET_KEY); client.send_key(0x02, key); addr = 0x8000; size = os.path.getsize('signed_app.bin'); resp = client.request_download(MemoryLocation(addr, size, 4, 4)); block_size = resp.max_length; for i, chunk in enumerate(chunks(signed_app_bytes, block_size-2)): client.transfer_data(i+1, chunk); client.request_transfer_exit(); client.start_routine(0xFF01); result = client.start_routine(0x0203); assert result.status_record == 0x00 (CRC OK, sig OK); client.ecu_reset(ECUReset.ResetType.hardReset)
▸ FBL-side secure verification code: after TransferExit: uint8_t digest[32]; Csm_HashStart(CSM_JOB_SHA256_FBL); Csm_HashUpdate(CSM_JOB_SHA256_FBL, (uint8_t*)APP_START_ADDR, app_size - SIG_LEN); Csm_HashFinish(CSM_JOB_SHA256_FBL, digest, &digestLen, TRUE); uint8_t *sig = (uint8_t*)APP_START_ADDR + app_size - SIG_LEN; Std_ReturnType verResult = Csm_SignatureVerify(CSM_JOB_ECDSA_VERIFY, CRYPTO_OPERATIONMODE_SINGLECALL, digest, 32, sig, 64, &verifyResult); if (verResult != E_OK || verifyResult != CRYPTO_E_VER_OK) { Dcm_SendNrc(0x72); AppHeader->MagicWord = 0x00000000; Dem_SetEventStatus(FBL_E_APP_SIG_FAIL, DEM_EVENT_STATUS_FAILED); } else { AppHeader->MagicWord = 0xA5A5A5A5; NvM_WriteBlock(NVM_BLOCK_APP_HEADER, AppHeader); }
▸ Validation checklist & failure injection: test 1 - valid signed firmware: expect 0x77 (TransferExit positive), routine 0x0203 returns 0x00, ECU reboots into new app; test 2 - tampered firmware (flip byte in app.bin): expect NRC 0x72 from CheckMemory routine, DTC set, no boot into app; test 3 - rollback (SWVersion=0x00000001 < MIN=0x00010001): expect NRC 0x72 from CheckProgrammingDependencies, DTC FBL_E_ROLLBACK set; test 4 - SecurityAccess brute force (3 wrong keys): expect NRC 0x36 then NRC 0x37 with 10-min delay; test 5 - power loss during TransferData: reprogram from block 1 (FBL block counter maintained in RAM); TRACE32 breakpoint: set BP at SecM_VerifySignature(), inspect return value; expected outputs: all 5 tests pass; TRACE32 shows digest matches; vFlash log: "Flashing completed successfully. Signature OK."
5
OTA & Production Workflows
5 chapters • 3.8 hrs reading
OTA Update Architecture 45 min read
▸ OTA system layers: Cloud Backend (AWS IoT Core / Eclipse hawkBit / Harman OTA / Aptiv VIP): stores firmware repository, manages campaign targeting (VIN filter, HW version range), delivers signed update packages via HTTPS (TLS 1.3, mutual auth); Telematics Control Unit (TCU/HU): receives package, stores in eMMC update partition, orchestrates on-vehicle distribution; FOTA Master (Central Gateway or dedicated OTA manager ECU): controls UDS reprogramming of target ECUs; FOTA Client (target ECU bootloader): executes UDS sequence; standards: AUTOSAR UCM (Update and Configuration Management, SWS_UCM); ISO 24089: Software Update Engineering for Road Vehicles; UN R156: cybersecurity regulation requiring SUMS (Software Update Management System)
▸ Update package format & delivery: AUTOSAR UCM package: .ucp (Update Campaign Package) ZIP containing: SwClusterManifest.xml (packageVersion, targetVehicle VIN mask, requiredHW, targetSW, dependencies), SoftwarePackage.bin (signed firmware), Signature.p7s (CMS/PKCS#7 detached signature); HTTPS delivery: TCU downloads via TLS 1.3, HMAC-SHA256 integrity, stores in inactive partition; delta updates: bsdiff/courgette patch (e.g., 200 KB patch for 2 MB firmware saves 80% bandwidth); compression: zlib deflate, block size 4096 B; bandwidth budget: LTE-M (Cat-M1): 300 kbps downlink → 200 KB/s effective → 10 MB firmware in ~50 s; package authentication: TCU verifies Signature.p7s before storing, FOTA Master re-verifies before programming
▸ Update state machine & campaign management: states: IDLE → DOWNLOADING → DOWNLOADED → VERIFYING → VERIFIED → INSTALLING → INSTALLED → ACTIVATING → ACTIVE (or FAILED/ROLLBACK); UCM state machine: RequestUpdate() → ProcessSwPackage() → Activate() → GetSwPackageStatus(); precondition checks before install: vehicle speed = 0 (via VehicleSpeed signal CAN frame), engine off (IgnStatus SNA), voltage ≥ 12.0 V (ADC reading from PowerMgmt), no active DTCs in safety-critical ECUs; AUTOSAR UCM API: Ucm_RequestUpdate(packageId) → UCM_E_OK; Ucm_Activate() → triggers FOTA Master to begin UDS; rollback trigger: if Ucm_GetSwPackageStatus() returns UCM_PACKAGE_INSTALL_FAILED after timeout → Ucm_Rollback(); timeout: 30 min per ECU, 90 min total campaign
▸ Vehicle network distribution: FOTA Master uses DoIP for Ethernet-connected ECUs (ISO 13400-2, TCP 13400, routing activation 0x0005 → logical address routing); uses ISO 15765-2 CAN TP for CAN-connected ECUs (block size 0, STmin 0 ms for maximum throughput ~45 KB/s at 500 kbps CAN); FOTA Master reprograms ECUs sequentially or in parallel: parallel only if ECUs on different networks (e.g., ADAS on Ethernet, Body on CAN simultaneously); bandwidth arbitration: FOTA Master requests network bandwidth from ComM (Communication Manager); EthSM/CanSM states checked before starting; partial network wake: NM wakes CAN network before programming; CRC-32 passed to FBL in TransferExit CRC field; FOTA Master logs per-ECU status: {ECU_addr, old_SW_ver, new_SW_ver, result, timestamp} written to NvM and uploaded to backend
FOTA Master / Slave Patterns 40 min read
▸ FOTA Master role & responsibilities: FOTA Master ECU (typically Central Gateway, Domain Controller, or HPC): orchestrates all UDS reprogramming; responsible for: (1) receiving package from TCU, (2) authenticating package, (3) sequencing ECU resets and NM wake-up, (4) distributing firmware over DoIP/CAN TP, (5) coordinating activation and rollback; FOTA Master implements: UDS tester (client) for all downstream ECUs; AUTOSAR module: Fbl_Master or custom app using Dcm stack in tester role; FOTA Master also manages its own self-update: while updating self, temporary watchdog token prevents infinite reset loop; AUTOSAR UCM Master vs UCM: UCM Master = orchestration; UCM = local SW management per ECU; UCM Master API: UcmMaster_ProcessSwPackage(), UcmMaster_Activate(), UcmMaster_GetCampaignStatus()
▸ FOTA Slave (target ECU) patterns: Single-FBL pattern: one FBL manages both CAN TP receive + flash programming + UDS server; suitable for low-RAM ECUs (≥16KB RAM); Dual-processor pattern: host CPU runs UDS/FBL, HSM runs security verification (S32K3 HSE); memory-constrained slave: downloadable flash driver loaded into RAM (flash_driver.bin downloaded via WriteMemoryByAddress 0x3D before RequestDownload, then executed from RAM); FBL entry triggers: FBL checks on power-on: (a) NvM BootFlag = 0xA5A5A5A5, (b) GPIO pin (e.g., PA5 pulled low by FOTA Master via dedicated wire on some platforms), (c) DiagnosticSessionControl extended → write DID 0xF101 BootFlag via 0x2E service → ECUReset → FBL enters programming mode
▸ Sequential vs parallel programming patterns: sequential: FOTA Master programs ECU1 fully → activate → ECU1 valid → program ECU2; safe but slow for many ECUs; parallel: multiple ECUs programmed simultaneously over different networks (e.g., ECU A over Ethernet, ECU B+C over CAN bus 1, ECU D+E over CAN bus 2); FOTA Master uses multithreading or RTOS tasks per channel; inter-ECU dependency: if ECU B depends on ECU A interface version → ECU A must complete first; dependency graph from manifest.json; programming bandwidth: CAN 500kbps = ~45 KB/s effective (ISO 15765-2, Block Size 0, STmin 0), DoIP Ethernet = ~2 MB/s; abort on failure: if any ECU fails → FOTA Master calls Dcm_ResetToDefaultSession() on all ECUs → trigger rollback on completed ECUs via WriteMemoryByAddress to clear BootFlag
▸ Error recovery & anti-bricking: FOTA Master state persistence: after each phase, write progress to NvM (CAMPAIGN_STATE block: {phase, current_ECU_index, retry_count}); on power loss recovery: read NvM state → resume from last completed ECU; max retries: 3 per ECU; on 3rd failure → mark ECU as failed, continue with others; post-campaign: upload result to cloud (ECU_addr, old_ver, new_ver, result_code, error_details); anti-bricking: FBL dual-bank: Bank A always valid as fallback; FBL itself protected by Boot ROM (never overwritten by application); watchdog: FBL sets HW watchdog window to 5000 ms per block; stale session timeout: S3server_boot=5000 ms → if FOTA Master loses connection, FBL resets; production variant: FOTA Master disables SOTA (Software OTA) during driving mode using VehicleState arbitration signal
Production End-of-Line Programming 35 min read
▸ EOL programming workflow overview: production line ECU arrives blank (unprogrammed flash); EOL programmer station (e.g., Softing TDX, Vector VN8900, Lauterbach PowerDebug) connects via: JTAG/SWD (fastest: 1-4 MB/s) for initial FBL flash, or CAN/Ethernet for application layer; sequence: (1) JTAG-flash FBL + bootparams (blank chip, ~2s for 32KB); (2) UDS programming session: flash application via FBL (SecurityAccess → RequestDownload → TransferData → TransferExit → CheckMemory); (3) HSE key provisioning: write OEM public key to HSE NVM, advance lifecycle; (4) write VIN + variant coding DIDs; (5) lock JTAG debug access; (6) final ECU reset + functional test; cycle time target: total EOL ≤ 45 s per ECU including functional test
▸ EOL tools & UDS sequence: Vector VN8900 + CANoe: uses .cbf script to automate UDS EOL sequence; Softing TDX: XML-based EOL plan (FlashJob) with steps: EraseMemory → Download → Verify → WriteDataByIdentifier; vFlash: .vFlashPack with FlashLayout (.fld) + firmware .hex/.s19; INCA EOL mode: direct parameter/data set download to ECU NVM; JTAG: OpenOCD or Lauterbach T32 cmm script: FLASH.ReProgram 0x00000000 /Srec fbl.s19; SecurityAccess for EOL: dedicated level 0x07/0x08 (EOL-only) with time-limited seed (expires after 30 min); DID writes: WriteDataByIdentifier 0x2E 0xF190 (VIN, 17 bytes), 0xF17C (programming date), 0xF180 (SW version string), 0xF18A (reprogramming attempts counter)
▸ Key provisioning & HSE lifecycle: NXP HSE provisioning flow: (1) Flash HSE firmware via JTAG: T32 FlashProgram HSE_FW.bin 0x00400000; (2) ECU boot into CUST_DEL lifecycle; (3) Host app sends HSE_SRV_ID_FORMAT_KEY_CATALOGS (define 10 NVM key slots + 4 RAM key slots); (4) HSE_SRV_ID_IMPORT_KEY: keyType=HSE_KEY_TYPE_ECC_PUB, keyBitLen=256, keyFlags=HSE_KF_USAGE_VERIFY, keyBuffer=oem_public_key_der; (5) HSE_SRV_ID_CHANGE_LIFE_CYCLE: advance to OEM_PROD; (6) lock debug: HSE_SRV_ID_SET_ATTR(HSE_DEBUG_AUTH_MODE_ATTR, HSE_DEBUG_AUTH_MODE_CR); SHE provisioning: send M1/M2/M3 messages for CMD_LOAD_KEY; MASTER_ECU_KEY loaded first; then BOOT_MAC_KEY with BOOT_PROT=1 (write-protect); tooling: NXP HSE host demo app or custom Python script using SPI/UART transport
▸ EOL functional test & compliance: post-programming functional test via EOL tester: (1) ReadDataByIdentifier 0x22 0xF190 → verify VIN; (2) ReadDataByIdentifier 0xF189 → verify SW version matches expected; (3) CommunicationControl 0x28 0x03 (enable all comms); (4) application start: ECUReset 0x11 0x01, wait 2s, send TesterPresent 0x3E 0x80, confirm positive response from application; (5) run basic functional checks (e.g., CAN signals present, DTC count = 0); traceability: write EOL result to NvM (EolResult block: PASS/FAIL + timestamp + station_id); DID 0xF17C (programming date, 4 bytes BCD: YYYYMMDD); DID 0xF15B (fingerprint: tester serial + timestamp); compliance: ISO 14229-1 Annex C EOL requirements; VDA scope: §6.6 flash programming traceability; fail rate target: <0.1% EOL failures; golden ECU: 100 EOL cycles per batch for process qualification
Bootloader Testing & Validation 40 min read
▸ FBL test strategy & coverage: ISO 26262 Part 6 (SW unit testing) requires MC/DC coverage ≥ 100% for ASIL-D; FBL test levels: (1) Unit test: FBL_Core functions (ApplFblBoot_CheckValidity, FblLookForStartMessage, FblMemSectorEraseStart) - test with LDRA Testbed or VectorCAST; (2) Integration test: FBL + UDS stack on target ECU with CAN/Ethernet tester; (3) System test: complete reprogramming campaign on vehicle or HIL; coverage goals: branch coverage ≥ 95%, MC/DC for all safety-critical conditions (AppValid check, CRC verify, SecurityAccess); negative test cases: corrupted download, wrong SecurityAccess key, power fail during erase, wrong block counter - each must produce specified NRC and no corruption of valid app
▸ Automated test framework: python-udsoncan + python-doip for automated UDS test scripts; pytest-based test suite: @pytest.mark.parametrize for NRC combinations; test cases: test_valid_reprogramming(), test_security_access_lockout(), test_crc_mismatch_nrc72(), test_block_counter_error(), test_power_cycle_recovery(), test_rollback_rejected(); hardware-in-loop: dSPACE SCALEXIO or National Instruments PXI with FPGA-based fault injection (power cut, CAN bus error injection); Vector CANoe .NET or CAPL script: diag.SendRequest(DiagRequest_0x10_02); diag.WaitForResponse(200); Assert(diag.Response[0] == 0x50); Vector vTESTstudio: keyword-driven test design mapped to DOORS requirements; CAPL test: testStep("SecurityAccess", "Send requestSeed"); output.writeLine("Seed: " + hex(seed_value))
▸ Hardware fault injection testing: power interruption during TransferData: use relay on VBAT controlled by NI DAQ; assert power off after sending block N/2 of firmware; verify: FBL recovers on power restore, requests retransmission from last valid block; CAN bus error injection: CANoe CAPL: canSetBusError(BUS_ERROR_FORM); verify FBL NRC 0x25 (upload/download blocked); flash erase failure simulation: override MemDriver_EraseSync() stub to return kMemDriverFailed; verify FBL sends NRC 0x72; supply voltage: program at 10.5V (low), 16.5V (high), normal 13.8V; verify programming succeeds in all cases; temperature: -40°C programming test in environmental chamber; FTFC timing: at -40°C, sector erase = 300ms (vs 200ms at 25°C) - FBL timeout must allow; timeout values: P2_boot=50ms at extremes
▸ Validation report & metrics: FBL validation report structure (per AUTOSAR SWS_FBL, ISO 26262 Part 8): test case ID, requirement ID (DOORS link), precondition, steps, expected result, actual result, pass/fail, tester, date; metrics: total test cases (target ≥ 200), pass rate ≥ 99.5%, statement coverage ≥ 95%, branch coverage ≥ 90%; timing measurements: FBL entry time < 100ms, SecurityAccess round-trip < 200ms, sector erase < 500ms, full 256KB flash < 10s; regression: automated nightly run on CI/CD pipeline (Jenkins + Git trigger); traceability matrix: each FBL function → test case → DOORS requirement → ISO 26262 work product; review: FBL safety validation signed off by functional safety manager before SOP (Start of Production)
Hands-On: Full OTA Update Pipeline 65 min read
▸ Lab environment setup: Eclipse hawkBit OTA server (Docker): docker run -p 8080:8080 hawkbit/hawkbit-update-server; or AWS IoT Core with MQTT + S3 firmware bucket; Vehicle-side: Raspberry Pi 4 as TCU (downloads from hawkBit), S32K3-EVB as target ECU (FBL), Ethernet for DoIP, CAN for legacy ECUs; tooling: hawkBit REST API (curl / Postman), python-doip, python-udsoncan, OpenSSL for signing; network: Pi ↔ S32K3 via USB-to-Ethernet (DoIP); lab steps: (1) build FBL v1.0 + App v1.0 for S32K3; (2) build App v2.0 with SWVersion incremented; (3) sign App v2.0 with ECDSA-P256; (4) create UCM package (zip manifest.json + app_v2.0_signed.bin); (5) upload to hawkBit; (6) trigger campaign; (7) observe full OTA pipeline
▸ Cloud-to-vehicle delivery: hawkBit campaign config: DistributionSet {name: "App_v2.0", version: "2.0.0", modules: [{type: firmware, artifact: app_v2.0.ucp}]}; TCU Python client: import hawkbit; client = hawkbit.Client(SERVER, TENANT, TOKEN); if client.poll(): pkg = client.download(); TCU verifies package HMAC: hmac.new(OEM_SECRET, pkg_bytes, sha256).digest(); store to /var/update/app_v2.0.ucp; then trigger FOTA Master via SOME/IP service call FotaMaster_StartUpdate(package_path); bandwidth monitoring: requests.get(url, stream=True); monitor progress with tqdm; retry on network error with exponential backoff (1s, 2s, 4s, max 3 retries); security: TLS 1.3 with mutual auth (client cert = TCU device cert signed by OEM PKI)
▸ FOTA Master orchestration code: Python FOTA Master script for lab: import udsoncan, doip_client; client = doip_client.DoIPClient("192.168.1.100", 0x0001); uds = udsoncan.Client(client); uds.change_session(0x02); seed = uds.request_seed(0x01); key = compute_aes128_key(seed, SECRET); uds.send_key(0x02, key); uds.erase_memory(0x8000, 0x38000); # RoutineControl EraseMemory with open("app_v2.0_signed.bin", "rb") as f: data = f.read(); block = uds.request_download(MemoryLocation(0x8000, len(data))); for i, chunk in enumerate(chunks(data, block.max_length-2)): uds.transfer_data(i+1, chunk); uds.request_transfer_exit(); result = uds.check_memory(); assert result == 0x00; uds.ecu_reset(0x01); time.sleep(2); resp = uds.tester_present(); assert resp is not None; print("OTA complete, App v2.0 active")
▸ Validation checklist & failure scenarios: test 1 - happy path: App v1.0 → v2.0 via hawkBit; verify DID 0xF189 reads "2.0.0" after reset; test 2 - tampered package: flip byte in app_v2.0_signed.bin; expect NRC 0x72 from CheckMemory, ECU stays on v1.0; test 3 - rollback blocked: attempt to flash v0.9 (< MIN_SW_VERSION=1.0); expect NRC 0x72 from CheckProgrammingDependencies; test 4 - power loss during download: kill Pi script after 50% transferred; reconnect; verify FBL restarts from block 1; test 5 - network interruption: iptables -A OUTPUT -d 192.168.1.100 -j DROP during TCU download; verify TCU retries with backoff; common pitfalls: (a) S3server_boot timeout - tester must send TesterPresent every 4s; (b) STmin too low for slow target - set STmin=5ms; (c) wrong block counter after retry - reset counter to 0x01 on retry; expected outputs: all 5 tests pass; hawkBit dashboard shows campaign SUCCESS; vFlash log: "Programming job completed. All ECUs: PASSED"

What You'll Learn

Develop complete flash bootloaders for automotive ECUs
Implement UDS-based reprogramming with security access
Design dual-bank and rollback-safe update strategies
Integrate HSM-based secure boot and code signing
Handle OTA update workflows for field updates
Test and validate bootloader robustness

Prerequisites

Strong C programming skills
Understanding of UDS diagnostics basics
Knowledge of flash memory concepts
Full Access
Free with Pro
Enroll Now Browse Modules

This course includes:

26 detailed documentation chapters
Downloadable resources
Searchable text documentation
Code snippets & technical diagrams
Hands-on exercises
Lifetime access
Certificate of completion