PostHeaderIcon ATmega1284PV: The Identity Stored Outside the Flash Array

A file that reads back cleanly is a file, not a device. The distinction becomes concrete the first time a recovered unit is put into a system alongside the original and the two of them start answering to the same address — or the first time a warranty claim is examined and the hour meter reads zero on a machine that has been running for four years.

Those values are almost never in the program. They are in the EEPROM, they are different on every unit ever built, and they are the reason a copy of the flash is not a copy of the product.

The kinds of data that live there

On a part with two kilobytes of byte-writable non-volatile memory, a production line will use it. The patterns repeat across industries:

Data Size Written when Consequence if missing
Serial number 4–8 bytes End of line Product cannot be registered or serviced
Calibration constants 8–64 bytes Test fixture Measurement accuracy is wrong
Network or bus address 1–2 bytes Commissioning Address collision on the bus
Run-hour or cycle counter 4 bytes Continuously Service intervals wrong, fraud detection
Feature or option flags 1–4 bytes Order entry Unlocked features locked, or the reverse
Last-known setpoints 8–32 bytes Every change Device starts with defaults

Any one of those missing turns a working clone into a support case. All of them together are what the customer actually needs, and they cost one extra read command to obtain.

Reading it is the easy part

The EEPROM array is addressed through the same programming interface as the flash, with a different command. It reads in seconds, it is not subject to page-buffer raggedness in the same way, and its contents are stable because it is written rarely.

The parts that are not easy:

  • The data may be encoded. A serial number stored as ASCII is obvious. The same value stored as a BCD field with a checksum byte, inside a structure with a magic word, needs the firmware to decode — which means finding the routine that reads it.
  • The array may be wear-levelled. Counters that increment frequently are sometimes rotated across slots to spread the erase cycles. What looks like noise is a log with a position pointer.
  • Some of it is not per-unit. Calibration constants are often identical across a production run. Comparing two units separates the constant block from the unique block, and that saves the effort of decoding the constants at all.

Telling the two blocks apart

Read the EEPROM from two units of the same product. Byte-by-byte comparison gives three regions: identical, similar with small differences, and completely different.

Identical regions are calibration or defaults, and they can be copied from either unit. Small differences with a pattern are counters or addresses. Completely different regions are serial numbers or cryptographic material.

That comparison needs two working units, which is not always available. When it is not, the fallback is to find the EEPROM read routine in the disassembly and see which addresses it treats as a structure — field offsets and lengths fall out of the load instructions, and the surrounding code usually reveals the width of each field by the register it lands in.

The address collision problem

On any shared bus, a duplicated address is worse than a missing unit. Two devices answering to the same Modbus slave address, two nodes with the same CAN identifier, two instruments on the same RS-485 segment: the bus does not degrade gracefully, it produces corrupted frames that look like a wiring fault.

If the identity is copied along with the flash, the clone must be re-addressed before it is connected. That step is easy to forget and it is the most common cause of a “the new unit broke the whole network” report.

The same applies to serial numbers in any system that enforces uniqueness — licence servers, cloud registration, warranty databases. A duplicated serial does not fail at commissioning; it fails months later, usually at the least convenient moment.

What to deliver, and in what order

A delivery that includes the EEPROM as a raw dump is more useful than one that does not, but a raw dump alone leaves the customer to guess. The practical format is three items:

  1. The flash image, with the read method and the verification described.
  2. The EEPROM image, plus a note on which bytes were unique to the unit and which were common across the product.
  3. A replacement plan for the unique fields — either a procedure to write new values, or the address at which the old ones sat so the customer’s own fixture can do it.

The third item is the one that turns a data delivery into something that can be deployed.

When the EEPROM is gone

A chip erase clears the EEPROM unless the save-on-erase fuse is programmed, which means a device that has been erased and reprocessed may have lost its identity permanently. In that case the only route is to derive values from the system the device belonged to: the label on the enclosure, the commissioning record, the service log.

That is worth stating plainly at the start of a project rather than at the end, because a customer who expects the serial number to be recoverable from an erased part has a mistaken model of how the device works, and the sooner that is corrected the less painful the conversation.

Related reading

FAQ

Is the EEPROM readable when the flash is locked? The lock bits cover both arrays, so a locked part blocks EEPROM reads through the programming interface as well.

Can EEPROM contents be inferred from the firmware? Structure yes, values no. The layout is recoverable; the serial number written at end of line is not.

Does a firmware update overwrite the EEPROM? Usually not, which is exactly why it accumulates production data over years.

What if only one unit exists? Then the unique block cannot be separated by comparison. Find the read routine, decode the structure, and treat anything unlabelled as unit-specific.

Conclusion

The program is the part everybody asks for and the part that is usually the same on every unit. The identity in the EEPROM is what makes a device a device, and a recovery that stops at the flash boundary has delivered half the product.

PostHeaderIcon ATmega128PA: The Compatibility Fuse That Rewrites the Vector Map

Most fuses change a detail: the clock source, the brown-out level, whether the watchdog starts enabled. One fuse on this part changes the machine. It exists because the ATmega128 replaced the ATmega103 in sockets that were already designed, and the transition needed to be survivable for products in production. The compatibility bit makes the newer part behave like the older one.

That is a reasonable engineering decision and a serious trap for anyone reading an image, because the same bytes decode into different meanings depending on a bit you might not have read yet.

What compatibility mode actually changes

In the compatibility setting the device presents the older part’s feature set rather than its own:

  • Fewer interrupt sources. The older device had a shorter vector table, so several sources do not exist in this mode and the remaining ones sit at different offsets.
  • One serial port. The second USART is not available.
  • Half the SRAM. The internal RAM visible to the program is reduced.
  • No TWI. The two-wire interface is not usable.
  • No JTAG. The debug and programming port that the newer part provides is not offered.

