2
\$\begingroup\$

I am using PIC24fj256ga702 controller in my project. I wanted to store some bytes data in a non-volatile memory in case if there is a power loss.

so i wrote some functions following http://microchipdeveloper.com/16bit:flash-write

#define pagelength 1024             //page 42, one erase block 1024 instruction words
#define rowlength 128               //one write block = 128 instruction words.
/*
 * program memory upper boundary (instruction words) 0x02AFFE,
 * +2, i.e. 0x02B000, start of address
 * page length 1024 instruction words(erase block)
 * write block 128 instruction block; 255 bytes.
 */
#define address 0x2B002             // might have less than 255 blocks for writing data

uint8_t Rambuffer[pagelength * 2];
uint8_t Rowbuffer[rowlength * 2];       //one row of data, that can be written once. 255 bytes.

void readFlashPage(void)
{
    int offset, i;
    TBLPAG = __builtin_tblpage (address);    //returns the page number of the memory address received as a parameter. For table instructions the returned value is placed in TBLPAG
    offset = __builtin_tbloffset (address);  //returns the offset from the base 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
    offset = offset & 0xF800; //set to the base of page
    for(i = 0; i<(pagelength * 2); i++){
        Rambuffer[i++] = __builtin_tblrdl(offset);  //returns the lower 16 bits of the memory address specified by TBLPAG and the offset parameter(calls TBLRDL instruction)
        Rambuffer[i] = __builtin_tblrdh(offset);    //returns the upper 8-bits of the memory adddress specified by TBLPAG and the offset parameter(calls TBLRDL instruction)
        offset = offset + 2;
    }
}

void eraseFlashPage(void){
    int offset;
    NVMADRU = __builtin_tblpage(address);
    offset = __builtin_tbloffset(address);
    NVMADR = (offset & 0xF800); // for page size of 1024 PM words

    //set WREN and page Erase in NVMCON
    NVMCON = 0x4003;

    __builtin_disi(6);      //disable interrupts for next six instructions
    __builtin_write_NVM();  //intiate write process
}

void rowFlashWrite(void){
    int offset, i;
    TBLPAG = 0xFA;   // base address of write latches 0xFA0000h till 0xFA00FEh

    //load row of data into write latches
    offset = 0;
    for (i = 0; i < rowlength * 2 ; i++){
        __builtin_tblwtl(offset, Rowbuffer[i++]);
        __builtin_tblwth(offset, Rowbuffer[i]);
        offset+=2;
    }

    //set the destination address into the NVM address registers

    NVMADRU = __builtin_tblpage(address);
    offset = __builtin_tbloffset(address);
    NVMADR = (offset & 0xF800);   // for page size of 1024 PM words

    //set WREN and enable row write in NVMCON

    NVMCON = 0x4002;

    __builtin_disi(6); // disable interrupts for 6 instruction cycles
    __builtin_write_NVM(); // initate write process
}

void rowFlashRead(void){
    int offset, i;
    TBLPAG = __builtin_tblpage (address);    //returns the page number of the memory address received as a parameter. For table instructions the returned value is placed in TBLPAG
    offset = __builtin_tbloffset (address);  //returns the offset from the base 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
    for(i = 0; i<(rowlength * 2); i++){
        Rowbuffer[i++] = __builtin_tblrdl(offset);  //returns the lower 16 bits of the memory address specified by TBLPAG and the offset parameter(calls TBLRDL instruction)
        Rowbuffer[i] = __builtin_tblrdh(offset);    //returns the upper 8-bits of the memory adddress specified by TBLPAG and the offset parameter(calls TBLRDL instruction)
        offset = offset + 2;
    }
}

but I am getting this error when I try to build it

