ECU Flashing & Reprogramming
Overview
”The RequestDownload service is used by the client to initiate a data transfer from the client to the server (ECU).”
Flashing is not a service — it is a choreography. ISO 14229 defines no “flash” request. Reprogramming an ECU means driving eight different services in a fixed order: 0x10 to reach PROGRAMMING, 0x27 to unlock, 0x31 to erase and verify, 0x34/0x36/0x37 to move the bytes, 0x3E to keep the session alive throughout, and 0x11 to boot what you just wrote. Get the order wrong and the ECU says no; get it half-right and the ECU is a brick.
This is the one irreversible operation in UDS. Erase is destructive and happens before the first payload byte arrives. If the run stops between erase and verify, the region stays erased or invalid and the next reset boots the bootloader instead of the application. That failure mode is not a bug in the simulator — it is exactly what happens on a real bench, which is why the flow re-erases on every attempt and needs no resume state.
The Reprogramming Ladder
Fifteen stages for a bootloader image, eleven for an application image. Toggle the image type to see the four driver-staging stages appear and disappear, then open any stage for the frames it puts on the wire.
Reprogramming Ladder
Every stage the orchestrator runs, in send order. Select a stage to see the bytes on the wire.
The resident bootloader stages a flash driver into RAM, then programs the Bootloader partition through it — a sector cannot erase itself while executing from it.
Percentages are the simulator’s fixed progress weights, normalised over the stages actually present — which is why an erase appears to stall at the same point on every run.
The full ladder, in send order. The four driver* stages run only for a BSW (bootloader) image — an application image is programmed directly by the resident bootloader, so those stages are skipped entirely.
| # | Stage | Bytes sent | Weight | BSW only |
|---|---|---|---|---|
| 1 | Connecting | 10 03 → 10 02 — session control | 3 | |
| 2 | Unlocking security | 27 01 / 27 02 — seed & key | 2 | |
| 3 | Preflight checks | 31 01 0203 — programming dependencies | 2 | |
| 4 | Requesting driver | 34 — RequestDownload (flash driver) | 3 | ✓ |
| 5 | Transferring driver | 36 — TransferData (driver blocks) | 10 | ✓ |
| 6 | Committing driver | 37 — RequestTransferExit | 2 | ✓ |
| 7 | Activating driver | 31 01 FF03 — activate flash driver | 3 | ✓ |
| 8 | Erasing | 31 01 FF00 — erase memory | 15 | |
| 9 | Requesting download | 34 — RequestDownload (image) | 1 | |
| 10 | Transferring data | 36 — TransferData blocks | 44 | |
| 11 | Committing image | 37 — RequestTransferExit | 2 | |
| 12 | Integrity check | 31 01 FF01 + 31 03 — CRC32 | 4 | |
| 13 | Verify programming | 31 01 FF02 — mark region valid | 7 | |
| 14 | ECU reset | 11 01 — hard reset | 2 | |
| 15 | Complete | Application running | — |
Weights are fixed, not measured. The progress bar uses the weights above, normalised over the stages actually present. That is why an erase always appears to stall at the same percentage — the ECU reports no fraction for a routine, only “in progress” and “done”. Only the block-transfer stages report real fractional progress, because only they have a byte count to divide by.
Byte-Exact Walkthrough
A complete bootloader-image run against the simulator’s mock ECU. Addresses and sizes come from the real memory map; an application run swaps the target for Flash Code at 0x00001000.
Step 1 — Enter EXTENDED
10 03> PROGRAMMING is not reachable from DEFAULT. The ladder always climbs through EXTENDED first.
EXTENDED active
50 03 00 64 01 F4> P2 = 0x0064 (100 ms). P2* = 0x01F4, encoded at 10 ms resolution = 5000 ms.
Step 9 — Erase memory
31 01 FF 00 44 00 40 00 00 00 00 80 00> RID 0xFF00 with the shared option record: ALFID 0x44, address 0x00400000, size 0x8000.
Deferred — then a pushed 71 01 FF 00 01 00
7F 31 78> Erase does not answer synchronously. The ECU sends 0x78 responsePending, then pushes the final response when the routine finishes.
Step 15 — Hard reset
11 01> Boot mode is recomputed from the programming states. A valid application boots the application; anything else boots the bootloader.
Resetting
51 01> The keep-alive has already stopped — the run is terminal.
The complete frame log for a 32 KB bootloader image. An application run skips steps 5–8 and targets Flash Code instead.
| Step | Request | Response | Meaning |
|---|---|---|---|
| 1 | 10 03 | 50 03 00 64 01 F4 | EXTENDED — P2 100 ms, P2* 5000 ms |
| 2 | 10 02 | 50 02 00 32 01 F4 | PROGRAMMING — only reachable from EXTENDED |
| — | 3E 80 | (suppressed) | Keep-alive starts, repeating every 2000 ms |
| 3 | 27 01 | 67 01 12 34 56 78 | Seed request, security level 1 |
| 4 | 27 02 B7 6E A6 77 | 67 02 | Key accepted — unlocked |
| 5 | 34 00 44 00 30 00 00 00 00 20 00 | 74 20 10 00 | Driver → RAM 0x00300000, 8 KB; maxBlock 0x1000 |
| 6 | 36 01…36 03 | 76 01…76 03 | 8 KB = 2 × 4094 + 4 bytes |
| 7 | 37 | 77 | Driver bytes committed to RAM |
| 8 | 31 01 FF 03 44 00 30 00 00 00 00 20 00 CC CC CC CC | 7F 31 78 … 71 01 FF 03 01 00 | Driver hash verified — flashDriverActive |
| 9 | 31 01 FF 00 44 00 40 00 00 00 00 80 00 | 7F 31 78 … 71 01 FF 00 01 00 | Region blanked to 0xFF, state → erased |
| 10 | 34 00 44 00 40 00 00 00 00 80 00 | 74 20 10 00 | Image → 0x00400000, 32 KB |
| 11 | 36 01…36 09 | 76 01…76 09 | 32 KB = 8 × 4094 + 16 bytes |
| 12 | 37 | 77 | Bytes land in the Bootloader region |
| 13 | 31 01 FF 01 44 00 40 00 00 00 00 80 00 CC CC CC CC | 71 01 FF 01 00 | CRC32 check starts — fast routine |
| 13 | 31 03 FF 01 | 71 03 FF 01 01 00 CC CC CC CC | Results read back — CRC pass |
| 14 | 31 01 FF 02 44 00 40 00 00 00 00 80 00 CC CC CC CC | 7F 31 78 … 71 01 FF 02 01 00 CC CC CC CC | programmingState → valid; keep-alive stops |
| 15 | 11 01 | 51 01 | Boot mode recomputed → application |
TesterPresent is load-bearing here, not belt-and-braces. During a ten-second erase the tester sends no requests at all. The pushed 0x78 frames travel ECU → tester and do not reset S3, which only resets on requests the ECU receives. Without the 2000 ms keep-alive the session collapses mid-routine and the run dies with the region already erased.
The Shared Option Record
All four flash routines take the same option record, deliberately shaped like the address/size pair in a SID 0x34 request. Learn it once and every routine in the ladder reads the same way.
[ALFID = 0x44] [memoryAddress : 4 bytes BE] [memorySize : 4 bytes BE] ([crc32 : 4 bytes BE])The 0x44 prefix is an addressAndLengthFormatIdentifier: high nibble = the length of the size field, low nibble = the length of the address field. Both are 4, so both fields are four bytes big-endian. The trailing CRC32 is present only on the routines that verify content. A malformed record is answered with NRC 0x13.
| RID | Routine | Option record | Answers | Notes |
|---|---|---|---|---|
0x0203 | checkProgrammingDependencies | none | synchronous | The one standard ISO routine in the ladder |
0xFF00 | eraseFlashMemory | address + size | deferred 0x78 → pushed | Blanks the partition to 0xFF, state → erased |
0xFF01 | checkMemoryIntegrity | address + size + CRC32 | synchronous | The only fast flash routine — computes at START |
0xFF02 | verifyProgramming | address + size + CRC32 | deferred 0x78 → pushed | The commit point — promotes the region to valid |
0xFF03 | activateFlashDriver | address + size + CRC32 | deferred 0x78 → pushed | Authenticates the staged driver against the ECU’s manifest |
0xFF00–0xFF03 are manufacturer-defined, not ISO. ISO 14229-1 reserves the RID range 0xF200–0xFFFF for the vehicle manufacturer and defines only 0x0202 eraseMemory and 0x0203 checkProgrammingDependencies as standard programming routines. Real OEMs each pick their own numbers in that range; the four above are this simulator’s choice. What transfers between programs is the shape — erase, check, commit, and a driver-activation gate — not the identifiers.
Deferred Routines & NRC 0x78
An erase takes seconds; P2 is 50 milliseconds. Three of the four flash routines resolve this the way real ECUs do — they answer 0x78 and push the real answer later.
The tester never polls. Each repeated 7F 31 78 refreshes the deadline to now + P2* (5000 ms), and the routine resolves only when the ECU pushes its 71 01 <RID> … frame. A full P2* window with neither a 0x78 nor a final response is a timeout. The overall budget is the routine’s execution time plus 10 seconds. A manual 31 03 <RID> results request still works from the console — the orchestrator simply never needs it.
0x78 is not an error. It is the only negative response code that means “still working”. Treating it as a failure is the single most common mistake in a hand-written flash client — the run aborts three seconds into a ten-second erase, leaving the region blank.
Deferred erase — 0x78 then a pushed final response
8 stepsBlock Transfer Math
The 0x74 response names the largest block the ECU will accept. Every off-by-one in a flash client lives in the two bytes between that number and the payload you may actually send.
maxNumberOfBlockLength includes the 0x36 service ID and the block-sequence-counter byte. A reported 0x1000 therefore permits 4094 payload bytes, not 4096.
| Quantity | Value | Derivation |
|---|---|---|
| maxNumberOfBlockLength | 0x1000 = 4096 | From the 74 20 10 00 response |
| Usable payload per block | 4094 | maxBlockLength − 2 (SID + BSC) |
| Bootloader image, 32 KB | 9 blocks | 8 × 4094 + 16 remaining |
| Application image, 64 KB | 17 blocks | 16 × 4094 + 32 remaining |
| Flash driver, 8 KB | 3 blocks | 2 × 4094 + 4 remaining |
| Block-sequence counter | 0x01 … 0xFF → 0x01 | Starts at 1, wraps back to 1 |
The counter wraps to 0x01, not 0x00. After block 255 the next block is 36 01 again. ISO wraps through 0x00; this simulator skips it, matching a common real-world implementation quirk. A mismatched counter is answered with NRC 0x73 — and because the negative response carries no expected-counter byte, there is nothing to resynchronise against. The only recovery is a fresh run, which is safe precisely because every run erases first.
ECU Software Architecture
Two questions decide the shape of every flash run: which layer is being written, and which layer is doing the writing. They are never the same layer.
ECU Software Architecture
Which layer runs the flash, and which layer it writes to, depends entirely on the image type.
- ASWFlash CodeValidApplication image slot — the flash target for an ASW image0x00001000 – 0x00011000 · 64 KB
- RAMFlash DriverexecutingActive in RAMTransient helper staged into RAM to program protected flash0x00300000 – 0x00302000 · 8 KB
- BSWBootloadertarget — being writtenProgrammed — pending verifyResident recovery loader — drives the UDS flash sequence0x00400000 – 0x00408000 · 32 KB
- HWMicrocontroller · Flash memoryHardwarePhysical non-volatile memory7 mapped regions
A bootloader cannot erase the sector it is executing from, so a BSW run stages a flash driver into RAM first and programs through it. The driver is transient — a reset releases it.
A microcontroller cannot erase the flash sector it is currently executing from. That single hardware constraint produces the whole two-stage BSW sequence:
| Image type | Target — being written | Executor — doing the writing | Driver staged |
|---|---|---|---|
| ASW (application) | Flash Code, 0x00001000, 64 KB | Bootloader (resident) | No |
| BSW (bootloader) | Bootloader, 0x00400000, 32 KB | Flash Driver in RAM, 0x00300000 | Yes — 8 KB |
The flash driver is transient: it lives in RAM, is authenticated by hash before it is trusted, and does not survive a reset. That is also why you cannot supply your own driver — routine 0xFF03 compares the staged bytes against the ECU’s own flash-driver manifest, and only the matching image passes.
A region’s programmingState is what the layer panel colours itself from, and what boot mode is computed from after a reset:
| State | Panel label | Meaning |
|---|---|---|
erased | Erased | Blanked to 0xFF, no valid content |
programmed | Programmed — pending verify | Bytes written, 0xFF02 has not yet confirmed them |
valid | Valid | Verified and bootable |
invalid | Invalid | Verification failed — will boot to the bootloader |
When It Goes Wrong
Not every negative response is fatal. A flash client that treats them all the same either gives up on a recoverable hiccup or grinds on through a real failure — both leave an unbootable ECU.
| Response | Classification | Behaviour |
|---|---|---|
0x78 responsePending | in-flight | Extends the wait to now + P2* on each repetition. Never fails the run |
0x21 busyRepeatRequest | retryable | Same frame retried 3×, backing off 200 / 500 / 1000 ms |
0x92 / 0x93 voltage | retryable, paused | Holds the phase, surfaces “voltage out of range”, auto-retries every 1 s up to 10× |
0x37 requiredTimeDelayNotExpired | retryable, timed | Waits out the 10 s security penalty once, then re-requests the seed. A second failure is fatal |
0x73 wrongBlockSequenceCounter | fatal | No expected-counter byte is returned, so there is nothing to resync to. Restart the run |
0x33 / 0x7F / 0x7E after setup | fatal — session lost | S3 expired or the session dropped mid-flash |
0x24 / 0x31 / 0x70 / 0x13 / 0x22 | fatal | Sequence or configuration error. The message names the phase and the NRC |
0x72, or a 0xFF01/0xFF02 verdict of 0x01 | fatal — verify mismatch | The region is now invalid. Re-flash from the top |
Retryable means the ECU asked you to wait, not that the write went wrong. Busy and voltage faults are transient conditions on the ECU side and the same frame is safe to resend. A CRC mismatch or a bad block counter means the two sides disagree about what was written — no amount of retrying reconciles that, and the only honest recovery is to erase and start again.
Flashing Wizard Walkthrough
The simulator runs the entire ladder against a mock ECU, one gated stage at a time. Nothing here is a mock-up of a flash tool — the frames in the log are the frames the engine actually processed.
Flashing Wizard
Three steps, at udssimulator.com/flashing.
Pick a firmware image — either one of the synthetic catalog images sized to exactly fill its partition, or your own file. The image type you choose (ASW or BSW) decides the whole shape of the run.
- Image catalog
- Pre-built ASW and BSW images. Each is sized to fill its target partition exactly, so the erase, download and verify records are correct by construction.
- Upload firmware
- File input, drag-and-drop, or a paste-hex textarea. Accepts .hex, .ihex, .s19, .s28, .s37, .srec, .mot and .bin up to 1 MB.
- Target partition picker
- Appears only for address-less input — raw binary and pasted hex carry no load address, so you name the target partition yourself.
- Firmware preview
- Detected format, byte count, target address and CRC32, shown before you commit to flashing anything.
Turn on “Show raw protocol bytes”. It is off by default, which keeps the log readable as a narrative — but with it on, every stage grows a TX/RX frame row carrying the exact request and response hex, truncated to 16 bytes with the full length appended. That is the view worth keeping open the first few times you step through a run.
Manual mode is the default, and that is the point. On a real bench a flash run is thirty seconds of unreadable scrolling. Here the wizard arms one stage, waits, and shows you what it is about to send before it sends it — which is the only way to actually watch an erase turn a region from valid to erased, or a verify promote it back.
Recovery & Abort
Abort stops after the current command, never in the middle of one, then drops the session back to DEFAULT. Returning to DEFAULT clears the download state, the active transfer, security access and the staged flash driver in one move.
Aborting mid-run leaves the ECU in bootloader mode, and it should. If the target had already been erased, its programmingState stays erased or invalid — so the next 11 01 boots the bootloader rather than an application that is no longer there. The simulator does not paper over this. It is the exact behaviour that makes a bootloader worth having.
Recovery is simply a fresh run. There is no resume-from-block, and none is needed: every run starts from the session ladder and re-erases before transferring, so re-flashing is idempotent. Restart after a failure and the second attempt is byte-for-byte the first one.
Firmware Images
The catalog images are sized to fill their target partition exactly, so the erase, download and verify records are correct by construction. You can also supply your own file.
| Format | Extensions | Carries a load address |
|---|---|---|
| Intel HEX | .hex, .ihex | Yes — from the record set |
| Motorola S-record | .s19, .s28, .s37, .srec, .mot | Yes — from the record set |
| Raw binary | .bin | No — pick the target partition yourself |
| Pasted hex | — | No — pick the target partition yourself |
Uploads are capped at 1 MB. Address-less input reveals a target-partition picker, because a raw byte array says nothing about where it belongs. Before anything is flashed, a preview reports the detected format, byte count, resolved target address and CRC32.
Only partitioned regions are flashable. Calibration regions define no flash partition, so a calibration-addressed image is rejected at parse time rather than routed into the sequence — an erase against a partitionless region would take NRC 0x31 anyway. Calibration data is written the free-form way, with SID 0x3D WriteMemoryByAddress.
Services in the Ladder
Every service the reprogramming sequence touches, in the order it first appears.
Reading the other direction: SID 0x35 RequestUpload is the mirror image of this whole page — same 0x36/0x37 chain, opposite direction, and no erase.
ISO 14229-1 References
Standard: ISO 14229-1:2020. Session control §9.2 · ECU reset §9.3 · Security access §9.4 · Routine control §13.2 · RequestDownload §14.2 · TransferData §14.4 · RequestTransferExit §14.5.
Key points
- ISO defines no “flash” service — reprogramming is a fixed composition of eight existing services
- addressAndLengthFormatIdentifier: high nibble = memorySize length, low nibble = memoryAddress length
- maxNumberOfBlockLength includes the 0x36 SID and the block-sequence-counter byte
- NRC 0x78 means “still working” and refreshes the P2* deadline — it never fails a run
- ISO reserves RIDs 0xF200–0xFFFF for the manufacturer; only 0x0202 and 0x0203 are standard programming routines
- PROGRAMMING is reachable only from EXTENDED, and every session change clears security access