# Unable to use compiler built-in functions to write to dsPIC EEPROM

As I mentioned here, I've been having issues with EEPROM on dsPIC30F6012A. Since my old EEPROM library was difficult to analyze and support, I went back to square one and rewrote it using the new (newer than my library!) C30 compiler built-in EEPROM routines. I took the Microchip demo code and tried to run it. Compiled, programmed, no problems, but it didn't seem to operate correctly. I made a modified version of their demo, also without success.

I run this program, read the chip's contents back into MPLAB X, and look at the contents of EEPROM. Erase operations work correctly, any addresses I erase comes back as 0xFFFF. But the write operations do nothing. I've tried several combinations, repeated writes, different addresses, nothing appears to be written.

#include <p30fxxxx.h>
#include <libpic30.h>

_FOSC(CSW_FSCM_OFF & FRC_PLL16);
_FWDT(WDT_OFF);                 /* Turn off the Watch-Dog Timer.  */
_FBORPOR(MCLR_EN & PWRT_OFF);   /* Enable MCLR reset pin and turn off the power-up timers. */
_FGS(CODE_PROT_OFF);            /* Disable Code Protection */

typedef struct _eestruct {
unsigned char testdata[10];
} eestruct;
eestruct eedata __attribute__((space(eedata)));
eestruct backup_eedata __attribute__((space(eedata)));

int main(){

int temp_word = 0x0102;
_wait_eedata();
_wait_eedata();

while(1){
ClrWdt();
};
return 0;
}


I've got four theories:

1. Writes are taking place, and I'm not reading the data back correctly.
2. Writes are not taking place, because something is wrong with my code.
3. Writes are not taking place, because something is wrong with my project/build options.
4. Writes are not taking place, because something is wrong with my hardware.

I've posted to the Microchip forums without result. I've opened a ticket with Microchip, and all they did was tell me to use/write an assembly call. They seem uninterested in whether/why these built-in function calls don't work.

Does anyone have any suggestions?

• What is the device? Aug 16 '12 at 13:23
• @LeonHeller From the previously mentioned question, it is a dsPIC30F6012a
– W5VO
Aug 16 '12 at 13:35
• Correct, adding to the question. Aug 16 '12 at 14:15
• Does MPLAB X read the EEPROM as expected? I have some theories of my own. (1) Not supported. (2) Doesn't read back until you tell it to in the menu. (3) Doesn't read back correctly. This is all original MPLAB experience. It's possible MPLAB X has fixed all of this. When in doubt, I have the PIC tell me about its EEPROM contents (some kind of dump routine). Aug 16 '12 at 14:54
• The data I initialize EEPROM with does read back properly into MPLAB X, and erased values read back as 0xFFFF. Everything seems to work EXCEPT the write. Aug 16 '12 at 15:11

When in doubt, refactor.

It's not likely a hardware issue, but to rule it out, I would try replacing the C calls with the exact inline assembly instructions described in section 7 of the datasheet, following all of its recommendations (disabling interrupts, polling WR to test for doneness, etc.)

If you get it working, make your own function/library from the inline assembly and carry on.

• Unfortunately, that's what I had that I'm trying to refactor out! Aug 19 '12 at 16:41
• You should post something on the Microchip forums, or get in contact with an FAE if the datasheet-published code does not work. Aug 22 '12 at 2:11
• Already posted on the forums, and opened a ticket. No response to either. Aug 22 '12 at 12:40
• Could you post your disassembly / listing file as well? Aug 28 '12 at 2:36
• Amusingly, Microchip tech support's answer was also to replace the compiler function calls with assembly code. When I asked why the functions didn't work, they had no answer, and refused to even test my code. Apparently, even though the functions I'm using are listed in the compiler documentation, and even though Microchip publishes demo code using them on their site, the functions are not actually supported by Microchip. Aug 28 '12 at 13:04

First, a good way to check whether EEPROM has been successfully written is to read it back thru the PSV (Program Space Visibility) window. In a lot of cases I don't need the PSV window for anything else, so I leave it permanently configured for access to the EEPROM.

Second, I didn't look at your C code but here is a snippet from a 30F4013 project that sets up the PSV window for reading and writes a byte to the EEPROM:

;*******************************************************************************
;
;   Subroutine IEE_INIT
;
;   Initialize the hardware and software state managed by this module.
;
glbsub  iee_init, regf0
;
;   Set up the program space visibility window to map the internal data EEPROM
;   to data space.
;
mov     #psvpage(ieestart), w0
mov     w0, Psvpag  ;which region of prog memory to map to data adr 8000-FFFF
bset    Corcon, #Psv ;enable program space visibility window