make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory 'C:/Users/HP/MPLABXProjects/Rollman/emulate.X'
make  -f nbproject/Makefile-default.mk dist/default/production/emulate.X.production.hex
make[2]: Entering directory 'C:/Users/HP/MPLABXProjects/Rollman/emulate.X'
"C:\Program Files (x86)\Microchip\xc16\v1.35\bin\xc16-gcc.exe"   main.c  -o build/default/production/main.o  -c -mcpu=24FJ256GA702  -MMD -MF "build/default/production/main.o.d"      -mno-eds-warn  -g -omf=elf -DXPRJ_default=default  -legacy-libc    -O0 -msmart-io=1 -Wall -msfr-warn=off  
nbproject/Makefile-default.mk:155: recipe for target 'build/default/production/main.o' failed
make[2]: Leaving directory 'C:/Users/HP/MPLABXProjects/Rollman/emulate.X'
nbproject/Makefile-default.mk:90: recipe for target '.build-conf' failed
make[1]: Leaving directory 'C:/Users/HP/MPLABXProjects/Rollman/emulate.X'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
main.c: In function 'readFlashPage':
main.c:83:32: error: Argument to __builtin_tblpage() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
main.c:84:34: error: Argument to __builtin_tbloffset() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
main.c: In function 'eraseFlashPage':
main.c:95:32: error: Argument to __builtin_tblpage() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
main.c:96:33: error: Argument to __builtin_tbloffset() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
main.c: In function 'rowFlashWrite':
main.c:120:32: error: Argument to __builtin_tblpage() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
main.c:121:33: error: Argument to __builtin_tbloffset() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
main.c: In function 'rowFlashRead':
main.c:134:32: error: Argument to __builtin_tblpage() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
main.c:135:34: error: Argument to __builtin_tbloffset() is not the address
of an object in a code, psv, or eedata section;
the object must not be qualified with any form of index
make[2]: *** [build/default/production/main.o] Error 255
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 573ms)

Will this fix it?

uint16_t address __attribute__ ((space(prog))) = 0x2B002;

and passing &address to __builtin_tblpage ?

but the space attribute is used to direct the compiler to allocate a variable in specific memory spaces, so this can't be correct?

I really need some help here.

Thanks

p.s. also, can you please see the code, and tell me what I might be doing wrong

\$\endgroup\$
  • \$\begingroup\$ You're trying to use memory which does not exist. There is only Flash under the 0x2AFFE address and you're trying to access 0x2B002. Datasheet page 42. \$\endgroup\$ – brhans Dec 4 '18 at 12:14
  • \$\begingroup\$ I thought the upper most bound of program memory was 0x2AFFE, and after this address till 0x7FFFFF could be used to emulate EEPROM. \$\endgroup\$ – aamir Dec 4 '18 at 12:27
  • \$\begingroup\$ No. There is no memory of any type implemented at addresses between 0x2AFFE and 0x800000 on your device - that's why the datasheet specifies "Unimplemented Read ‘0’" for that range. If you want to use some Flash to emulate EEPROM, you have to use Flash in the 'User Flash Program Memory' area between 0x00000 and 0x2AFFE. \$\endgroup\$ – brhans Dec 4 '18 at 13:23
  • \$\begingroup\$ oh, so what if i overwrite the page on which the program is written? this is what pickit tells me when i program my controller with a previous version... The following memory area(s) will be programmed: program memory: start address = 0x0, end address = 0x23ff Programming/Verify complete \$\endgroup\$ – aamir Dec 4 '18 at 14:11
  • \$\begingroup\$ if i declare this array, and pass the address of the first memory location, the project builds... is this the right way of doing it? const unsigned int __ attribute __ ((space(prog), address (0x6000))) table[10]; \$\endgroup\$ – aamir Dec 4 '18 at 14:42
1
\$\begingroup\$

You're trying to use memory which does not exist.
There is only Flash under the 0x2AFFE address and you're trying to access 0x2B002.
See the Datasheet page 42.
There is no memory of any type implemented at addresses between 0x2AFFE and 0x800000 on your device - that's why the datasheet specifies "Unimplemented Read ‘0’" for that range.
If you want to use some Flash to emulate EEPROM, you have to use Flash in the 'User Flash Program Memory' area between 0x00000 and 0x2AFFE.

