PIC18F6620: Five Blocks, a 512-Byte Boot, 25 MHz

The PIC18F6620 tops out at 64 KB in the DS39609 family: 3,840 bytes of RAM, 1,024 bytes of EEPROM, five CCP modules, a parallel slave port and two addressable USARTs in a 64-pin package. Two facts about it are easy to get wrong, and both come from its own datasheet rather than from neighboring parts: its boot block is 512 bytes, and its clock ceiling is 25 MHz.

The block map

Per the family documentation, the FX620 members divide program memory into blocks of 16 KB on binary boundaries, with the first block further split into a 512-byte boot block and a 15.5 KB Block 0:

Block Range Controlled by
Boot 0x0000–0x01FF CPB
0 0x0200–0x3FFF CP0
1 0x4000–0x7FFF CP1
2 0x8000–0xBFFF CP2
3 0xC000–0xFFFF CP3

Five blocks, each with its own CP, WRT and EBTR bits, plus CPD over the EEPROM at 0xF00000. This is a different geometry from the 6520’s 2 KB boot and 8 KB blocks in the same datasheet, and different again from the 8722 family the PIC18F6621 belongs to. A per-block read report is only meaningful against the right map — which is why the device ID at 0x3FFFFE is read first, every time.

A locked Block 0 on this part is a 15.5 KB region starting 512 bytes into the array. Someone checking with a 2 KB boot assumption reports the boundary 1.5 KB off and mislabels which code returned zeros. Since 0x0000 is a legal instruction, the zeros disassemble cleanly and nothing flags the error.

The 25 MHz ceiling

The 6520 in the same datasheet clocks to 40 MHz; the 6620 and the 128 KB 6720 stop at 25 MHz. The oscillator configuration lives in the config words at 0x300000, outside the array, carried by type-04 hex records. A rebuild that loses the config words — or inherits them from a faster sibling’s file — changes the instruction clock on a board designed around it. UART baud rates, PWM frequency and CCP timing shift together. The 25 MHz limit is also a quiet discriminator: firmware built for a 40 MHz part is a hint that the die is not a 6620.

What the read-out must cover

Four items make a complete deliverable:

1. The array, 0x0000–0xFFFF, with a per-block state report. 2. The config words at 0x300000 — including the oscillator setting that the 25 MHz ceiling constrains. 3. The 1,024 EEPROM bytes at 0xF00000, under their own CPD bit. On industrial-control boards this is the calibration and the node parameters; a blank EEPROM clone runs on the bench and misreads sensors in the field. 4. The device ID and revision from 0x3FFFFE.

A file that stops short of 0xFFFF is incomplete by definition on this part. Since the flash image is the product, completeness is arithmetic, not impression: 65,536 array bytes, 1,024 EEPROM bytes, twelve config bytes.

When blocks are locked

Where some blocks read as zeros, the map above locates the missing code precisely, and the protection state follows from the pattern without decapsulation. Where the whole array is locked, the code protection work moves to the die, and the services page sets out what that route adds over a programmer attempt. None of it changes the shape of the deliverable: the same four items, from a die read instead of an ICSP session.

What the firmware tends to look like

Five CCP modules and two addressable USARTs put the 6620 on industrial control and monitoring boards: firmware that drives outputs and reports to a host simultaneously. The array fills from both ends — application logic low, protocol strings and scaling tables high — and Block 3, the top 16 KB, is a common home for exactly the tables the host-facing side reads. That is why a partial read that reports Blocks 0 through 2 as firmware and Block 3 as zeros is worse than it sounds: the unit will boot, communicate, and send values computed from missing tables. The 3,840 bytes of RAM buffer the conversation, so the corruption happens quietly rather than as a fault. The user ID locations, often holding a date code from production, ride along in the same read pass and give the rebuild a way to be matched to a specific unit later.