1

I have some questions regarding the flash memory with a dspic33ep512mu810. I'm aware of how it should be done: set all the register for address, latches, etc. Then do the sequence to start the write procedure or call the builtins function.

But I find that there is some small difference between what I'm experiencing and what is in the DOC.

  1. when writing the flash in WORD mode. In the DOC it is pretty straightforward. Following is the example code in the DOC
int varWord1L = 0xXXXX;
int varWord1H = 0x00XX;
int varWord2L = 0xXXXX;
int varWord2H = 0x00XX;
int TargetWriteAddressL; // bits<15:0>
int TargetWriteAddressH; // bits<22:16>
NVMCON = 0x4001; // Set WREN and word program mode
TBLPAG = 0xFA; // write latch upper address
NVMADR = TargetWriteAddressL; // set target write address
NVMADRU = TargetWriteAddressH;
__builtin_tblwtl(0,varWord1L); // load write latches
__builtin_tblwth(0,varWord1H);
__builtin_tblwtl(0x2,varWord2L);
__builtin_tblwth(0x2,varWord2H);
__builtin_disi(5); // Disable interrupts for NVM unlock sequence
__builtin_write_NVM(); // initiate write
while(NVMCONbits.WR == 1);

But that code doesn't work depending on the address where I want to write. I found a fix to write one WORD but I can't write 2 WORD where I want. I store everything in the aux memory so the upper address(NVMADRU) is always 0x7F for me. The NVMADR is the address I can change. What I'm seeing is that if the address where I want to write modulo 4 is not 0 then I have to put my value in the 2 last latches, otherwise I have to put the value in the first latches.

If address modulo 4 is not zero, it doesn't work like the doc code(above). The value that will be at the address will be what is in the second set of latches. I fixed it for writing only one word at a time like this:

if(Address % 4)
{
    __builtin_tblwtl(0, 0xFFFF);
    __builtin_tblwth(0, 0x00FF);
                                                        
    __builtin_tblwtl(2, ValueL);
    __builtin_tblwth(2, ValueH);        
}
else
{
    __builtin_tblwtl(0, ValueL);
    __builtin_tblwth(0, ValueH);
    __builtin_tblwtl(2, 0xFFFF);
    __builtin_tblwth(2, 0x00FF);
}

I want to know why I'm seeing this behavior?

2)I also want to write a full row. That also doesn't seem to work for me and I don't know why because I'm doing what is in the DOC. I tried a simple write row code and at the end I just read back the first 3 or 4 element that I wrote to see if it works:

NVMCON = 0x4002;                  //set for row programming
TBLPAG = 0x00FA;                  //set address for the write latches
NVMADRU = 0x007F;                 //upper address of the aux memory
NVMADR = 0xE7FA;
int latchoffset;
latchoffset = 0;
__builtin_tblwtl(latchoffset, 0);
__builtin_tblwth(latchoffset, 0);         //current = 0, available = 1
latchoffset+=2;
__builtin_tblwtl(latchoffset, 1);
__builtin_tblwth(latchoffset, 1);         //current = 0, available = 1
latchoffset+=2;
.
. all the way to 127(I know I could have done it in a loop)
.
__builtin_tblwtl(latchoffset, 127);
__builtin_tblwth(latchoffset, 127);
INTCON2bits.GIE = 0;  //stop interrupt
__builtin_write_NVM();
while(NVMCONbits.WR == 1);
INTCON2bits.GIE = 1;  //start interrupt
int testaddress;
testaddress = 0xE7FA;
status = NVMemReadIntH(testaddress);
status = NVMemReadIntL(testaddress);
testaddress += 2;
status = NVMemReadIntH(testaddress);
status = NVMemReadIntL(testaddress);
testaddress += 2;
status = NVMemReadIntH(testaddress);
status = NVMemReadIntL(testaddress);
testaddress += 2;
status = NVMemReadIntH(testaddress);
status = NVMemReadIntL(testaddress);

What I see is that the value that is stored in the address 0xE7FA is 125, in 0xE7FC is 126 and in 0xE7FE is 127. And the rest are all 0xFFFF. Why is it taking only the last 3 latches and write them in the first 3 address?

Thanks in advance for your help people.

VC.One
  • 14,790
  • 4
  • 25
  • 57
Philippe
  • 13
  • 5

2 Answers2

0

The dsPIC33 program memory space is treated as 24 bits wide, it is more appropriate to think of each address of the program memory as a lower and upper word, with the upper byte of the upper word being unimplemented

(dsPIC33EPXXX datasheet)

dsPIC33EPXXX PROGRAM MEMORY ORGANIZATION

There is a phantom byte every two program words.

Your code

if(Address % 4)
{
    __builtin_tblwtl(0, 0xFFFF);
    __builtin_tblwth(0, 0x00FF);
                                                        
    __builtin_tblwtl(2, ValueL);
    __builtin_tblwth(2, ValueH);        
}
else
{
    __builtin_tblwtl(0, ValueL);
    __builtin_tblwth(0, ValueH);
    __builtin_tblwtl(2, 0xFFFF);
    __builtin_tblwth(2, 0x00FF);
}

...will be fine for writing a bootloader if generating values from a valid Intel HEX file, but doesn't make it simple for storing data structures because the phantom byte is not taken into account.

If you create a uint32_t variable and look at the compiled HEX file, you'll notice that it in fact uses up the least significant words of two 24-bit program words. I.e. the 32-bit value is placed into a 64-bit range but only 48-bits out of the 64-bits are programmable, the others are phantom bytes (or zeros). Leaving three bytes per address modulo of 4 that are actually programmable.

What I tend to do if writing data is to keep everything 32-bit aligned and do the same as the compiler does.

Writing:

UINT32 value = ....;
:
__builtin_tblwtl(0, value.word.word_L); // least significant word of 32-bit value placed here
__builtin_tblwth(0, 0x00); // phantom byte + unused byte          
__builtin_tblwtl(2, value.word.word_H); // most significant word of 32-bit value placed here
__builtin_tblwth(2, 0x00); // phantom byte + unused byte       

Reading:

UINT32 *value
:
value->word.word_L = __builtin_tblrdl(offset);
value->word.word_H = __builtin_tblrdl(offset+2);

UINT32 structure:

typedef union _UINT32 {
    uint32_t val32;
    struct {
        uint16_t word_L;
        uint16_t word_H;
    } word;
    uint8_t bytes[4];
} UINT32;
pm101
  • 1,309
  • 10
  • 30
0

The ECC mechanism in the dspic33c gets confused by multiple writes to the same flash address after erase. This is different from the dspic33e with no ECC. I think that upon overwrite, the ECC is updated with the complete new word, but the flash address only updates bits that are changing from 1 to 0. Every other bit that does not change is seen as an error by the ECC mechanism. Single bit errors are "corrected" transparently, but double bit errors cause a generic hard stop CPU trap which, if not handled results in reset. Re-writing the above code so it only writes a double word (48-bit write, addr and addr+2) when it has two words to write should do it. Trying to write a mask of 0x00FFFFFF over the same address is causing the ECC to corrupt the data. With the dspic33c you can only write to each flash address once after erase.