| Responsibility | Primary Bootloader (PBL) | Secondary Bootloader (SBL) |
|---|---|---|
| Location | Dedicated flash region; hardware write-protected | Downloaded to RAM or application flash area |
| Persistent? | Always present; never erased | Temporary; downloaded by PBL; destroyed after use |
| Handles UDS? | Minimal: session control, security access, SBL download | Full: erase, download, verify, checksum routines |
| Can erase itself? | No — PBL region is hardware-protected | Yes — can erase application region |
| Size | 4–32 kB (small, simple, immutable) | 32–256 kB (full flash driver + UDS stack) |
| Loaded by | Factory programming (JTAG/in-circuit) | PBL over UDS during reprogramming session |
The Bootloader's Role in ECU Reprogramming
Two-Stage Bootloader Architecture Rationale
Power-on / Reset
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ PRIMARY BOOTLOADER (PBL) — always runs first │
│ Region: 0x80000000–0x80007FFF (32 kB) — HARDWARE PROTECTED │
│ │
│ Decision logic: │
│ 1. Is reprogramming flag set in NvM? → goto reprogramming mode │
│ 2. Is application CRC valid? → jump to application │
│ 3. Is SBL present and valid? → jump to SBL │
│ 4. None of above → stay in PBL, await UDS │
└──────────────────────────────┬──────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌───────────────────┐
│ APPLICATION │ │ SBL (in RAM or │ │ REPROGRAMMING │
│ 0x80020000+ │ │ SBL flash area) │ │ MODE (PBL only) │
│ Normal run │ │ Handles erase/ │ │ Awaits DoIP/CAN │
│ │ │ download/verify │ │ tester │
└─────────────────┘ └──────────────────┘ └───────────────────┘
Why two stages?
- PBL is never erased → always recoverable even if app is corrupt
- SBL contains the flash driver → can erase/write application flash
- SBL lives in RAM → can program its own flash region (no self-erase)
- SBL is verified by PBL before execution → chain of trustFlash Memory Layout: Aurix TC3xx Example
Address Size Region Protection Contents 0x80000000 ── 32 kB PBL HW locked Primary bootloader 0x80008000 ── 64 kB SBL header Lockable SBL metadata + signature 0x80018000 ── 256 kB SBL body Lockable Secondary bootloader code 0x80058000 ── 32 kB Bootloader data Lockable Config, fingerprints, NvM 0x80060000 ── 4 kB Boot flags Write-once Reprogramming trigger flags 0x80061000 ── 3 kB Reserved — — 0x80064000 ── 7424 kB Application Erasable Application + AUTOSAR SWC 0x80780000 ── 512 kB Calibration Erasable Online/offline calibration 0x80800000 ── [DFLASH] Data Flash Various NvM, VIN, mileage, security Key constraints: - PBL sector(s): fused/OTP-locked at factory → cannot be erased in field - Boot flags sector: written in programming session; read at every POR - Application region: erased by SBL RoutineControl 0xFF00 before download - Calibration region: can be updated independently (no app re-flash needed)
Vector Table and Interrupt Relocation
/* Problem: both PBL and Application have a vector table */
/* ARM Cortex-M: VTOR register selects which vector table is active */
/* Aurix TriCore: trap table controlled by BTV CSR */
/* PBL starts with its own vector table at flash base (0x80000000) */
/* When jumping to application: must redirect interrupts to app's vector table */
#include
/* Cortex-M: SCB->VTOR register at 0xE000ED08 */
#define SCB_VTOR (*(volatile uint32_t *)0xE000ED08u)
#define APP_BASE 0x80020000u /* application start address */
void Pbl_JumpToApplication(void)
{
/* Step 1: Disable all interrupts */
__disable_irq();
/* Step 2: Relocate vector table to application's vector table */
SCB_VTOR = APP_BASE; /* app vector table starts at app base */
/* Step 3: Get application stack pointer and reset handler */
uint32_t *app_vectors = (uint32_t *)APP_BASE;
uint32_t app_sp = app_vectors[0]; /* initial stack pointer */
uint32_t app_reset = app_vectors[1]; /* reset handler address */
/* Step 4: Set stack pointer */
__set_MSP(app_sp);
/* Step 5: Jump to application reset handler */
void (*app_entry)(void) = (void (*)(void))(app_reset | 1u); /* thumb bit */
app_entry();
/* never returns */
}
/* Aurix TriCore: BTV (Base address of Trap Vector table) */
/* __MTCR(BTV, APP_BASE); */
/* __MTCR(BIV, APP_INTERRUPT_BASE); */ Summary
The two-stage bootloader architecture is universal in automotive ECUs because it solves the self-erase problem: the PBL is hardware-protected and can never be erased, so it is always available to re-download a corrupt SBL or application. The SBL contains the flash driver and runs from RAM or a protected flash region, so it can safely erase and program the application area. Vector table relocation is mandatory before jumping to the application — without it, all interrupts and exceptions after the jump will use the PBL's handlers, which typically call fault handlers or reset the ECU.
🔬 ECU Flash Memory Layout — Detailed Breakdown
Understanding the exact memory layout of a bootloader-enabled ECU is fundamental to designing a robust OTA update system. A typical Cortex-M / TriCore ECU has the following flash regions:
- Interrupt Vector Table (IVT) / Reset Vector (0x00000000): The first few KB of flash. On reset, the CPU fetches the reset handler address from this location. The bootloader IVT lives here — if it is corrupt, the ECU cannot boot at all. This region must be write-protected (hardware flash block protection) and must never be erased during application flashing.
- Bootloader code region: Typically 16–64 KB. Contains the UDS programming session handler, flash driver (loaded to RAM), and crypto verification logic. Should be in a separate flash sector from the application so application erase cannot touch it.
- Application code region: The main ECU software. This is what gets erased and reprogrammed during OTA/EOL flashing. Typically 1–4 MB on modern ECUs.
- Calibration data region: Read-only calibration maps (A2L data). Separate region allows calibration updates without full software reflash. Usually the last few hundred KB before the NvM region.
- NvM / Data Flash region: Typically a separate data flash or emulated EEPROM area. Stores DTC history, odometer, adaptation values, and VIN. Must never be erased during software programming — erase of this region is a critical production error.
- Boot Status Block: A small (32–256 byte) region written by the bootloader to track: programming attempt count, last programming result (SUCCESS/FAIL), valid application fingerprint. Used for anti-rollback and brick recovery logic.
🏭 Production Bootloader Lessons
- Dual-bank (A/B) flash: High-end ECUs (e.g., TriCore TC399 with 8 MB flash) use two complete application banks. The bootloader flashes bank B while the ECU runs from bank A. After verification, the boot bank pointer is atomically updated. If verification fails, the ECU continues on bank A. This eliminates the 'brick' risk present in single-bank designs.
- Write protection in production: Once the bootloader has been flashed and verified at EOL, hardware write-protect bits in the flash controller (PFLASH/DFLASH protection registers) are set to prevent any overwrite of the bootloader region. These bits are one-time-programmable on some MCUs (NXP S32K) — misconfiguring them permanently bricks the ECU.
- CRC verification performance: A CRC32 over a 4 MB application image takes ~2 seconds in software on a 40 MHz CPU. At EOL with 100 ECUs per hour, this adds 200 seconds/hour of wasted time. Hardware CRC accelerators (available on TriCore, S32K, RH850) reduce this to ~100 ms.
⚠️ Bootloader Design Pitfalls
- Bootloader and application sharing the same flash sector: An application erase command that targets the first flash sector will erase the bootloader. The ECU becomes unrecoverable without physical programmer access. Always place bootloader in a dedicated sector with hardware protection.
- No boot validity check on application: If the application CRC/signature is not verified before jumping from bootloader to application, a partially written application (from an interrupted flash sequence) will execute corrupted code. Always check the application fingerprint in the Boot Status Block before every reset.
- NvM erased during application reflash: An erase command with an address range that extends into the Data Flash region will erase customer NvM data (DTC history, mileage, VIN). This is an EOL programming defect that causes warranty claims. Validate address range strictly: StartAddress + Length must not exceed ApplicationEndAddress.
- Anti-rollback not implemented: Without anti-rollback protection, an attacker with access to the UDS programming interface can reflash an older vulnerable firmware version — bypassing security fixes. Implement a monotonically increasing software version counter stored in NvM, and reject flashing any image with a lower version number.
📊 Industry Note
In 2020, a European OEM recalled 500,000 vehicles because their bootloader did not validate the application CRC before jumping. Corrupted OTA updates caused ECUs to enter an infinite reset loop with the application partially written. The cost of the recall vastly exceeded the 2-day engineering effort required to add proper CRC validation.
🧠 Knowledge Check — Click each question to reveal the answer
❓ Why must the flash driver be copied to RAM before execution during a flash programming sequence?
❓ What information should a Boot Status Block contain, and why is it important for OTA update reliability?
❓ An ECU supports dual-bank (A/B) flash updates. The OTA update to bank B fails mid-programming. Describe the exact recovery sequence.