The memory maps, the interrupt numbering, and the peripheral register set all shift together. Nothing about the flash changes, which is exactly why the confusion is so easy to fall into: a full read succeeds, the file looks healthy, and the addresses inside it are being interpreted against the wrong map.

Feature Normal mode Compatibility mode
Vector table layout Full set, extended numbering Reduced set, older offsets
USART Two ports One port
SRAM available Full Reduced
TWI Available Not available
JTAG Available Not available
External memory Full width Older restrictions apply

Why this matters before you disassemble

Disassembly is the act of assuming a map. You tell the tool which part it is, the tool applies the vector offsets and the register addresses, and the output is only as good as that assumption. On this part there are two plausible assumptions and no automatic way to tell them apart from the binary alone.

The consequences are not subtle. A handler that you identify as the timer compare because of its table position is actually a different source in the other mode, so every conclusion about timing behaviour is attached to the wrong peripheral. Worse, register addresses in the extended I/O space decode to different names, so the code appears to configure things the part does not have.

The fix is one byte read from the fuse area before the first disassembly. It takes a second and it eliminates the entire class of error.

The behavioural tells

If the fuse cannot be read — a locked part, a dead interface — the mode can still be identified from behaviour, and the evidence is strong.

JTAG does not respond at all. In compatibility mode the interface is absent rather than disabled. A part that ignores every JTAG attempt while ISP works normally is a candidate.

The second serial port is silent no matter what you do. Not uninitialised, not misconfigured, simply not present.

A program written for the full feature set behaves strangely. If the firmware was built for normal mode and the fuse is wrong, initialisation of the second USART and the TWI block writes to addresses that do not respond, and the failure is at startup rather than at first use.

The SRAM test fails above a threshold. A memory test that fills all of RAM and reads back a shorter length than expected is a signature of the reduced map.

Any two of those together is enough to act on. One alone is a hypothesis.

Where the fuse comes from

Two routes produce a device in this state. The first is legacy: a product designed around the older part, where the compatibility setting was chosen deliberately to reuse firmware and factory fixtures. The second is accident: a production programmer with a default fuse value that was never checked, applied to a part that was supposed to run in native mode.

The second route is more common than people expect, and it produces a device that half works — which is why the unit arrives at a bench described as “intermittent” rather than “dead”.

Recovering and re-deploying

For the recovery itself the mode does not change the extraction: the array is read the same way. What it changes is the analysis, and the re-deployment.

If the firmware was written for compatibility mode, the replacement part must be configured the same way, or the recovered image will not run — not because it is damaged, but because its interrupt offsets and peripheral addresses assume the older map. Writing the same fuse value is part of the delivery, and it should be recorded alongside the hex file rather than left to whoever programs the replacement.

If the firmware was written for native mode and the device was mis-configured, then the correct action is to clear the fuse and accept that the product may have been running degraded for years — some of its features never worked, and the customer’s expectations were formed around that broken behaviour. That conversation is worth having before the “fixed” device goes back into service.

Related reading

FAQ

Does compatibility mode affect the flash contents? No. The array is identical. Only the way the CPU and peripherals interpret it changes.

Can the fuse be changed on a locked part? Fuse access is separate from flash access on most AVR parts, but changing the fuse on a device whose image you have not yet extracted is a risk. Extract first, then change configuration.

Will a normal-mode image run in compatibility mode with small edits? Rarely. The vector offsets and peripheral addresses both move, and those are not patchable in a binary without rebuilding from source.

Is the mode visible in the device signature? No. The signature identifies the part, not its configuration. The fuse byte is the only direct answer.

Conclusion

A single configuration bit on this part swaps the entire peripheral and interrupt map. Read it before disassembling, record it in the delivery, and program it onto the replacement — because a recovered image that is correct and running against the wrong map looks exactly like a bad recovery.

PostHeaderIcon ATmega1284P: The Vector Table as a Table of Contents

Reverse engineering an unknown 128 KB image is mostly a problem of attention. There is too much of it, most of it is library code, and the part that matters is a few kilobytes scattered across the map. The map exists, it is at address zero, and it is the only part of the image whose layout is fixed by the silicon rather than by the compiler.

Every AVR interrupt vector has a defined position and a defined meaning. On a part this size each entry is a four-byte jump, so the table is short and every slot in it can be named without guessing.

Reading the table by hand

The first entry is the reset vector, a jump to the startup block. Everything after it corresponds to an interrupt source in the order the datasheet lists them. Write the addresses down against the source names and you have a list of the firmware’s entry points, sorted by the hardware that triggers them.

Table slot Source Empty Single handler Rich handler
0 RESET impossible jump to startup jump to startup
1–3 External interrupts jump to stub short routine pin-change logic
4–7 Pin change groups often empty one port handled several ports
8–10 Timer 2 empty if unused overflow tick compare plus overflow
11–15 Timer 1 empty if unused timing base capture plus two compares
16–18 Timer 0 usually used system tick PWM update
19–21 USART 1 empty if unused RX only RX, TX, and data register empty
22–25 ADC, EEPROM, comparator mixed conversion complete sequenced sampling
26–28 TWI empty if unused address match full state machine
29–32 Timer 3 empty if unused one compare three compares used
33–35 USART 2 empty if unused RX only full duplex

An empty slot is not nothing. It is a jump to a common stub, and every empty slot jumps to the same place. Counting how many slots point at that address gives you the number of unused interrupts in one arithmetic step, which is a fast way to see the shape of the firmware before reading any handler.

What the distribution tells you

Three patterns are worth recognising immediately.

A dense Timer 1 block with two compare channels and a capture. That is a motor controller, a servo driver, or an input capture application. The capture channel in particular is a signature: it means the firmware measures an external pulse width, which means a sensor or a tachometer is attached.

A busy USART with the data-register-empty interrupt enabled. The transmit interrupt is only useful when the firmware queues output rather than blocking on it, which implies a protocol with back-to-back frames and a state machine to feed it.

