I am trying to write a system which stores 1024 bytes of data in the program space (PSV) - arranged in 128 blocks of 8 bytes.

I have pored through the data sheet, the flash programming manual, the "Data EEPROM Emulation" application note and source code, but I still just don't understand the workings of it.

So, my questions that I could really do with being answered are:

1) How do you reliably allocate the memory within the PSV space so that it uses one entire table page so that erasing only affects that allocated data?

At the moment I have:

unsigned char __attribute__((space(psv),aligned(1024))) nvram[128][8];

From what I can see that is then giving me a variable address of 0x00C400, a table page of 0x000000, and a table offset of 0x004400. When I erase the table page using these values, as per the example code, it nicely corrupts my code.

2) How do the values relate to each other?

From reading the data sheets etc I understood that the table page should be the page number - being a multiple of 512 rows of 64 program words. That to my mind is 1024 bytes of low-order storage. So how is the table page from above 0? Surely it would be 0x000031 (0x32 * 1024 = 0x00C400) or something similar?

Further, I understood that the table offset is the number of bytes from the start of the table to the address of the object you want to reference - so for a properly aligned variable that should be 0. How can you have an offset of 0x004400 in a block of memory that is only 0x0400 bytes long?

I am using the __builtin_tblpage() and __builtin_tbloffset() functions to obtain the values in question.

3) Is there anywhere that explains, in plain English how to work with the internal flash?

Yes, it's all very well having these data sheets and stuff, but they present way too much information in an overly complex way. Even the example code I can find is ridiculously complex and convoluted. Is there anywhere that can explain it better?

Extra info:

The PIC in question is a dsPIC33FJ128GP802

I am using MPLAB C30 V3.30b


3 Answers 3


I suspect you are a C language programmer. IC datasheets are generally written to a target audience of assembly language programmers, who need to be aware of many quirky little details. Often C language programmers are happy to let pre-written library functions take care of most of those details, rather than re-writing everything from scratch. Alas, the people who write those libraries often let some of the quirky little details show through.

There are two popular ways to store data to flash memory: let some library functions handle the quirky bits for you, or write your own functions to handle the quirky bits.

Using the library functions

Using "Data EEPROM Emulation" library that you linked. There are several ways of using its functions to read and write your data, to store your 1024 bytes of data, such as "8 virtual EEPROM banks with 128 bytes in each bank."

Check out the "PIC24/dsPIC33F/dsPIC33E Emulation Checklist" in AN1095. In principle, it explains how to use that library to store stuff in flash in relatively clear English. You edit the "DEE Emulation 16-bit.h" file, add that file and a few other library files to your project. When your program runs, it calls the DataEEInit() function during boot-up initialization. Later your program calls DataEERead() to read the latest version of your data values from flash, or calls DataEEWrite() to write new version of your data values to flash, or both.

Since it does wear-leveling, the latest version of the data is stored at different addresses at different times -- it allocates the memory for you, and keeps track of the address of the latest version of your data. So there's no point in creating your own variable "nvram" at some fixed address to refer to that data, since even if that happens to point to the correct address at one time, sooner or later that data will move to some other address, and that variable will be left pointing to old stale data.

writing your own library functions

The __builtin_tblpage() gives the "high part" of an address when divided up in the right way for the TBLRD and TBLWT instructions to read and write flash.

The __builtin_psvpage() gives the "high part" of an address when divided up in the right way for PSV to read flash. (My understanding is that the only way for a program running on that chip to write values to its program flash is with the TBLWT instruction; those values can later be read with either TBLRD or PSV).

The slight difference between these two ways of dividing an address into a "high part" and a "low part" is implied in the "dsPIC33FJ32GP302/304, dsPIC33FJ64GPX02/X04, and dsPIC33FJ128GPX02/X04 Data Sheet" that you linked to, in 4 pages of the datasheet starting with "4.6 Interfacing Program and Data Memory Spaces" and "TABLE 4-39: PROGRAM SPACE ADDRESS CONSTRUCTION".

1) How do you reliably allocate the memory [at some specific address] ? Alas, this is different for every programming language, and is different even between different C compilers. The "MPLAB C30 C Compiler User's Guide" and its documentation updates would be a good place to look for this information. I think you will also be interested in the documentation for

  • void _erase_flash(_prog_addressT dst)
  • void _write_flash16(_prog_addressT dst, int *src)

You need to modify the linker script (.gld) file so that the linker knows not to put code where you want to put data. See chapter 9. Make sure the linker script is included in your MPLAB project tree, under Header Files.

  • \$\begingroup\$ You can modify the linker script, but it is unnecessary. It is better if all this information stays in your source code. If you create a section at a fixed address, the linker will put that there and place everything relocatable around it. You therefore don't need to reserve fixed ranges for that purpose in the linker file. \$\endgroup\$ Commented Oct 18, 2011 at 12:23

That PIC doesn't have EEPROM, so you are apparently wanting to use one erase page of program memory to emulate EEPROM, and you furthermore want to read the data thru the PSV window? I'll assume that's what you are trying to do, but it would have been good if you said so explicitly.

Part of your confusion may be in what you can do with a PSV window. It basically gives you a data-mapped read-only view of the low 16 bits of successive 24 bit program memory words. Note the read-only part. If you want to erase and re-write the data, you'll have to do it the normal way using table writes, NVMCON, etc.

