dsPIC33 Flash Erase broken

396 views Asked by At

I am having a lot of trouble when it comes to flash erasing on the dsPIC33EP64GP503 and I am hoping someone on here will be able help.

I am wanting to store a data struct in the flash program memory of the device. I am having trouble when it comes to erasing the flash though. I need to erase it and re-write it when the data changes.

I am padding the rest of the page with 0s so it can be safely erased.

I can write to the same memory location of the struct. When doing a flash write onto the start of the struct, the byStructValid turns into 0x11 (I know this is all very bad, because it is writing double word. But I am just trying to get the flash operations working first), however when I do an erase nothing happens. Is someone able to figure out what I am doing wrong?

I initialised the struct with 0xFF's and tried to perform a flash write. This was successful as the CAN message I received showed the data changed from 0xFF to 0x11.

I then tried to do a flash erase, but nothing happened. The device just carried on as normal. I don't have access to debug so it is hard to fully understand what is going on during this time.

I have tried moving the struct location around, so that it is on an 'even' page boundary (as specified in the datasheet) but this hasn't worked either. I have also tried using an assembly version of the erase function, provided by the datasheet, this also doesn't work. The device just carries on as though there was no command for flash erase.

Below are some snippets of code that I have been using.

Any help would be greatly appreciated, thank you.

Note: I am unable to use the debugger. I use CAN messages to periodically send ‘debug’ messages, which contain data that is read from the flash location. This is so I can see if the write/erases are working.

#define MEMORY_USER_CALIBRATION_LOC 0x006000

typedef struct
{
    byte byStructValid;
    byte abyStructData[3];
}stFlashStruct_t;

volatile const __prog__ stFlashStruct_t stFlashStruct __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC))) =
{   
    .byStructValid = 0xFF,  
    .abyStructData = {50, 10, 20},
};

const byte padding[_FLASH_PAGE*2 - sizeof(stFlashStruct_t)] __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC + sizeof(stFlashStruct_t)))) = {0};

//FLASH Write
void FLASH_WriteDoubleWord(dword address, dword data[2])
{
    word INTCON2Save;
    word i;
    
    //set WREN and ERASE settings for operation
    NVMCON = 0x4001;
    TBLPAG = 0xFA;
    
    //set address to erase
    NVMADR = address & 0xFFFF; 
    NVMADRU = (address >> 16) & 0x3F; 
    
    for (i = 0; i < 2; i++)
    {
        __builtin_tblwtl(i*2, data[i] & 0xFFFF);
        __builtin_tblwth(i*2, (data[i] >> 16) & 0xFF);
    }
    //save the interrupt register
    INTCON2Save = INTCON2;
    
    // Disable interrupts for NVM unlock 
    __builtin_disable_interrupts(); 
    __builtin_write_NVM();
    
    // Start write cycle 
    while(NVMCONbits.WR == 1); 
    
    //restore interrupts
    INTCON2 = INTCON2Save;
}

//FLASH Erase
void FLASH_ErasePageC(dword dwAddress)
{
    word INTCON2Save;
    
    //set WREN and ERASE settings for operation
    NVMCON = 0x4003;
    
    //set address to erase
    NVMADRU = (dwAddress >> 16) & 0x3F; 
    NVMADR = dwAddress & 0xFFFF; 
    
    //save the interrupt register
    INTCON2Save = INTCON2;
    
    __builtin_disable_interrupts(); 
    
    // Disable interrupts for NVM unlock 
    __builtin_write_NVM();
    
    // Start write cycle 
    while(NVMCONbits.WR == 1); 
    
    //restore interrupts
    INTCON2 = INTCON2Save;
}

byte temp_flash_write(void)
{
    dword new_data[2] = {0x1111, 0x1111};
    
    FLASH_WriteDoubleWord(&stCustomerCalibration, new_data);
    return 0;
}
1

There are 1 answers

1
Dan1138 On

Your "dsPIC33 Flash Erase broken" issue is one of not understanding just how badly the Run Time Flash Programming (RTFP) method is described in the Microchip dsPIC33EP64GP503 data sheet and family reference manuals.

This post will not explain how any of this works. It does work but is really hard to comprehend.

What will be hard for you is that a program flash word can only be written one time after an erase. Writing to the same program flash word a second time will corrupt it and the next time it is read an ECC trap error will assert.

Attached is example code that allocates a 1024 instruction word page at address 0x6000. Declares a structure at the start of that page that is 2 instruction words in size. The code then erases that page then writes different data to the first 2 instruction words in that page.