An ADC interrupt with a sequence. Single conversions are usually polled. A conversion-complete interrupt with a channel index incremented in the handler means the firmware samples several channels on a schedule, which is a data logger or a controller with feedback.

None of these conclusions requires understanding a single instruction. They come from the shape of the table plus the size of each handler, which is simply the distance to the next entry point.

Measuring handler size from the table

Because each entry is a four-byte jump into the code section, the handler addresses are not in table order. Collect them, sort them, and the gaps between consecutive addresses approximate handler sizes. A gap of thirty bytes is a flag setter. A gap of six hundred bytes is a protocol engine.

That approximation is good enough to prioritise. Read the three largest handlers first; they contain the application’s actual behaviour. Read the smallest last, because they are usually one-line acknowledgements.

The bootloader question

Two things in the table answer it. First, whether the reset vector points into the boot section rather than to address zero of the application — which happens when the boot reset fuse is programmed. Second, whether there is code at all in the region the boot section occupies, which the fuse decides the size of.

If the reset vector lands near the top of the array, the device boots into a bootloader and the application only runs after the bootloader hands over. That changes the recovery plan, because the bootloader is a separate program with its own protocol and its own update path, and it often survives an application that does not.

Where the table misleads

Two failure modes are common enough to mention.

The compiler sometimes emits vectors for interrupts that the source never enables, because a library object was linked in. A handler that does nothing but return is one of these, and it looks identical to a handler for an unused source.

And on this part, as on others in the family, the table is fixed but the code behind it is not. A jump can point into a table of function pointers, which then points elsewhere again. When a handler’s first instruction is an indirect load followed by an indirect call, the useful entry point is the pointer table, not the vector slot.

Doing this before anything else

The order matters because everything downstream depends on it. Read fuses, read the array, then read the vector table and write down all thirty-five entries with the source names, the target addresses, and the distances. That one-page document is the outline of the reverse engineering job, and it takes less time than deciding where to start without it.

Related reading

FAQ

Are the vector positions the same across the 644 and 1284? Largely, because the peripheral set is shared. Confirm against the datasheet for the exact orderable code before relying on a position, since new revisions add sources.

What does an entry containing zero bytes mean? An empty slot jumps to a shared stub rather than containing nothing. A slot reading as all ones means the region is erased, which usually means the image read is incomplete.

Can the vector table be used to date the firmware? Indirectly. The set of implemented sources plus the library startup signature narrows the toolchain version, and the startup block often contains a version string that the application prints.

Why are handler addresses not in table order? The linker places functions by size and alignment, not by vector order. Sorting the addresses is what turns the table into a size estimate.

Conclusion

Two hundred bytes of fixed layout give you a prioritised reading list for 128 KB of unknown code. It is the cheapest piece of analysis in the whole job, and skipping it is why so many reverse engineering efforts start in the middle of a library routine.

PostHeaderIcon ATmega644PV: Charge Loss After Ten Years in the Field

Almost every recovery guide is written for a device that was working last week. The realistic case is often different: an industrial controller that ran continuously for eleven years, a pump monitor that sat in a cabinet, a door controller that has been powered since installation. Those parts are not damaged. They are old, and old flash behaves differently from new flash in ways that look exactly like a broken programmer.

The distinction matters because the two failure modes call for opposite responses. A broken link tells you to slow down, shorten cables, and check grounds. A worn array tells you that some cells are marginal and that the answer is to read more carefully rather than to read harder.

What actually degrades

Flash stores a bit as charge on a floating gate. The charge leaks, slowly, through the oxide and along the edges of the cell, and the rate depends on temperature, on how much the cell has been written, and on how the part was screened. The datasheet retention figure is twenty years at 85 °C for a cell that has been through relatively few cycles; a cell that has been erased and rewritten tens of thousands of times has far less margin left.

Nothing about this is visible from the outside. The part still reads, the signature is still correct, the programmer still reports success. What changes is the distance between the two threshold windows, and once that distance shrinks, the outcome of a read becomes a matter of timing, supply voltage, and temperature rather than a matter of the stored value.

Symptom Worn array Bad link
Errors Isolated single bits, scattered Whole bytes or fields, in bursts
Repeatability Changes between passes Changes between cable movements
Temperature dependence Strong None
Effect of slower clock Small improvement Large improvement
Address pattern Random Often aligned to a page or a boundary

Reading the same address twice is not verification

The instinct is to read twice and compare, and that catches a transfer fault. It does not catch a marginal cell, because a marginal cell often reads the same value on both passes at the same temperature and the same supply. The error is stable within a session and unstable across conditions.

The useful test moves one variable at a time and holds the other two fixed:

  1. Read the array cold. Ten minutes in a freezer bag with a desiccant sachet is enough for a small package, and it is the single most informative test available, because charge loss is worst when the cell is cold and the read reference has moved.
  2. Read the array warm. A warm cabinet or a heat pad at 60 °C is enough. Compare the map of differing bits between the two runs.
  3. Read at the two supply extremes the part is rated for, since the sense amplifier’s margin depends on the rail.

If the same addresses disagree between cold and warm, those addresses are marginal and the array is worn. If the disagreement moves around at random, the problem is the link.

Voting instead of averaging

Once marginal cells are identified, the extraction strategy changes from “read it right” to “read it many times and decide”. A single read is a sample. Fifteen reads at three temperatures, with a majority vote per bit, recovers a value that a single pass cannot, and it does so without touching the part.

The cost is time, and the constraint is that the part must not be allowed to reset mid-session, because each reset costs a re-entry into the programming mode and a fresh synchronisation. On a 64 KB array with a slow clock this is measured in minutes, which is affordable. On a part wired into a board where the application keeps grabbing the reset line, it is not, and the part has to come off.

The traps that produce fake errors

Three mistakes account for most of the false positives in this work.