leaverest

;*******************************************************************************
;
;   Macro WAIT_EEPROM
;
;   Wait for any previous EEPROM operation to complete.  TASK_YIELD is called in
;   a loop for the duration of the wait.  TASK_YIELD is not called if the EEPROM
;   is not active on entry.
;
.macro   wait_eeprom
1:                           ;back here to check EEPROM busy again
btss    Nvmcon, #Wr ;EEPROM is busy ?
jump    2f          ;no, all done
jump    1b          ;back to check EEPROM busy again
2:                           ;the EEPROM is now idle
.endm

;*******************************************************************************
;
;   Subroutine IEE_WRITE
;
;   Write the value in W0 to the EEPROM word at the offset in W1.  W1 must
;   contain the low 16 bits of the EEPROM location as mapped into program
;   memory.  This can be found, for example, by using the TBLOFFSET function on
;   any of the address symbols defined in the .EEPROM section above.  Since there
;   are two addresses per EEPROM word and a whole word is always written, the
;   low bit of W1 is irrelevant.
;
;   W1 is returned incremented by 2, which is the address of the next EEPROM
;   word.  Successive calls to this routine without modifying W1 therefore
;   set sequential EEPROM words.
;
;   This routine returns after the write has completed.  This may take several
;   milliseconds.  TASK_YIELD is called in a loop during any waits.
;
;   This processor has 512 words of EEPROM.
;
glbsub  iee_write, regf2

wait_eeprom         ;wait for any previous operation to complete
;
;   Get the current EEPROM target word value into W2.
;
mov     #tblpage(ieestart), w2
mov     w2, Tblpag  ;select the program memory page to access
tblrdl  [w1], w2    ;read the target EEPROM word
cp      w0, w2      ;compare the new value to the existing
bra     z, iew_leave ;no change, nothing to do ?
;
;   Erase the EEPROM word if any 0 bits are being changed to 1 bits.  The
;   existing value of the word is in W2.  TBLPAG is already set approriately for
;
com     w2, w2      ;make mask of bits currently set to 0
and     w2, w0, w2  ;make mask of 0 bits to change to 1
bra     z, iew_derase ;no 1 bits changing to 0, skip the erase
;
;   Perform the erase.
;
;   According to the family reference manual, to perform a erase you
;   need to set the address and then do a word erase.  However, we have
;   found this doesn't work for the first write attempt since startup.
;   After some experimentation it was found that loading a write latch
;   once seems to be what allows erases to work.  We therefore load the
;   write latch before doing a erase, even though this is not necessary
;   the word to erase also sets the address, we don't need to set
;   NVMADR explicitly.  The data written to the write latch probably
;   doesn't matter, but we write the erase value since that makes the
;   most sense and has the best chance of working around any additional
;   undocumented behavior.
;
mov     #0xFFFF, w2

mov     #0b0100000001000100, w2
;  0--------------- don't initiate operation now
;  -1-------------- enable erase operation
;  --0------------- clear any previous error condition
;  ---XXXXX-------- unused
;  --------0100---- select erase operation
;  ------------0100 one data word
mov     w2, Nvmcon  ;configure for the erase operation

disi    #5          ;disable interrupts around unlock sequence
mov     #0x55, w2   ;perform unlock sequence
mov     w2, Nvmkey
mov     #0xAA, w2
mov     w2, Nvmkey
bset    Nvmcon, #Wr ;start the erase

wait_eeprom         ;wait for the erase to complete
iew_derase:                  ;done with erasing the word
;
;   Write the word.
;
tblwtl  w0, [w1]    ;write the word, increment address

mov     #0b0100000000000100, w2
;  0--------------- don't initiate operation now
;  -1-------------- enable erase operation
;  --0------------- clear any previous error condition
;  ---XXXXX-------- unused
;  --------0000---- select write operation
;  ------------0100 one data word
mov     w2, Nvmcon  ;configure for the erase operation

disi    #5          ;disable interrupts around unlock sequence
mov     #0x55, w2   ;perform unlock sequence
mov     w2, Nvmkey
mov     #0xAA, w2
mov     w2, Nvmkey
bset    Nvmcon, #Wr ;start the erase

wait_eeprom         ;wait for the write to complete

iew_leave:                   ;common exit point