The PIC18F6720 is the largest member of the DS39609 family: 128 KB of flash — 65,536 instructions — with 3,840 bytes of RAM, 1,024 bytes of data EEPROM, five CCP modules, a parallel slave port and two addressable USARTs, in a 64-pin TQFP clocked at up to 25 MHz. It is also the member where the gap between “the file disassembles” and “the file is complete” is at its widest, because half the array lives above 0xFFFF.
Nine blocks
The family splits program memory into 16 KB blocks on binary boundaries, with the first block subdivided around a 512-byte boot block:
| Block | Range | Controlled by |
|---|---|---|
| Boot | 0x0000–0x01FF |
CPB |
| 0 | 0x0200–0x3FFF |
CP0 |
| 1 | 0x4000–0x7FFF |
CP1 |
| 2 | 0x8000–0xBFFF |
CP2 |
| 3 | 0xC000–0xFFFF |
CP3 |
| 4 | 0x10000–0x13FFF |
CP4 |
| 5 | 0x14000–0x17FFF |
CP5 |
| 6 | 0x18000–0x1BFFF |
CP6 |
| 7 | 0x1C000–0x1FFFF |
CP7 |
Nine blocks, each with its own CP, WRT and EBTR bits in the CONFIG5L/6L/7L registers at 0x300008–0x30000C. Blocks 4 through 7 — the upper 64 KB — sit entirely above 0xFFFF. A dump that stops at the 16-bit boundary captures exactly half the part and reports the upper half as nonexistent rather than as locked. Intel HEX carries those addresses in type-04 records; tools that skip them produce files whose checksums still pass.
Why the upper half is not padding
On a 128 KB part, XC8 places constant tables, string pools and sometimes entire secondary code images in the top blocks. Firmware that references those tables by computed address runs fine until it reaches them, then returns garbage — no crash, no obvious symptom. A truncated 6720 image can pass a bench test and fail only in the field mode that uses the big tables. This is why completeness on this part is arithmetic: 131,072 array bytes ending at 0x1FFFF, twelve config bytes, 1,024 EEPROM bytes, and the ID from 0x3FFFFE. Anything less is partial, whatever it looks like.
The 25 MHz ceiling is the other family fact worth carrying: the 6520 in the same datasheet clocks to 40 MHz, so oscillator settings copied from a faster sibling’s file tell on themselves. The config words live outside the array, and losing them changes every time base the flash image depends on.
The EEPROM and per-unit data
The 1,024 EEPROM bytes at 0xF00000 sit under their own CPD bit, independently lockable from the nine flash blocks. On metering and instrumentation boards — the 6720’s home turf — this is where calibration curves and serial numbers live. The standard failure follows: flash copied, EEPROM blank, unit runs and misreads by the calibration constant. The PIC18F6620 shows the same split at 64 KB.
Assessment
Read the ID to fix the family; read the array to 0x1FFFF with a nine-block report; read the config words; attempt the EEPROM. A per-block pattern states the protection state without decapsulation; a wholly locked array moves the work to the die, where the code protection routes and the services page describe what remains. The deliverable never changes shape: the full array, the config words, the EEPROM, the ID — nine blocks’ worth, not five.
What the firmware is like
Five CCP modules, dual addressable USARTs and a parallel slave port point the 6720 at metering and instrumentation: firmware that accumulates, computes and reports. That kind of code keeps its constants — scaling tables, energy-per-pulse factors, tariff structures — in the flash array, and at 128 KB those tables live high, often in Blocks 6 and 7. The 3,840 bytes of RAM buffer the reporting side. A truncated copy therefore fails in the least visible way possible: the unit communicates correctly and computes wrong, because every value it serves came from a table the copy never reached. The user ID locations ride along with the config words on every read and often hold a production date code, which lets a rebuilt unit be matched to its source in a fleet where calibration has drifted per-unit. None of the assessment changes: nine block states, config words, EEPROM, ID, and the array verified to 0x1FFFF.