ATmega640P: What a Complete Hex File Should Contain
A hex file and a binary file describe the same memory, and they are not interchangeable. The hex format carries addresses, record types, and a checksum per line; the binary carries bytes in order from a starting offset and nothing else. On a part with a large flash and a non-trivial address space, that difference decides whether you can tell “empty” from “not included”.
This matters most when an image is recovered and then handed to someone else. A file that looks complete, assembles without error, and silently omits a region is worse than a file that is obviously truncated.
The records that matter
The format used by essentially every AVR toolchain encodes four record kinds:
| Record | Purpose | Why it matters to a recovery |
|---|---|---|
| Data | Bytes at an address | The payload |
| End of file | Terminates | A missing one means truncation |
| Extended address | Sets the upper address bits | Required once the image passes 64 KB |
| Start address | Entry point hint | Usually zero on AVR; non-zero is a signal |
The extended address record is the one that trips people up on larger parts. Without it, addresses wrap at 64 KB, and a file assembled from a 128 KB read will contain a second copy of the first half written over itself. The result is a file that opens, disassembles, and is wrong in a way that only a checksum comparison reveals.
What is not in the hex at all
Three things live outside the address space the hex describes:
The fuse bytes. They are read and written through separate commands and they determine clock source, start-up delay, boot section size, and interface enable. A hex file says nothing about them, which is why a recovered image applied to a blank part with default fuses can fail to start at all.
The lock byte. Same category. It records protection state, not content.
The calibration byte. Stored in the signature area, read through a different address space, and not part of the file. Firmware that reads it at run time does not need it; firmware that hard-coded a value already carries it in the payload, usually invisibly.
A recovery that produces only a hex file has produced the payload and left three inputs behind. That is fine when the intent is analysis and a problem when the intent is reproduction.
Holes in the address space
A 64 KB part does not necessarily have 64 KB of program memory. Smaller members of a family occupy a subset of the same map, so an image read from a smaller die on a programmer configured for the larger one contains a large FF region that means “not implemented”, not “erased”.
Telling those apart requires the device signature. A signature read tells you which member you have, which tells you how much of the map is real. Reading without that step produces files with unexplained empty tails, and those tails get interpreted as erased code, missing firmware, or a failed read — three different wrong conclusions from one missing read.
The same logic applies in reverse: a full-map read of a small part is not wasteful, because the unimplemented region is a reliable marker once you know to expect it.
Verification that actually verifies
Reading a page and comparing it to itself proves the interface is stable. It does not prove the address was right. Two checks that do:
Read the vector table and disassemble the reset vector. On a part with a boot section, the reset vector either points into the application area or is redirected by a fuse. Either way, the first instruction is a jump, and the target must land inside a region that actually contains code. A jump into the empty half of the map is the signature of an offset error.
Read the same page at two different addresses in the tool. If the tool returns identical content for two addresses you know are different, the address latch is not working and every byte after the first page is in the wrong place.
Both are cheap, both catch errors that a self-comparison cannot.
Why the P revision affects none of this
The P suffix marks a picoPower die — lower current, revised oscillator, more brown-out levels. None of that changes the file format, the address map, or the relationship between the hex and the fuse bytes.
What it does change is the electrical margin during the read, and therefore the probability that a marginal read silently corrupts a page. On a picoPower part, read at the design rail, verify twice, and treat any difference between passes as a voltage problem rather than a memory problem.
Related notes
- ATmega640 and the external bus — the case where the file is genuinely incomplete.
- ATmega640A and revision differences — how a different die changes interpretation.
- ATmega644 and page buffers — the granularity behind every partial region.
FAQ
Is a hex file better than a binary? It carries addresses and a per-line checksum, which makes truncation and misalignment visible. For anything passed between people, yes.
Does the hex include the EEPROM? Only if the tool put it there, and usually at a separate address range. A hex of the flash does not imply the EEPROM is included.
Can fuses be reconstructed from a hex? No. They can sometimes be inferred from behaviour — a part that starts slowly was configured with a long start-up delay — but inference is not recovery.
Why does the tail of my dump read FF? Either the die is a smaller member of the family, or the region is unimplemented. The signature read distinguishes the two.
Conclusion
A hex file is a claim about addresses, and a claim is only as good as the checks behind it. Three items live outside the file — fuses, lock byte, calibration byte — and two regions inside it are routinely misread: unimplemented tails and address wraps past 64 KB. Reading the signature first and disassembling the vector table afterwards turns a plausible file into a verified one.