const unsigned int attribute ((space(prog), address (0x6000))) table[10];
is a perfectly acceptable way of doing it.
You've having the compiler allocate that block of memory to your array so it won't put any code there, making it safe for you to use.
However, you need to be sure that the address and size you specify for your array lines up with exactly one or more of the 'Erase Blocks' in your PIC's Flash.
The datasheet tells you that the 0x2B000 range is split into 172 blocks, so that's 0x400 per block. Your array start address & size must be a multiple of this value, so for your case you should change the size of your array.
If your array does not exactly fill one or more blocks, then the compiler is free to use the remainder of a block for code - but since the PIC will erase an entire block when it needs to do so for your EEPROM emulation, this would result in some code being erased too.

\$\endgroup\$
  • \$\begingroup\$ Thanks, this did fix the problem, however now whenever i try to read the memory, after i have written it, i get only ones. maybe i am writing it wrong, or maybe i am reading it wrong? \$\endgroup\$ – aamir Dec 5 '18 at 15:59
0
\$\begingroup\$

It seems the variable you pass to __builtin_tblpage macros are a byte (bit), while you need to pass a short (16bits)

I use these function for Microchip to write / read PIC24EP microchip, they should be quite similar for you.

/*********************************************************************
 * Function:        unsigned int NVMErasePage(void* address)
 *
 * Description:     Block Erases Program Memory
 * PreCondition:    None
 *
 * Inputs:          address:  Destination page address to Erase.
 *
 * Output:          '0' if operation completed successfully.
 *
 * Example:         NVMemErasePage(UINT32 0xBD000000)
 ********************************************************************/
UINT NVMemBlockErase(void)
{

    NVMCON = 0x400D;                //Bulk erase on next WR
    INTCON2bits.GIE = 0;                            //Disable interrupts for next few instructions for unlock sequence
    __builtin_write_NVM();
    while(NVMCONbits.WR == 1){}
    INTCON2bits.GIE = 1;                            // Re-enable the interrupts (if required).


    // Return WRERR state.
    return NVMCONbits.WRERR;    
}   



/*********************************************************************
 * Function:        unsigned int NVMErasePage(void* address)
 *
 * Description:     A page erase will erase a single page of program flash,
 *                  which equates to 1k instructions (3KBytes). The page to
 *                  be erased is selected using NVMADDR. The lower bytes of
 *                  the address given by NVMADDR are ignored in page selection.
 *
 * PreCondition:    None
 *
 * Inputs:          address:  Destination page address to Erase.
 *
 * Output:          '0' if operation completed successfully.
 *
 * Example:         NVMemErasePage(UINT32 0xBD000000)
 ********************************************************************/
UINT NVMemErasePage(UINT32 address)
{
    DWORD_VAL eraseAddress;
    eraseAddress.Val = address;


    TBLPAG = eraseAddress.byte.UB;
    NVMADRU = eraseAddress.word.HW;
    NVMADR = eraseAddress.word.LW;
    __builtin_tblwtl(eraseAddress.word.LW, 0xFFFF);
    NVMCON = 0x4003;                //Erase page on next WR

    INTCON2bits.GIE = 0;                            //Disable interrupts for next few instructions for unlock sequence
    __builtin_write_NVM();
    while(NVMCONbits.WR == 1){}
    INTCON2bits.GIE = 1;                            // Re-enable the interrupts (if required).


    // Return WRERR state.
    return NVMCONbits.WRERR;

}


/*********************************************************************
 * Function:        unsigned int NVMWriteWord(UINT32 address, UINT32 data)
 *
 * Description:     The word at the location pointed to by NVMADDR is programmed.
 *
 * PreCondition:    None
 *
 * Inputs:          address:   Destination address to write.
 *                  data:      Word to write.
 *
 * Output:          '0' if operation completed successfully.
 *
 * Example:         NVMWriteWord(0xBD000000, 0x12345678)
 ********************************************************************/
