PIC24 DATA EEPROM Emulación __builtin_tblpage error

Estoy usando el controlador PIC24fj256ga702 en mi proyecto. Quería almacenar algunos bytes de datos en una memoria no volátil en caso de que haya una pérdida de energía.

así que escribí algunas funciones siguiendo 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;
    }
}

pero recibo este error cuando intento construirlo

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)

¿Esto lo arreglará?

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

y pasar &address a __builtin_tblpage?

pero el atributo de espacio se usa para indicar al compilador que asigne una variable en espacios de memoria específicos, ¿entonces esto no puede ser correcto?

Realmente necesito un poco de ayuda aquí.

Gracias

pd también, ¿puedes ver el código y decirme qué podría estar haciendo mal?

Estás tratando de usar una memoria que no existe. Solo hay Flash bajo la dirección 0x2AFFE y está intentando acceder a 0x2B002. Ficha técnica página 42.
Pensé que el límite superior de la memoria del programa era 0x2AFFE, y después de esta dirección hasta 0x7FFFFF podría usarse para emular EEPROM.
No. No hay memoria de ningún tipo implementada en las direcciones entre 0x2AFFE y 0x800000 en su dispositivo; es por eso que la hoja de datos especifica "Lectura '0' no implementada" para ese rango. Si desea usar algo de Flash para emular EEPROM, debe usar Flash en el área 'User Flash Program Memory' entre 0x00000 y 0x2AFFE.
oh, ¿y si sobrescribo la página en la que está escrito el programa? esto es lo que me dice pickit cuando programo mi controlador con una versión anterior... Se programarán las siguientes áreas de memoria: memoria de programa: dirección inicial = 0x0, dirección final = 0x23ff Programación/Verificación completa
si declaro esta matriz y paso la dirección de la primera ubicación de memoria, el proyecto se compila... ¿es esta la forma correcta de hacerlo? const unsigned int __ atributo __ ((espacio(prog), dirección (0x6000))) tabla[10];
He convertido mis comentarios en una respuesta a continuación.

Respuestas (2)

Estás tratando de usar una memoria que no existe.
Solo hay Flash bajo la dirección 0x2AFFE y está intentando acceder a 0x2B002.
Consulte la página 42 de la Hoja de datos
. No hay memoria de ningún tipo implementada en las direcciones entre 0x2AFFE y 0x800000 en su dispositivo; es por eso que la hoja de datos especifica "Lectura '0' no implementada" para ese rango.
Si desea usar algo de Flash para emular EEPROM, debe usar Flash en el área 'User Flash Program Memory' entre 0x00000 y 0x2AFFE.

const unsigned int attribute ((space(prog), address (0x6000))) table[10];
es una forma perfectamente aceptable de hacerlo.
Ha hecho que el compilador asigne ese bloque de memoria a su matriz para que no coloque ningún código allí, lo que lo hace seguro para su uso.
Sin embargo, debe asegurarse de que la dirección y el tamaño que especifique para su matriz se alineen exactamente con uno o más de los 'Bloques de borrado' en el Flash de su PIC.
La hoja de datos le dice que el rango 0x2B000 se divide en 172 bloques, por lo que es 0x400 por bloque. La dirección de inicio y el tamaño de su matriz deben ser un múltiplo de este valor, por lo que, en su caso, debe cambiar el tamaño de su matriz.
Si su matriz no llena exactamente uno o más bloques, entonces el compilador es libre de usar el resto de un bloque para el código, pero dado que el PIC borrará un bloque completo cuando sea necesario para su emulación EEPROM, esto resultaría en algún código que se borra también.

Gracias, esto solucionó el problema, sin embargo, ahora cada vez que trato de leer la memoria, después de haberla escrito, solo obtengo unos. tal vez lo estoy escribiendo mal, o tal vez lo estoy leyendo mal?

Parece que la variable que pasa a las macros __builtin_tblpage es un byte (bit), mientras que necesita pasar un corto (16 bits)

Utilizo esta función para Microchip para escribir / leer el microchip PIC24EP, deberían ser bastante similares para usted.

/*********************************************************************
 * 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;
}

Y los uso como:

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();
    }
}

Y

#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;
}

Simplemente puede crear una estructura de sus datos y enviar un puntero a esas funciones.


Nota:

  • No puede escribir flash sin borrar primero una página completa. Fallaría o los datos se corromperían.
  • Necesitas escribir alineado en una página.