/*
 * File:   main.c
 * Author: Dan1138
 *
 * Description:
 *  Example for Run Time Self Programming (RTSP).
 *  This is very limited, useful as a test bench but not much more.
 * 
 * Created on December 10, 2022, 2:05 PM
 */

/* Define the system oscillator frequency this code must configure */
#define FSYS (7372800ul)
#define FCY  (FSYS/2ul)

// DSPIC33EP64GP503 Configuration Bit Settings

// 'C' source line config statements

// FICD
#pragma config ICS = PGD1               // ICD Communication Channel Select bits (Communicate on PGEC1 and PGED1)
#pragma config JTAGEN = OFF             // JTAG Enable bit (JTAG is disabled)

// FPOR
#pragma config ALTI2C1 = OFF            // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF            // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25           // Watchdog Window Select bits (WDT Window is 25% of WDT period)

// FWDT
#pragma config WDTPOST = PS32768        // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128           // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON              // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)

// FOSC
#pragma config POSCMD = NONE            // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = ON            // OSC2 Pin Function bit (OSC2 is general purpose digital I/O pin)
#pragma config IOL1WAY = OFF            // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD           // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)

// FOSCSEL
#pragma config FNOSC = FRC              // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config IESO = ON                // Two-speed Oscillator Start-up Enable bit (Start up device with FRC, then switch to user-selected oscillator source)

// FGS
#pragma config GWRP = OFF               // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF                // General Segment Code-Protect bit (General Segment Code protect is Disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <libpic30.h>

#define MEMORY_USER_CALIBRATION_LOC (_FLASH_PAGE * 24)

typedef struct
{
    uint8_t byStructValid;
    uint8_t abyStructData[3];
} stFlashStruct_t;

volatile const __prog__ __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC))) union 
{
    uint16_t words[_FLASH_PAGE];    /* reserve the entire erase page. Note only the low 16-bits of the instruction word can be accessed with this method. */
    struct {
        stFlashStruct_t stFlashStruct;  /* calibration structure */
    };
} CalSpace =
{   
    .stFlashStruct.byStructValid = 0xFF,  
    .stFlashStruct.abyStructData = {50, 10, 20},
};

int main(void) 
{
    volatile stFlashStruct_t ReadBack;
    
    /*
     * application initialization
     */
    ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
    ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
    ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
    ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
    __builtin_software_breakpoint();  /* breakpoint here to inspect the ReadBack structure with the debugger */
    Nop();
    Nop();
    
    /* Erase 1024 instruction words starting at address MEMORY_USER_CALIBRATION_LOC */
    NVMCON = 0x4003;
    NVMADR = __builtin_tbloffset(&CalSpace);
    NVMADRU = __builtin_tblpage(&CalSpace);
    __builtin_disi(5);                          // Disable interrupts for NVM unlock
    __builtin_write_NVM();                      // Start write cycle
    while(NVMCONbits.WR == 1);    

    ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
    ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
    ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
    ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
    __builtin_software_breakpoint();  /* breakpoint here to inspect the ReadBack structure with the debugger */
    Nop();
    Nop();
    
    /* Update data in structure to be written */
    ReadBack.byStructValid = 1;
    ReadBack.abyStructData[0] = 2;
    ReadBack.abyStructData[1] = 3;
    ReadBack.abyStructData[2] = 4;

    /* Write 2 instruction words starting at address MEMORY_USER_CALIBRATION_LOC */
    NVMCON = 0x4001; // Set WREN and word program mode
    TBLPAG = 0xFA; // write latch upper address
    NVMADR = __builtin_tbloffset(&CalSpace.stFlashStruct);
    NVMADRU = __builtin_tblpage(&CalSpace);
    __builtin_tblwtl(0,*((uint16_t *)(&ReadBack)+0));   // load low 16-bits of first instruction word
    __builtin_tblwth(0,0x00);                           // make high 8-bits of first instruction word zero
    __builtin_tblwtl(2,*((uint16_t *)(&ReadBack)+1));   // load low 16-bits of second instruction word
    __builtin_tblwth(2,0x00);                           // make high 8-bits of second instruction word zero
    __builtin_disi(5); // Disable interrupts for NVM unlock sequence
    __builtin_write_NVM(); // initiate write
    while(NVMCONbits.WR == 1);
    
    ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
    ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
    ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
    ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
    __builtin_software_breakpoint();  /* breakpoint here to inspect the ReadBack structure with the debugger */
    Nop();
    Nop();

    /*
     * Application process loop
     */
    for(;;)
    {
        Nop();
        Nop();
        Nop();
        __delay_ms(100);
    }
}