Recover IC LPC2102FBD48 Data Through the Boot Loader
A closed debug port does not mean a closed device. Every part in this family carries a factory boot loader in a reserved region of flash, and that loader speaks a plain ASCII command protocol over a UART. It exists so a board can be programmed on a production line without a debugger, and it is the first thing worth trying when the JTAG chain has refused to attach.
It works for architectural reasons. The loader runs before any application code, from a region the protection scheme does not cover, and it decides which services to offer by reading the control word in sector 0. When the answer is a refusal rather than silence, you have learned something precise about the device state, which is worth more than a failed attach.
How the loader gets control
Two conditions bring the boot loader up instead of the application. The first is the entry pin, held at the required level while reset is released; the loader then runs and waits for a command. The second is a validity check on the image itself. If the flash does not carry a correct vector checksum, the loader assumes there is no usable program and enters the same waiting state.
The second condition is often the more useful one, because on a production board the entry pin may be inaccessible — buried under a via, or driven by another device on the same net — while a flash fault will drop the part into the loader without any external help. Before reaching for a soldering iron, pull the reset line while watching the UART: a loader that announces itself is a loader you can talk to.
Synchronisation is not a formality
The first exchange is a single question mark. The device answers with a fixed string ending in OK, and in doing so it measures the timing of that character and locks its own baud rate generator to the measurement. This is auto-baud, and it is why the protocol works on a board whose crystal is unknown.
It is also why the protocol fails on a board whose clock is unusual. The loader derives its timing from the external oscillator and the divisor range is finite. A design running 3.6864 MHz, chosen so that standard rates divide evenly, will synchronise at a different question-mark timing than a design running 12 MHz, and a terminal configured for 9600 will sit there receiving nothing at all. Sending the question mark repeatedly with a short guard interval, starting from a deliberately awkward rate, is a more reliable habit than trusting a number from a table.
The command set, and what each verb costs
| Command | Function | Effect on the array |
|---|---|---|
| ? | synchronise and lock baud rate | none |
| J | report part identifier | none |
| K | report boot loader version | none |
| R | read memory at address for a count | none, refused under protection |
| M | compare memory against supplied bytes | none, refused under protection |
| P | prepare sectors for a write | none, marks sectors writable |
| E | erase selected sectors | destructive, whole array if sector 0 |
| C | copy a RAM buffer into flash | destructive, one page at a time |
| G | jump to an address and run | none, but the run has consequences |
The two harmless commands come first. The part identifier is a word that names density and die revision; the loader version word tells you which errata apply. Neither is blocked below CRP2, and together they confirm that you are talking to the device you think you are talking to, on the board you think you are connected to.
Reading then proceeds in blocks. Each request carries an address and a count as ASCII decimal fields, the device echoes the numeric fields back as zeros so both ends know the frame parsed, and the payload follows. Reading a small part like this one in full takes seconds; the limit is the frame buffer, not the link speed.
The return code that ends the argument
Every failed command returns a small integer instead of a zero, and the integers are specific enough to be diagnostic on their own.
Eight means the target sector is not blank, so an erase is required before a write. Nine means the sector was not prepared before the write, which is a sequencing mistake rather than a hardware fault. Thirteen means the address is malformed or outside the array. Nineteen means the loader understood the request, is capable of carrying it out, and is refusing because code read protection is engaged.
That nineteen is the most valuable response in the whole protocol. It converts an ambiguous situation into a diagnosis: the link is good, the loader is alive, the command syntax is correct, and the only thing in the way is a policy word. No amount of retrying changes it, and no variation of the command gets around it. It is time to stop working on the port and start working on the silicon.
What the loader can deliver on a protected part
Where the loader still answers reads, the recovery is straightforward and usually fast: read the whole array in blocks, read the part identifier, read the loader version, then verify by compare rather than by eye. Where the loader answers nineteen, the useful output is different but not worthless.
The part identifier and the loader version remain readable in most protected states. The behaviour of the device at reset remains observable. The command set tells you which protection state is engaged, because CRP1 refuses read and allows erase while CRP2 refuses even erase — the shape of the refusal is itself the state reading.
There is also a narrow but real middle case. Some products leave a service command in the application that reads flash and prints it, guarded by a password or triggered by a pin sequence. That is not part of the loader, but it is reachable through the same UART, and on a device whose silicon path is expensive it is worth five minutes of looking for.
Erasing is a decision, not a step
The loader will happily erase everything, and on a protected part that single command destroys exactly what the customer wants. The protocol makes this easy to do by accident, because the erase command takes a sector number and the number zero means the whole device rather than the first block.
Treat the erase command as a one-way switch that you only touch after the passive work is complete and the customer has agreed to the consequence. Before that point, everything in the sequence — sync, identify, attempt read, attempt compare — is non-destructive, and the responses are worth collecting in a log because they define what you can promise.
Where this fits in the wider family
The LPC2102FBD48 is the sixteen-kilobyte member of a range that shares the loader, the command grammar and the return codes all the way up to the half-megabyte parts. Procedures written here run unchanged on the larger devices; what changes is how long a full read takes and how much of the array can be recovered sector by sector.
For the branch of the family where the array is large enough for partial recovery to matter, see LPC2132FBD64 data recovery. For the mechanism that produces the return code nineteen in the first place, see LPC2101FBD48 protection states. And for a device where the same idea is applied to a different vendor’s protection scheme, AT91SAM7S256 recovery makes a useful comparison, because the ARM7 debug architecture is shared while the vendor’s policy is not.
A working sequence
Sync with the question mark, then identify the part and the loader version, then attempt a small read at the base of the array and record the exact return code. If that read succeeds, read the whole device in blocks, compare against a second pass, and only then discuss what the image contains. If it returns nineteen, stop and report the protection state with the evidence, because that answer determines whether the next step is a bench with a scope or a conversation about cost.
Everything before the first erase is reversible. Very little after it is.