Reading without fixing the clock. The ISP clock is derived from the programmer, but the internal oscillator still determines part of the timing on some operations. A part whose RC oscillator has drifted does not fail, it just reads more slowly, and a programmer that times out reports an error where none exists.

Ignoring the write that never finished. A device that lost power during a firmware update has one page that is half programmed. Those cells are in an intermediate state and will read inconsistently forever. This is not wear, it is an interrupted operation, and it looks identical unless you check the date code and the customer’s story.

Trusting a checksum. A CRC over the whole image matches when the two reads agree, and the two reads agree when both are wrong in the same way. Checksums detect transport errors, not marginal cells.

What the recovered image looks like

A worn array produces an image with a small number of bits that cannot be resolved by voting, typically tens out of half a million. Those bits land inside code, inside tables, and occasionally inside a constant that matters.

The sensible way to present the result is honest about it: a hex file, a list of the addresses that did not converge, and a note on how each was decided — by voting, by comparison with a second unit, or by inference from the surrounding instructions. A single flipped bit inside an AVR instruction is usually obvious, because the opcode space is sparse and an invalid opcode stands out. A single flipped bit inside a lookup table is not obvious, and claiming otherwise is how a recovery project turns into a debugging project for the customer.

Related reading

FAQ

Can a worn part be repaired? The array can be rewritten with the recovered image, which refreshes the charge and restores margin. That fixes the part and destroys the evidence, so it should only happen after the image is final.

Is retention loss the same as endurance failure? Related but not identical. Endurance is about how many write cycles a cell survives. Retention is about how long it holds a value afterwards. An old device with few writes can still have poor retention.

Does the part report anything about its own health? No. There is no status register that reports margin. The only health measurement available is behavioural, which is what the temperature test provides.

How many reads are enough? Fifteen across three temperatures recovers almost everything that is recoverable. Beyond that the returns are in single bits, and the time is better spent comparing against a second unit.

Conclusion

An eleven-year-old part is not a broken part, and treating it as one leads to destructive decisions. Measure the failure first: if the errors move with temperature, the array is worn, and the answer is many careful reads and an honest list of what did not converge.

PostHeaderIcon ATmega644P: The Second USART Is a Way In

A 40-pin AVR with 64 KB of flash usually has one serial port, and by the time a product ships that port is doing something important — a display, a host protocol, a radio module. Anything you want to say to the device has to share the wire with traffic that was designed without you in mind, which is why so much AVR work ends up on the programming header instead of the application interface.

The ATmega644P is different in a way that is easy to overlook when reading a datasheet and impossible to ignore once you are holding a board. It has two independent USARTs. The second one is frequently initialised in the vendor’s firmware even when the final product does not use it, because it existed during development, or because a factory fixture used it, or because the same code base serves several products and only one of them connects the pins.

What a second port buys you

Two serial channels means the application keeps its own conversation and a second conversation becomes available. If the second USART is brought out to a pair of pads, a header, or a test point, then read and write access to the running firmware exists without touching the ISP pins at all.

Situation One USART Two USARTs
Application port in use by a host No free channel Second channel available if brought out
Bootloader waits on UART0 No free channel Wrong port for you, but the first is now documented
Factory test pads left on the board Not usable unless the application idles Usually still listening for a fixture command
Debug printf compiled out of release Gone Sometimes still present on the unused port

The practical benefit is not that a second port is faster. It is that a second port is often idle, and an idle port does not fight you for timing.

Finding out whether the second one is alive

The measurement is simple and does not require the firmware image. Power the board normally, attach a serial adapter to the second port’s pins at the supply’s logic level, and listen. Then send a newline, a couple of 0x55 bytes, and a question mark. Devices that were built with a fixture protocol frequently answer something, even if the answer is an error message, and an error message is a signature that identifies the protocol.

Do this at the baud rate the clock actually produces, not the one on the schematic notes. A part running from its internal 8 MHz oscillator divided by eight is at 1 MHz, and a nominal 9600 baud setting will be wrong by whatever error the divider introduces. Sweeping the common rates at each plausible clock is a small, bounded search: it either produces readable text or it does not.

Two rules keep this from wasting a day. First, do not assume the pin order — swap TX and RX once before concluding anything. Second, if the port is shared with another function through a multiplexer or a jumper, find the selector before you decide the port is dead.

What to do with a channel once you have one

An open channel changes the shape of the job. Instead of extracting an image and then guessing what it does, you can ask the running firmware questions.

  • Read the device signature and the fuse bytes through bootloader commands, which confirms what the part actually is rather than what the silkscreen claims.
  • Request a page of flash and compare it against a second unit, which separates shared code from per-unit data.
  • Trigger the vendor’s own self-test, which often exercises the peripherals and reports which are populated.

None of that requires the lock bits to be cleared, which is the point. A bootloader that was written to accept commands over a UART rarely checks whether the flash is locked, because the lock bits exist to stop reads through the programming interface, not to stop the bootloader from doing its own job.

When the port is not there

Boards that never brought the second USART out still sometimes leave the two pins configured as a USART with the transmitter enabled. That is measurable: the TX pin idles high with a faint periodic activity if the firmware sends anything at all, and it reads as a push-pull output rather than a floating input. A scope on the pin answers the question in ten seconds.

If the pins are used for something else, they will be switching with the application’s rhythm, and the idea is closed. That is a normal outcome and worth recording, because it tells you the vendor was not leaving a door open and the work has to go through the programming interface or through the array itself.

The part-specific detail that matters during extraction

The 644P organises flash into 256-byte pages and supports read-while-write, which means the boot section can be programming the application section while the CPU executes from the boot section. That is useful to the firmware and unhelpful to a reader: a dump taken while a page write is in progress can return a page that is half old and half new.

The defence is not clever, it is procedural. Read the whole array, then read it again, and compare. A page that differs between two consecutive passes is a page that was being written; it is not a corrupted chip and it is not a reason to reach for the erase command.

Related reading

FAQ

