Writing to Flash Memory on a 16-bit PIC® MCU

Last modified by Microchip on 2023/11/09 09:00

Overview of Writing to Flash Memory

Flash memory panels on 16-bit PIC® Microcontrollers (MCUs) are divided into pages. Each page consists of a number of rows. Each row has a set of 24-bit program words. Page and row sizes differ among the MCUs. Consult the datasheet to determine the page and row sizes for the PIC MCU you are using.

The implementation details of flash pages and rows are invisible to the application program. Rows and pages have no effect on program control or where the linker places program objects. Pages and rows are only taken into consideration when the application writes to the flash memory at run-time.

Writing to flash consists of loading the data to be written into a holding latch (or series of latches) using Table Write instructions. The flash address to be written to is loaded in the Non-Volatile Memory (NVM) address register. Once the data and address are loaded, a specific sequence of register operations will initiate the writing process. The writing process transfers the data from the holding latches into the Flash memory. During the writing process, the MCU stalls, resuming once the write operation has been completed

Process for Writing to Flash Memory​​​​

All 16-bit PIC MCUs allow the application program to specify individual Flash memory words to be written. Using this word addressed write method, 24-bits of data are loaded into a write latch with a pair of Table Write instructions (TBLWTH and TBLWTL). After the latch is loaded, the applications load the destination address into the NVM address registers. After both sets of registers are loaded, the application sets the control bits, then executes the write sequence on the NVMKEY register. The write sequence causes the data in the latches to be written to the Flash address specified by the NVM address registers.

Some devices provide the ability to program an entire row at one time. To program an entire row, the application loads a series of holding latch registers with repeated Table Write instructions. The applications then load the first address of the row to be written into the NVM address registers. After both sets of registers are loaded, the application executes the write sequence. With row programming, an entire row is written to flash at one time.
Back to top

Resources used in writing to Flash Memory

Instructions

Assembly Read Instructions
TBLRDLTable_Read_LowReads the lower 16-bits of a 24-bit flash memory word. The address to be read is designated by the TBLPAG register and a working register passed to this instruction.
TBLRDHTable_Read_HighReads the upper 8- bits of a 24-bit flash memory word. The address to be read is designated by the TBLPAG register and a working register passed to this instruction.

For more details on the table, read the Read Flash Memory instructions.

Assembly Write Instructions
TBLWTLTable_Write_LowLoads 16-bits of data into a holding latch. The address of the data latch to be written to is designated by the table page TBLPAG register and a working register. The latch will be written to flash memory when the programming sequence is initiated.
TBLWTHTable_Write_HighLoads 8-bits of data into a holding latch. The address of the data latch to be written to is designated by the TBLPAG register and a working register. The latch will be written to flash memory when the programming sequence is initiated.
C Language Macros
MacroDescription
__builtin_tblpageReturns the page number of the memory address received as a parameter. For table instructions the returned value is placed in TBLPAG.
__builtin_tbloffsetReturns the "offset" from the base page address for a memory location whose address is passed as a parameter. The return value of this function is passed as a parameter to table read and table write instructions.
__builtin_write_NVMPerforms the programming sequence on NVMKEY then immediately sets the WR__builtin_NVM causes the contents of the data latch(es) to be written to flash memory. This macro does NOT disable interrupts.
__builtin_disiDisables interrupts for a set number of instructions. This macro is typically called immediately preceding __builtin_write_NVM.
__builtin_tblrdhReturns the upper 8-bits of the memory address specified by TBLPAG and the offset parameter. (Calls TBLRDH instruction).
__builtin_tblrdlReturns the lower 16-bits of the memory address specified by TBLPAG and the offset parameter. (Calls TBLRDL instruction).
__builtin_tblwrthWrites 8-bits of data to a holding latch which will be programmed to flash when the program sequence is initiated. This function receives two parameters: the value to be written and a page offset. This function determines the latch to be programmed using the contents of TBLPAG and the offset parameter. (Calls TBLWRTH instruction).
__builtin_tblwrtlWrites 16-bits of data to a holding latch which will be programmed to flash when the program sequence is initiated. This function receives two parameters: the value to be written and a page offset. This function determines the latch to be programmed using the contents of TBLPAG and the offset parameter. (Calls TBLWRTL instruction).

Back to top

Registers

