10.3.4 Page Write

A page is written by first loading the buffer registers in the buffer RAM. All buffer registers are then written to PFM by setting the NVMADR to an address within the intended address range of the target PFM page, setting the NVMCMD bits to ‘b101, and then executing the unlock sequence and setting the GO bit.

If the PFM address in the NVMADR is write-protected, or if NVMADR points to an invalid location, the GO bit is cleared without any effect, and the WRERR bit is set.

CPU operation is suspended during a page write cycle and resumes when the operation is complete. The page write operation completes in one extended instruction cycle. When complete, the GO bit is cleared by hardware and NVMIF is set. An interrupt will occur if NVMIE is also set. The buffer registers and NVMCMD bits are not changed throughout the write operation.

The internal programming timer controls the write time. The write/erase voltages are generated by an on-chip charge pump and rated to operate over the voltage range of the device.

Important: Individual bytes of program memory may be modified, provided that the modification does not attempt to change any NVM bit from a ‘0’ to a ‘1’. When modifying individual bytes with a page write operation, it is necessary to load all buffer registers with either 0xFF or the existing contents of memory before executing a page write operation. The fastest way to do this is by performing a page read operation.

In this device a PFM page is 128 words (256 bytes). This is the same size as one bank of general purpose RAM (GPR). This area of GPR space is dedicated as a buffer area for NVM page operations. The buffer areas for each device in the family are shown in the following table:

Table 10-2. NVM Buffer Banks
Device GPR Bank Number
PIC18Fx6Q40 21
PIC18Fx5Q40 13
PIC18Fx4Q40 9
There are several ways to address the data in the GPR buffer space:
  • Using the TBLRD and TBLWT instructions
  • Using the indirect FSR registers
  • Direct read and writes to specific GPR locations

Neglecting the bank select bits, the 8 address bits of the GPR buffer space correspond to the 8 LSbs of each PFM page. In other words, there is a one-to-one correspondence between the NVMADRL register and the FSRxL register, where the x in FSRx is 0, 1 or 2.

The sequence of events for programming a page of PFM is:

  1. Set the NVMADR registers to an address within the intended page.
  2. Set the NVMCMD to ‘b110 (Erase Page).
  3. Disable all interrupts.
  4. Perform the unlock sequence as described in the Unlock Sequence section.
  5. Set the GO bit to start the PFM page erase.
  6. Monitor the GO bit or NVMIF interrupt flag to determine when the erase has completed.
  7. Set NVMCMD to ‘b101 (Page Write).
  8. Perform the unlock sequence.
  9. Set the GO bit to start the PFM page write.
  10. Monitor the GO bit or NVMIF interrupt flag to determine when the write has completed.
  11. Interrupts can be enabled after the GO bit is clear.
  12. Set the NVMCMD control bits to ‘b000.

Writing a Page of Program Flash Memory in C

// Code sequence to write a page of PFM
// Input[] is the user data that needs to be written to PFM
// PFM target address is specified by PAGE_ADDR

#define PAGESIZE 128               // PFM page size

// Save Interrupt Enable bit Value
uint8_t GIEBitValue = INTCON0bits.GIE;

// The BufferRAMStartAddr will be changed based on the device, refer
// to the "Memory Organization" chapter for more details
uint16_t bufferRAM __at(BufferRAMStartAddr);

// Defining a pointer to the first location of the Buffer RAM
uint16_t *bufferRamPtr = (uint16_t*) & bufferRAM;

//Copy application buffer contents to the Buffer RAM
for (uint8_t i = 0; i < PAGESIZE; i++) {
    *bufferRamPtr++ = Input[i];
}

// Load NVMADR with the base address of the memory page
NVMADR = PAGE_ADDR;              
NVMCON1bits.CMD = 0x06;            // Set the page erase command
INTCON0bits.GIE = 0;               // Disable interrupts
//––––––––– Required Unlock Sequence ––––––––– 
NVMLOCK = 0x55;
NVMLOCK = 0xAA;
NVMCON0bits.GO = 1;                // Start page erase
//–––––––––––––––––––––––––––––––––––––––––––––
while (NVMCON0bits.GO);            // Wait for the erase operation to complete
// Verify erase operation success and call the recovery function if needed
if (NVMCON1bits.WRERR){             
   ERASE_FAULT_RECOVERY();
}

// NVMADR is already pointing to target page
NVMCON1bits.CMD = 0x05;            // Set the page write command
//––––––––– Required Unlock Sequence ––––––––– 
NVMLOCK = 0x55;
NVMLOCK = 0xAA;
NVMCON0bits.GO = 1;                // Start page write
//–––––––––––––––––––––––––––––––––––––––––––––
while (NVMCON0bits.GO);            // Wait for the write operation to complete
// Verify write operation success and call the recovery function if needed
if (NVMCON1bits.WRERR){             
   WRITE_FAULT_RECOVERY();
}

INTCON0bits.GIE = GIEBitValue;     // Restore interrupt enable bit value
NVMCON1bits.CMD = 0x00;            // Disable writes to memory