Does a second USART mean the flash is readable? No. It means a command channel may exist. Whether the firmware will hand over memory through that channel depends on what the bootloader was written to accept.

Can the lock bits be cleared over a serial bootloader? Some bootloaders implement a chip erase command, which clears the lock bits and the program together. That is destruction, not recovery, and it should never be issued before the image has been secured.

Why would a vendor initialise a port the product does not use? Shared code bases, factory fixtures, and leftover debug paths. In practice all three are common, and all three leave the port listening.

Is the second USART present on every 644 variant? It is present across the 644 family. Confirm against the specific orderable code before relying on pin positions, because package options differ.

Conclusion

The second USART on this part is not a feature you exploit so much as a door that vendors leave unlocked. Look for it before committing to a destructive route: two pins, a level shifter, and a baud sweep either open a conversation or close the question in an afternoon.

PostHeaderIcon PIC18F4620: A Word-Addressed Machine and Its Config Words

The PIC18 architecture does not think in bytes. Its program memory is an array of instruction words, the program counter counts words, and a jump target is a word address. That is different enough from a byte-addressed machine that habits carried over produce consistent, plausible errors.

The other difference is architectural: the protection and oscillator settings live in configuration words at addresses far outside the program range, so a dump of the program is a dump of the program only.

Word addressing, and what it breaks

Three consequences:

A stored byte address is not a program address. Converting between them is a shift, and code that stores addresses for a table lookup often stores the byte form.

Instructions vary in length. Most are one word; calls, jumps and table operations are two. A disassembler has to know which, and a reader assuming uniform width will drift.

A dump’s length in bytes is twice its length in instructions. An eight-kilobyte image is four thousand instructions, which changes any estimate of how much code there is.

Property Value on this part
Instruction width 16 bits, some 32
Address unit one instruction word
Table access dedicated pointer, byte-oriented
Configuration separate address range

Table access is where the two views meet

Reading constant data from program memory uses a table pointer that addresses bytes while the program counter addresses words. That mismatch is deliberate — tables hold bytes — and it is the source of the most common off-by-one in PIC18 analysis.

A table lookup therefore looks like: set the pointer to a byte address, execute the read, consume one byte. A reader who treats the pointer as a program address will land at half the intended location and find data that looks like instructions.

The configuration words

The oscillator selection, the watchdog, the code protection bits and the brown-out settings are held in configuration words outside the program array. They are not in a hex dump of the program, and they are read through their own mechanism.

This is the same structural point that applies to every architecture: protection is configuration, and configuration is elsewhere. A recovery that omits the configuration words produces an image that cannot be made to run, because the oscillator setting alone decides whether the part starts at all.

What protection looks like here

Code protection is a set of bits in a configuration word, and its enforcement happens at the programming interface, not at execution. A protected part runs normally and refuses to be read.

The important practical consequence is the same one as everywhere: the operation that clears the protection bits also erases the program memory. There is no sequence that unlocks a part and leaves its contents intact, and any description that suggests otherwise is describing a different mechanism.

Reading order for a PIC18 device

  1. Read the device identifier and the configuration words. Record both before anything else.
  2. Read the program array, verifying each region twice.
  3. Establish the reset vector and the interrupt vector targets.
  4. Disassemble with word addressing, checking instruction width at every branch.
  5. Locate table accesses and convert their pointers from bytes to words for interpretation.

Step 5 is where the two address views are reconciled, and getting it wrong produces a reconstruction that is internally consistent and externally wrong.

Where this differs from the AVR habit

An analyst moving between architectures carries assumptions. Two of them fail here: that addresses are bytes, and that configuration lives somewhere in the image. Neither is true, and both errors are quiet.

The defence is procedural rather than intellectual: read the identifier and configuration first, write down the address unit, and never assume a width without checking the opcode.

Related notes

FAQ

Are PIC18 instructions always two bytes? Most are. A few are four, and those are the ones that break a naive disassembly.

Where are the configuration words? Outside the program address range, read through a dedicated operation.

Can the program be read if the part is protected? No, and the configuration that blocks it is the same one that has to be cleared to read it.

Why does a table pointer use bytes on a word machine? Because tables contain bytes, and the hardware provides both views.

Conclusion

A word-addressed machine punishes byte-oriented habits in a way that looks like a data problem. Reading the configuration words first, writing down the address unit, and checking instruction width at every branch are the three habits that keep a PIC18 analysis from drifting — and they cost less than a single wrong reconstruction.

PostHeaderIcon ATmega644PA: When RSTDISBL Removes Your Way In

A device that ignores the programmer is usually diagnosed as dead or locked. Both conclusions are premature. The most common cause on an AVR is a fuse that removed the pin the programmer needs, and the chip is behaving exactly as configured.

The fuse is RSTDISBL, reset disable. When it is programmed, the reset pin stops being a reset pin and becomes a general-purpose I/O. The serial programming interface enters the chip through that pin, holding it low to select programming mode. With the pin reassigned, there is nothing to hold and no mode to enter.

Why a designer would do that

Because pins are scarce. A 40-pin part with a dozen peripherals competing for I/O will trade a reset line for one more usable pin, especially in a product that never needs field reflashing. It is a legitimate, common optimisation — and it is invisible from the outside until someone tries to program the part.

The same logic applies to two neighbouring fuses. The debug-wire enable fuse assigns the reset pin to the on-chip debug interface, which also excludes ISP. The self-programming enable fuse, in the opposite direction, allows the boot section to write flash — which is a different question, but shows up in the same fuse byte.

Fuse What it does Effect on serial programming
RSTDISBL programmed Reset pin becomes I/O ISP cannot be entered
DWEN enabled Reset pin owned by debug interface ISP cannot be entered
SPIEN unprogrammed Serial programming disabled ISP cannot be entered
EESAVE programmed EEPROM preserved through chip erase no effect on entry