UINT NVMemWriteWord(UINT32 address, UINT32 data)
{
    DWORD_VAL writeAddress;
    DWORD_VAL writeData;

    writeAddress.Val = address;
    writeData.Val = data;

    NVMCON = 0x4001;        //Perform WORD write next time WR gets set = 1.
    NVMADRU = writeAddress.word.HW;
    NVMADR = writeAddress.word.LW;

    // Set the table address of "Latch". The data is programmed into the FLASH from a temporary latch. 
    TBLPAG = 0xFA;
    //The smallest block of data that can be programmed in
    //a single operation is 2 instruction words (6 Bytes + 2 Phantom Bytes).
    // Mask the high or low instruction words depending on the address and write either high or low instruction word.
    if(address % 4)
    {
        __builtin_tblwtl(0, 0xFFFF);                //Mask the low word of 1-st instruction into the latch.
        __builtin_tblwth(1, 0x00FF);                //Mask the high word of 1-st instruction into the latch. (8 bits of data + 8 bits of "phantom data" (phantom byte is always 0))

        __builtin_tblwtl(2, writeData.word.LW);     //Write the low word of 2-nd instruction into the latch
        __builtin_tblwth(3, writeData.word.HW);     //Write the high word of 2-nd instruction into the latch        

    }
    else
    {
        __builtin_tblwtl(0, writeData.word.LW);     //Write the low word of 1-st instruction into the latch
        __builtin_tblwth(1, writeData.word.HW);     //Write the high word of 1-st instruction into the latch 
        __builtin_tblwtl(2, 0xFFFF);                //Mask the low word of 2-nd instruction into the latch.
        __builtin_tblwth(3, 0x00FF);                //Mask the high word of 2-nd instruction into the latch. (8 bits of data + 8 bits of "phantom data" (phantom byte is always 0))

    }       

    INTCON2bits.GIE = 0;                            //Disable interrupts for next few instructions for unlock sequence
    __builtin_write_NVM();
    while(NVMCONbits.WR == 1){}
    INTCON2bits.GIE = 1;                            // Re-enable the interrupts (if required).

    // Return WRERR state.
    return NVMCONbits.WRERR;
}

And I use them like:

typedef short          Word16;
typedef unsigned short UWord16;
typedef long           Word32;
typedef unsigned long  UWord32;
typedef union tuReg32
{
    UWord32 Val32;

    struct
    {
        UWord16 LW;
        UWord16 HW;
    } Word;

    char Val[4];
} uReg32;

void WritePM(char *Data, uReg32 SourceAddr, unsigned int Size)
{
    unsigned int    j, i = 0;
        uReg32 data1;
        char ptrData[6];

    while (i < Size)
    {
        for (j=0; j <6;j++)
            ptrData[j] = Data[j+i];

        data1.Val[0] = ptrData[0];
        data1.Val[1] = ptrData[1];
        data1.Val[2] = ptrData[2];

        NVMemWriteWord((UINT32) SourceAddr.Val32, (UINT32) data1.Val32);

        SourceAddr.Val32 += 2;
        i += 3;

        ClrWdt();
    }
}

And

#define CONFIG_ADDR 0x7FF000

void WriteConf(char * ptr, unsigned int size)
{
    uReg32 Addr;

    Addr.Val32 = CONFIG_ADDR;

    _IPL = 7;
    Erase(Addr.Word.HW, Addr.Word.LW,PM_ROW_ERASE);
    _IPL = 0;

    WritePM( ptr, Addr, size);

}

void ReadConf(char * ptr, unsigned int size)
{
    uReg32 Addr;

    Addr.Val32 = CONFIG_ADDR;

    _IPL = 7;
    ReadPM(ptr, Addr, size);
    _IPL = 0;
}

You can simply create a structure of your data and send a pointer to those functions.


Note:

  • You can't write flash without erasing an entire page first. It would either fail or the data would be corrupted.
  • You need to write aligned into a page.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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