As for where this is all described, it's a little tricky to dig out dsPIC information until you understand the method. Unlike with other PIC types, the datasheets of the 24 bit core families (24, 30, 33) are only overviews and information unique to particular parts. The datasheet will have a chapter for each peripheral, but that is a overview only and generally insufficient to write code from. The details of the peripherals are documented in the family reference manual. Unfortunately I don't know of a way to download that all in one shot. Each chapter is for a different peripheral, but is also a separate PDF document. At the beginning of the chapter for that peripheral in the datasheet, it tells you the chapter of the family reference manual that covers that peripheral. Pay attention to the FRM chapter number. Some peripherals appear the same but actually are a little different and are described in different FRM chapters.

So the process for getting information about a peripheral is to look in the datasheet of the specific part, find the chapter for that peripheral, look at the beginning of that chapter to find the FRM chapter for that specific variant of that peripheral, then get the details from that FRM chapter.

I just checked, and chapter 5 of the datasheet for the 33FJ128GP802 is Flash Program Memory. The gray box at the beginning of that tells you the details are in the 33F/24H Family Reference Manual chapter 5 (just coincidence that datasheet and FRM chapter happen to have the same number). Download and study that. I've got various pieces of code that erase and write program memory, and it's always worked just like the FRM says it does. The FRM chapter may be large because they go over every detail, but it's written well and it's all in there.

For example, here is a snippet of a routine that erases a single page of program memory:

;   Erase the program memory block covered by the cache.
         mov     #tbloffset(nvol), w0 ;get nvol start adr in prog mem into W1:W0
         mov     #tblpage(nvol), w1
         mov     cawofs, w2  ;get nvol word offset of this block
         add     w0, w2, w0  ;add program memory address offset of block start
         addc    #0, w1
         add     w0, w2, w0
         addc    #0, w1
         mov     w1, Tblpag  ;set upper address bits of block
         mov     #0xFFFF, w2
         tblwtl  w2, [w0]    ;set low 16 address bits of block
         tblwth  w2, [w0]
         mov     #0b0100000001000010, w1
                 ;  0--------------- don't start write/erase operation now
                 ;  -1-------------- enable write/erase operation
                 ;  --0------------- clear any previous error condition
                 ;  ---XXXXXX------- unused
                 ;  ---------1------ operation is erase, not write
                 ;  ----------XX---- unused
                 ;  ------------0010 erase one erase block
         mov     w1, Nvmcon  ;select flash memory operation to perform

         disi    #1000       ;don't allow interrupts during unlock
         mov     #0x55, w1   ;perform the special erase/write unlock
         mov     w1, Nvmkey
         mov     #0xAA, w1
         mov     w1, Nvmkey
         bset    Nvmcon, #Wr ;start the erase operation
         nop                 ;required NOPs after erase or write
         clr     Disicnt     ;re-enable interrupts

This code is doing a few other things related to the higher levels, but as you can see it's pretty small and straight forward if you've read the manual.

As for mapping the program memory erase page to the PSV window, first make sure your program memory region is aligned at a erase page. I would use a seperate linker section for that and either give it a fixed aligned address or use the aligned attribute. In your code to set up the PSV window, you use the psvpage function to get the value to load into PSVPAG, and the psvoffset function to get the indirect address to use in one of the W registers to access the PSV word.


You are now complaining that I only told you things you already know and didn't answer two of your questions. First, I've found that someone saying they've read the datasheet is no information at all and therefore irrlevant. The datasheet and FRM are well written and complete. I know this because I've done a number of designs with these parts and successfully erased and wrote program memory pages at run time. This includes several bootloaders and a general purpose module that uses a page of program memory for non-volatile data storage, like a low endurance EEPROM. You also specifically asked where to read about this stuff in plain english, implying you aren't even aware of the Family Reference Manual. That's why I answered your question 3 in some detail.

I also answered your questions 1 and 2:

1) How do you reliably allocate the memory within the PSV space so that it uses one entire table page so that erasing only affects that allocated data?

As I said, put the program memory to be used as EEPROM in its own linker section, and make sure the memory itself is aligned at a erase page boundary. The reason for putting this in its own linker section is so the linker can place it independently. If you were to put it in the same section as your code, for example, then the alignment to a erase page start could leave a permanent gap. With a seperate linker section, this gap can be used for other purposes.

As I also pointed out, you seem to have a misconception that erasing is somehow related to the PSV window. The two are completely independent. As I said, think of the PSV window as a read-only view into program memory. Any erases and writes must be done directly on the program memory using the normal TBLWTx instructions and manipulation of NVMCON and related.

2) How do the values relate to each other?

As I said, the psvpage and psvoffset functions built into the assembler provide the mapping of a specific setion of program memory to the PSV window at run time.

  • 5
    \$\begingroup\$ You may have noticed that I linked to all that information from within the question. I already know all this - I have (as I said in my question quite clearly) read them all extensively. I have code that erases quite happily - it just appears to kill some of the rest of my code as well. Your last paragraph just touches on question one. Any chance you could answer the other two as well? \$\endgroup\$
    – Majenko
    Commented Oct 17, 2011 at 17:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.