Three different fuses, one identical symptom: the programmer reports no device. Separating them requires reading the fuse bytes, and reading the fuse bytes requires an interface — which is the circular problem the fuse created.

The way back in

There is one: high-voltage parallel programming. That mode does not use the reset pin as a selection line in the same way; it applies twelve volts to the reset pin and drives the part through a parallel command interface. It works regardless of RSTDISBL, which is precisely why the fuse is called “disable reset” and not “disable access”.

So a part with RSTDISBL set is not closed. It is closed to serial access. The parallel interface still reads the signature, still reads the fuses, still reads the lock byte — and still respects the lock bits, which is the important caveat.

Two practical consequences:

  • Recovering the part means a different adapter, a different socket, and a twelve-volt supply. The procedure is documented and the hardware exists, but it is a distinct workflow, not a setting change.
  • The parallel interface does not bypass protection. If the lock bits are in mode 2 or 3, entering in parallel mode still does not permit a flash read. It permits a chip erase, which clears the locks and the contents together.

That combination — RSTDISBL plus locked flash — is the genuinely hard case, and it is the configuration a careful designer would choose deliberately.

Why “no device” is not a diagnosis

A programmer that cannot enter programming mode reports the same error for four different conditions: no power, wrong pinout, RSTDISBL or DWEN, and SPIEN disabled. The error message does not distinguish them, and the natural next step — assume the part is dead — closes the investigation too early.

The distinguishing test is voltage. Measure the supply at the socket, then measure the reset pin while the programmer asserts it. A part in ISP mode holds that pin near ground when the programmer drives it; if the pin does not follow, either the fuse reassigned it or the die is not responding. That single measurement separates a configuration problem from a hardware failure, and it takes less time than a retry.

Reading a device that will only answer in parallel

Once in parallel mode, the working order is the same as anywhere else: signature, fuses, lock byte, then memory. The difference is that each of those is now a parallel command rather than a serial one, and the timing is different — the parallel interface is much faster, which is convenient for a full read and unforgiving of a badly made adapter.

A useful habit: read the fuse bytes twice, before and after any operation that might have changed them. Fuses do not change spontaneously, so a difference means the read was wrong, not that the part changed. That check catches marginal contact on the socket, which is by far the most common source of nonsense from a parallel adapter.

What the design choice tells you

A product with RSTDISBL set and the general lock bits clear is a product designed by someone who did not think about protection at all — they were optimising pin count. Those are the easy ones: parallel mode in, fuses read, memory read, done.

A product with RSTDISBL set and the lock bits in mode 2 is a different proposition. Someone thought about it. The configuration says: no serial access, no readback, and the only way to change anything is to erase the whole thing. Recovering anything from that part requires a route around the lock bits themselves, not around the fuse, and the fuse is a distraction from the real obstacle.

Knowing which of the two you are facing changes the estimate by an order of magnitude, and it is established by one fuse read.

Related notes

FAQ

Can RSTDISBL be undone over ISP? No. ISP is what the fuse disables. The fuse can only be changed through the parallel interface.

Does RSTDISBL erase the flash? No. It changes a pin’s function. The program memory is untouched and keeps running.

Is a part with RSTDISBL set unusable? No. It is unusable over serial. The parallel interface still works and still reads the fuse and lock bytes.

Can the parallel interface read locked flash? No. It respects the lock bits exactly as the serial interface does. It can always erase, and erasing clears the lock and the contents together.

Conclusion

RSTDISBL is a pin-count decision that reads as a hardware failure. It removes serial access without touching the memory, and it is diagnosed by one voltage measurement and one fuse read rather than by retrying the programmer. The important question is not whether the fuse is set, but what the lock byte says next to it — because that determines whether the part is merely inconvenient or genuinely closed.

PostHeaderIcon ATmega644A: What the A Revision Actually Changes

Part numbers that differ by one letter usually differ by more than one letter internally. An A suffix on a megaAVR marks a die revision: same pinout, same instruction set, same memory sizes, different silicon and a different errata list. For anyone examining a device, the interesting question is not what stayed the same but which of the differences can be observed from outside.

The answer is a short list, and every item on it is measurable without opening anything.

The signature byte is the observable difference

Tools identify a part by reading three signature bytes through the programming interface. The first two identify vendor and family and do not change across revisions. The third identifies the model, and it is where a revision can diverge.

When it does diverge, the consequence is procedural rather than technical. A programmer with a fixed device table refuses to talk to a part it does not recognise, and reports an error that sounds like a hardware fault. The part is fine; the table is stale.

The way around it is to stop using the tool’s identification step as a gate. Issue the signature read command directly, print the three bytes, and select the programming algorithm by family rather than by the tool’s model match. Every megaAVR in a given flash-size class uses the same algorithm, so the family-level selection is correct even when the model match fails.

Errata are where the revisions actually differ

Silicon revisions exist to fix things. A published errata sheet lists behaviours that deviate from the datasheet, and the list shrinks with each revision. Three categories recur across the megaAVR line:

  • Analogue deviations. Brown-out thresholds that trip slightly outside the specified window, or an internal reference that drifts with temperature more than specified.
  • Timing deviations. A peripheral that needs an extra cycle in a corner case, or a start-up delay that is longer than documented.
  • Interface deviations. A programming command that behaves differently when issued at the edge of the specified clock range.

The third category is the one that lands on the bench. A revision that changed how the part responds to a fast programming clock will fail on a programmer tuned for the earlier die, and pass on a slower one. That is a testable hypothesis and worth testing before assuming a part is damaged.

What does not change, and why that is the useful part

The lock bit mechanism, the fuse map, and the page organisation of the flash are stable across revisions. That stability is what makes a family-level method work: the sequence signature, fuses, lock byte, then memory does not need to be rewritten for each revision.

Item Changes with revision Same across revisions
Device signature, third byte sometimes —
Instruction set — yes
Lock bit state machine — yes
Fuse map — yes
Page size and buffer — yes
Errata behaviour yes —