TBLPAG Table Page Register - TBLPAG is used by the Table Read and Table Write instructions. This register specifies the upper 8-bits of the address to be accessed by the table instruction. The lower 16-bits of the address are passed to the instruction as a value in a working register. When a Table Read instruction is to be executed this register contains the page address of the word to be read. When a Table Write instruction is to be executed TBLPAG should be loaded with the base address of the holding latch(es) (#FA00 on many 16-bit PIC MCUs)

NVMADR/NVMADDRU NVM Address Registers - Holds the address of the flash memory word (or row address) which will be written to when the program sequence is executed. After loading the holding latch(es) the application sets the destination address in NVMADR/NVMADRU before initiating the write sequence. The write sequence causes the data to be copied from the latches into the flash memory location specified by NVMADR/NVMADRU.

NVMCON Non-Volatile Memory Control Register - The register through which the application selects the function to be performed when the program sequence is executed. The options in this register include Erase Page, Write Word, and Write Row. NVMCON also contains the bit to be written to start the write process (WR).

NVMKEY Non-Volatile Memory KeyRegister - To reduce the potential for Flash to be modified by a spurious write to NVMCON WR cannot be set unless a specific sequence of data values are written to NVMKEY. This sequence is commonly referred to as the program sequence. Once the sequence is executed WR becomes 'write-able' for 2 instruction cycles.

Back to top

Program (Unlock) Sequence

The program sequence for a 16-bit PIC Microcontroller (MCU) is performed by writing specific values, in successive order, to NVMKEY. Writing 0x55, immediately followed by 0xAA, will unlock WR for two instruction cycles. If WR is set within two instruction cycles of the programming sequence, the MCU will perform the flash self-write process.

Interrupts should be disabled during the write program sequence to prevent servicing an interrupt between the two write instructions.

The unlock sequence can be written in C so long as the compiler places the two write instructions in sequential order. The MPLAB® XC16 compiler provides the function builtin-write-NVM to perform this program sequence. After executing the program sequence, builtin-write-NVM sets WR which initiates a flash write.

Back to top

Using MPLAB® XC16 C Compiler

XC16 Compiler Built-in Functions for Writing Flash​​​​

Using Assembly Language

Assembly Language Code for Writing to Flash​​​​

Algorithm to Self-Program Flash Memory on a 16-bit PIC® MCU

The 16-bit PIC MCU reference manual advises that a row or word in Flash program memory should not be programmed twice before being erased. Given this erase-before-programming mandate, the following application flow is commonly used:

Copy the Page To Be Updated Into Data Memory
  • Load the base address of the Flash memory page into TBLPAG.
  • Using successive TBLRDH and TBLRDL instructions, copy the contents of the Flash page to data RAM. Care should be taken when copying 24-bit wide Flash memory into the 16-bit wide data memory. One word of Flash memory requires two read instructions (TBLRDH and TBLRDL). These two read instructions will return a total of 32 bits of data.
Code Example for Reading a Page of Flash Memory

Example of Reading a Page of Flash Memory

Back to top

Update the Page in Data Memory

Pages can only be erased in their entirety. This means that updating even a single word of Flash memory requires the entire page to be read into Random Access Memory (RAM), erased in Flash, then reprogrammed. Unlike most Electrically Erasable Programmable Read-Only Memory (EEPROM) implementations, Flash memory words cannot be individually updated.

Back to top

Erase the Page In Flash Memory
  • Load the base address of the page to be erased into the NVM address registers NVMADR and NVMADDRU. The base address is a modulo of the page size. For MCUs with a page size of 1024, the lowest 10 bits of the address for all page addresses will be '0'. Ensure that the value placed into NVMADR has the correct base address by masking off the appropriate number of lower bits.
  • Set the bits in NVMCON to enable writes and erase the page. The MCU in this example requires the value 0x4003 to set WREN and to erase a page. Check the datasheet for the bits to set in NVMCON of the MCU being used.
  • Disable interrupts for the duration of the write sequence.
  • Perform the program sequence.
  • Ensure WR is set within two instruction cycles of the completion of the programming sequence. __builtin_write_NVM issues the program sequence, then sets WR.

Code Example for Erasing a Page of Flash Memory

Back to top

Program the New Data Into the Flash Memory

To write Flash, you must first consult the datasheet for the part you are using to determine the base address of the write latches. This information is typically listed in the memory map section rather than the Flash memory section of the datasheet. In this example, 0xFA000 is the base address of the write latches.

Writing a Row of Flash Data
  • Set TBLPAG with bits <11:8> of the base address of the write latches.
  • Using multiple TBLWRTH and TBLWRTL instructions move data from RAM into the data latches.
  • Load NVMADR and NVNADDRU with the base address of the Flash row to be written to.
  • Set the Write Enable (WREN) and the bits needed to write a row into NVMCON.
  • Perform the program sequence.
  • Set WR within two cycles of completing the program sequence.

Code Example for Writing a Row of Flash Memory

Back to top