The practical reading of that table is: the procedure is portable, the timing and thresholds are not. Plan the method once; calibrate the electrical parameters per die.

Revision and the identity question

Devices get redesigned without a part number change far more often than with one. A revision suffix is a gift, because it tells you the vendor changed something and documented it. The absence of a suffix does not tell you the opposite.

When a customer supplies units that behave differently from each other, the first check is the signature byte, then the date code. Two units with the same signature and different date codes are the same design at different points in the production lifetime; two units with different signature bytes are different dies and may legitimately behave differently.

That distinction changes the answer to a common question — “is this a counterfeit?” A different revision is not a counterfeit. A different signature on a part sold as a specific revision is.

Reading a device whose revision you cannot confirm

The order of operations is unchanged: signature, fuses, lock byte, then memory. What changes is the interpretation of anomalies.

If a read fails at a fast clock and succeeds at a slow one, suspect revision-specific timing before suspecting damage. If a brown-out test trips earlier than the datasheet suggests, suspect the errata list before suspecting the supply. If an erase completes but the lock byte does not read back as expected, suspect the command sequence before suspecting the die.

Each of those is a hypothesis that costs one retest to falsify, and retesting is cheaper than the alternatives.

Related notes

FAQ

Is an A revision a different chip? It is a different die implementing the same specification, with corrections. Pinout, instruction set and memory map are unchanged.

Do I need a different socket? No. The package and pinout are the same, so the same adapter fits.

Can a revision change the lock bit behaviour? The state machine is stable across the family. What can differ is timing at the edges of the specified range.

Does a different signature byte mean the part is fake? Not by itself. It means the die is not the one the tool expected. Check the errata and the date code before drawing a conclusion about authenticity.

Conclusion

The A suffix is a documented die change, and its observable footprint is narrow: a signature byte that may differ, an errata list that shrinks, and analogue or timing behaviour that shifts slightly. The recovery procedure does not change with it. The electrical calibration does, which is why measuring beats assuming on every revision.

PostHeaderIcon ATmega644: Page Buffers, RWW, and Why Dumps Come Out Ragged

Flash memory does not accept single bytes. It is erased in blocks and written in pages, and between the two sits a temporary buffer that holds the page while it is being assembled. Every AVR in this family works this way, and the arrangement leaves fingerprints in real device dumps that are routinely mistaken for corruption.

Knowing the mechanism makes those fingerprints readable. It also explains a class of failure that has nothing to do with protection: a device that was interrupted mid-update, and still runs, but from a program that is no longer what the vendor shipped.

The write sequence, in order

A page write on this family is a four-step procedure executed by SPM from the boot section:

  1. Erase the page. The whole page becomes FF.
  2. Fill the page buffer, one word at a time, by executing SPM with the buffered-write bit set.
  3. Commit: execute SPM with the write-page bit set, which transfers the buffer to the array.
  4. Wait for the operation to complete, polling or by interrupt.

Two properties follow from that order. The buffer is volatile — if power is lost before step 3, the buffer contents are gone and the page stays blank. And the erase in step 1 precedes the fill, so a commit that happens with a partially filled buffer writes a page that is part real data and part FF.

That second case is exactly what an interrupted firmware update produces. It is not corruption in the sense of damaged cells; it is a page that was committed before it was fully assembled.

Why half the flash is read-while-write and half is not

The array is split into two sections. The read-while-write section can be read by the CPU while a page in it is being written. The no-read-while-write section cannot — while a page there is being erased or written, the CPU stalls.

Section During a page write Typical contents
RWW CPU keeps executing from it Application code
NRWW CPU halted until the operation finishes Bootloader, interrupt vectors

The split exists so that a bootloader living in the NRWW section can service the interface while it writes the application — but the bootloader itself cannot run from the section it is writing. Anyone who has watched an AVR update stall for tens of milliseconds per page has seen the NRWW boundary in action.

For a read attempt, the practical consequence is simpler: reading is a separate operation from writing, and a page write in progress does not block a read through the programming interface. There is no need to wait for an erase to finish before reading a different page, though it is polite to.

Where the ragged dumps come from

Three distinct patterns show up in real dumps and each has a different cause.

A page of FF in the middle of code. The page was erased and never committed, or committed empty. In a device that has been through a failed update, this is a normal artifact.

A page that is half code and half FF. The commit happened with a partially filled buffer. The boundary within the page tells you how many words were buffered before the interruption — which is, incidentally, a fairly precise indicator of how far the update got.

A single word that differs from its neighbours in a repeating pattern. Usually not flash at all, but a read taken at the wrong voltage or too fast, sampled at the point where the sense amplifiers are marginal. Reading the page again at a lower clock rate either reproduces it exactly or makes it vanish.

The third pattern is the one worth checking first, because it costs nothing to re-read and because acting on a misread image wastes far more time than the re-read.

Interrupts and the read-while-write trap

On a part running from flash, an interrupt vector fetch during an NRWW write stalls the core. Firmware that writes the application section from the boot section therefore has to either disable interrupts or accept the stall. Both are common, and both leave a trace: a device that drops characters or misses a deadline during a field update is showing the stall.

This is not a recovery problem, but it is a diagnostic one. If a customer reports that a device “sometimes bricks during update”, the mechanism above is a better explanation than flash wear, and the fix is in the bootloader, not the memory.

Reading order that respects the mechanism

Because page boundaries are the unit of everything, a read is best planned page by page rather than as one long burst:

  • Read the page, then read it again. Two identical reads establish that the interface is stable; a difference means voltage or clock, not memory.
  • Record the number of non-FF words per page. Pages that are entirely FF are candidates for “never written” or “erased”; pages with a partial tail are candidates for “interrupted”.
  • Compare the pattern against the vector table. The first page holds the reset vector and the interrupt table; if it is FF, the device cannot be running the code you think it is.

That last check resolves a surprising number of contradictions. A part that appears to hold an application but power-cycles into the bootloader is usually a part whose application start is elsewhere in the address space, with the vector table redirected by a fuse rather than by the code.

Related notes

FAQ

Can a single byte be changed in flash? Not without erasing and rewriting its whole page. The buffer supports partial fill, but the array operation is per page.

Does a stalled CPU mean the flash is busy? It means the CPU is fetching from the NRWW section while that section is being written. The array is busy; the reading interface is not.

Is a page of FF a sign of damage? Usually not. It is a sign of an erased page that was never committed, which is what a normal erase without a following write leaves behind.

Can the page size be changed? No. It is fixed by the die and documented in the programming specification. It is the same for every member of the family with the same flash size.

Conclusion

Page granularity is the organising fact of AVR flash. Erase-then-fill-then-commit explains the partial pages, the FF blocks, and the update stalls, and the read-while-write split explains why a bootloader can update the application but not itself. Once those two mechanisms are clear, a ragged dump stops looking like damage and starts reading like a history.

PostHeaderIcon ATmega324PA: Brown-Out Detection and Reads That Look Corrupt

There is a specific kind of bad result that wastes more time than any other: the read that works. No error, no timeout, a full buffer of bytes, and every one of them wrong in a way that is not obvious until something downstream fails to assemble.

On this family the usual cause is electrical, not logical. The interface completed; the sense amplifiers did not resolve. And the block that exists to prevent exactly this — the brown-out detector — is usually disabled in the products that end up in this situation.

What the detector does

The brown-out detector monitors the supply and holds the part in reset while the supply is below a configured threshold. Its purpose is to stop the core from executing at a voltage where it cannot reliably fetch instructions.

It has two consequences that matter here:

It is configurable, and often configured off. Products that must operate down to a low rail turn it off, because a detector that resets the device at 2.7 volts is unhelpful in a device specified to run at 2.5. That is a legitimate choice, and it is also why the parts you receive may have no detector active at all.

Its threshold is a measurement. When the detector is enabled, the voltage at which the device drops out is an observable property of the die. If it trips noticeably higher or lower than the fuse setting suggests, that is information about the part, and it is often the first sign that the supply path is not what it appears to be.

Why a marginal read is worse than a failed one

A failed read is a clear signal: retry, change something, move on. A marginal read produces output, and the output goes into a workflow that assumes validity.

Consider what happens with a single bit that resolves wrongly. If it lands in a data table, the recovered product computes slightly wrong numbers. If it lands in an instruction, the disassembly produces an instruction that does not exist, and an analyst spends an hour deciding whether that is a deliberate obfuscation or a bug in the tool.

The defence is cheap and mechanical: read the same region twice and compare. Marginal reads rarely reproduce the same error twice, because the error depends on noise, temperature, and timing — all of which vary between passes.

Symptom Most likely cause First action
Identical garbage from two reads Genuine data, or a locked read returning a fill pattern Check the command response, not the buffer
Different garbage from two reads Marginal voltage or clock Lower SCK, raise supply, retry
Mostly sound with isolated wrong bytes Contact resistance on one pin Reseat, clean, retry
Clean read, wrong at the very end Supply sag during a long read Shorter passes, re-verify the tail

The interaction with the erase

Chip erase needs the charge pump. If the supply is marginal, the erase can complete from the interface’s point of view while leaving cells partially erased. The check that catches this is trivial and routinely skipped: after erasing, read the whole array and confirm it is uniformly FF.

Skipping it means a later programming step writes into a page that was not fully erased, and the resulting device behaves unpredictably. Worse, if the goal was to clear the lock bits and read the EEPROM, a partial erase means the EEPROM may be half-gone — which looks like the part never had data there.

Powering the part properly

Three details matter more than the nominal voltage.

The supply must be at the socket, not at the bench supply’s display. A cable, a clip, and a socket pin can drop a few hundred millivolts. Measuring at the supply output tells you nothing about what the die sees.

Decoupling belongs at the socket. The programming interface draws current in short bursts during page operations. Without local capacitance the supply dips at exactly the moment the part needs it most.

Ground return matters as much as the positive rail. A long ground lead shared with other equipment injects noise directly into the reference the sense amplifiers use. Short, dedicated ground returns fix problems that look like bad flash.

None of this is exotic. All of it is skipped routinely, and each skip shows up as a data-quality problem rather than a hardware problem, which is why it takes so long to find.

Establishing trust in a read

The sequence that reliably produces a trustworthy image:

  1. Power at the part’s design rail, measured at the socket.
  2. Read signature and fuses. Confirms wiring and interface enable.
  3. Read one page twice; require byte equality.
  4. Read the full array in modest passes, verifying each page as it arrives.
  5. Re-read the first and last page at the end and compare with step 4.

Step 5 is the one that catches drift over time — a part that warms up, a supply that sags, a clip that loses contact halfway. If the ends still match, the middle almost certainly does too, and the image can be treated as data rather than as a hypothesis.

Related notes

FAQ

Does enabling brown-out make a read more reliable? It makes the device safer to operate and it can catch a sagging supply, but the detector is not part of the programming interface’s path. Correct supply and clock are what fix a marginal read.

Can a locked part return a plausible-looking image? Yes, if the tool fills the buffer when the command is refused. That is why the command response, not the buffer contents, is the real answer.

Is it normal for two reads to differ by one byte? No. A single differing byte is a strong indicator that the margin is inadequate somewhere.

Does temperature matter? It shifts the oscillator and the sense amplifier behaviour. A read that works cold and fails warm is telling you the margin is thin.

Conclusion

Corrupt-looking output is a signal-processing problem before it is a memory problem. The brown-out detector, the decoupling, and the ground return all shape the margin the interface has to work with, and the two-pass comparison is the cheapest possible test of whether that margin is adequate. Establish trust in the electrical layer first; the protection layer becomes much easier to reason about once the data is real.