This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

LPC2000 IAP, programed data in flash are not consistent with data in RAM buffer.

Hi, Folks,

Hopefully, someone can shed some light to this. Thanks.

I am using the LPC2000 IAP functions to put user data in to flash, but I got a weird problem. My target MCU is a LPC2214 and debugger is the Keil uvision 3.05 + ULink2. I have a MSB2100 evaluation board but it's broken, so I cannot test this on it.

The following is the test program. For each user, I read the 512 bytes block the user should be in to a RAM buffer, update the user data and then program the buffer back in.

The problem is the programed data are often not consistent with the data in the RAM buffer. I programmed 8000 users, usually this will happen more then 1000 times. The following example is one of them. The user number 7 was wrong, and this happened when user 8 was programmed. I had erased this section before I ran this function and checked all data in the section were 0xFF. In the whole process, the IAP functions return 0 (CMD_SUCCESS)

d 0x10000
0x00010000 01 00 00 00 10 FF FF FF
0x00010008 02 00 00 00 10 FF FF FF
0x00010010 03 00 00 00 10 FF FF FF
0x00010018 04 00 00 00 10 FF FF FF
0x00010020 05 00 00 00 10 FF FF FF
0x00010028 06 00 00 00 10 FF FF FF
0x00010030 07 00 02 00 10 FF FF FF
0x00010038 08 00 00 00 10 FF FF FF
0x00010040 FF FF FF FF FF FF FF FF
0x00010048 FF FF FF FF FF FF FF FF
.......ALL oxFF

d flash_buffer
0x4000021C 01 00 00 00 10 FF FF FF
0x40000224 02 00 00 00 10 FF FF FF
0x4000022C 03 00 00 00 10 FF FF FF
0x40000234 04 00 00 00 10 FF FF FF
0x4000023C 05 00 00 00 10 FF FF FF
0x40000244 06 00 00 00 10 FF FF FF
0x4000024C 07 00 00 00 10 FF FF FF
0x40000254 08 00 00 00 10 FF FF FF
0x4000025C FF FF FF FF FF FF FF FF
0x00000264 FF FF FF FF FF FF FF FF
.......ALL oxFF

//////////////my test program//////////////
typedef struct
{
        unsigned long   id;
        BYTE    zone;
} USER_DATA;

unsigned char flash_buffer[512];

//the start address of users in flash (64k block)
USER_DATA *user = (USER_DATA *)0x10000;
unsigned long blockerrors = 0;
unsigned long singleerrors = 0;

void test_user()
{
  unsigned long i;
  unsigned char *program_addr;
  USER_DATA temp_user;

  for(i = 1; i <= 8000, i++)
  {
    //get the 512 bytes block base address the current user is in.
        program_addr =(BYTE*)((unsigned int)(&user[i]) & 0xFFFFFE00);

        //update the user in the buffer.
        memcpy((void*)flash_buffer, (void*)program_addr, 512);
        temp_user = (USER_DATA*)(flash_buffer + (((BYTE*)(user + i)) - program_addr));
        temp_user->id = i;
        temp_user->zone = 16;

    //program the flsh
        FlashProgram((uint)program_addr, flash_buffer, 512);

        if(memcmp(flash_buffer, program_addr, 512)) //check block programming errors
                  ++blockerrors;

          if(i != user[i].id) //
                  ++singleerrors;

                DELAY(100); //delay 100 millisends
  }
}

#define XTAL_HZ    10000000                   // Oscillator Frequency in HZ
#define PLL_MUL 6
#define VPB_DIVIDER 4              // VPB Clock divider
#define CPU_CLK_HZ (XTAL_HZ*PLL_MUL)                    // CPU Clock (cclk) in HZ

#define CPU_CLK_KHZ   (CPU_CLK_HZ / 1000)          // CPU Clock (cclk) in kHz

#define VPB_CLK_HZ   (CPU_CLK_HZ / VPB_DIVIDER)              // VPB Clock (pclk) in Hz


struct iap_in {
  uint cmd;
  uint par[4];
};

typedef void (*IAP)(struct iap_in *in, uint *result);
#define IapEntry ((IAP) 0x7FFFFFF1)       // IAP Entry Point

uint FlashProgram (uint addr, uchar *data, uint size)
{
        struct iap_in iap;                       // IAP Input Parameters
        uint  result[16];                // IAP Results

        assert(FlashGetSecNum (addr) >= 8);

        di();                                    // disable all interrupts


        iap.cmd = 50;                            // IAP Command: Prepare Sectors for Write
        iap.par[0] = FlashGetSecNum (addr);         // Start Sector
        iap.par[1] = iap.par[0];                 // End Sector
        IapEntry (&iap, result);                // Call IAP Function
        if (result[0])
                goto exit;                // Error occured?

        iap.cmd = 51;                            // IAP Command: Copy RAM to Flash
        iap.par[0] = addr;                       // Destination Address
        iap.par[1] = (uint) data;        // Source Address
        iap.par[2] = size;                       // Number of Bytes
        iap.par[3] = CPU_CLK_KHZ;                       // CPU Clock
        IapEntry (&iap, result);                // Call IAP Function

exit:
        ei();                                    // enable all interrupts
        return (result[0]);
}

  • Are you trying to wear out the flash? You are aware that you may only write a limited number of times to each memory block?

  • You should certainly pay attention to Per's warning about wearing-out the flash...

       FlashProgram( (uint)program_addr, flash_buffer, 512);
    
       if( memcmp(flash_buffer, program_addr, 512) ) //check block programming
    


    Leaving aside any questions about your pointer casting, are you sure that it's valid to read the flash immediately after programming it? Do you need to wait for the operation to complete...?

  • Hi Per,

    I am new to flash. To my understanding, one flash erase-rewrite cycle means you can erase the section once and rewrite it multiple times. So I thought I was not wearing out the flash. Please point out if I am wrong.

    Thanks,
    Leo

  • Hi Andy,

    Even if I waited 1 minute before reading the flash I got the same problem.

    Thanks
    Leo

  • "To my understanding, one flash erase-rewrite cycle means you can erase the section once and rewrite it multiple times"

    Not exactly.

    Erasing an EPROM cell (whether Flash or not) sets it to a '1'.

    Thus, erasing a section of EPROM sets all cells (ie, bits) in that section to '1'

    To program it, you write '0' to those cells (bits) that you don't want to be '1'

    The only way to set a cell back to '1' is to erase it again.

    Thus, after an erase cycle, you can only ever change bits from '1' to '0' - you cannot "write" a '1' to a bit that has been '0'

    Maybe this is the source of your problem?

  • Thus, after an erase cycle, you can only ever change bits from '1' to '0' - you cannot "write" a '1' to a bit that has been '0'

    And each sector of flash ROM can only take high, but limited number of erase cycles (usually, the manufacturers guarantee a number between 10^4 and 10^6). Once you get past that number, there is no guarantee that any following erase or write operations will be successful.

  • The number of write cycles are very much affected by temperature.

  • "Once you get past that number, there is no guarantee that any following erase or write operations will be successful."

    Also, even if the write appears successful initially, there is no guarantee of how long the data will remain valid...

  • This forum has it's glitches. Forgetting to save the posted text is one of them...

    Anyway, I think it is better to always write full FLASH pages. If you get a problem with a full page, then you have the contents of that page available and can decide if you want to try again, or possibly use a different page as "spare".

    If your code constantly adds a couple of bytes at a time and there is a timing glitch somewhere in the bootloader code, then the probability of you getting hit by the glitch will increase.

    When storing log information or similar to the FLASH, i.e. the data gets available in small blocks, then it may be a good idea to define all zero and all one bytes as NULL-information. This may be used to inform the program that a couple of bytes does not work as expected, and that the software should ignore these bytes - or possibly skip to the next sector. It isn't totally foolproof, but may increase the usability of the chip.

  • "usually, the manufacturers guarantee a number between 10^4 and ..."

    Remember that these are statistical measures - it doesn't mean that everything will be fine up to cycle number 9,999 and fail completely on cylce 10,000...

  • To program it, you write '0' to those cells (bits) that you don't want to be '1'

    The only way to set a cell back to '1' is to erase it again.

    Thanks, Andy.

    I was aware of this, so only add users at blank area, that is, the Oxff area. Once write the user in I will never change it until erase the whole section unless I want to delete it. when deleting an user, I will set the user id to 0, which is defined as an invalid user. When I want to change the user zone information, I just delete this user, change the zone and add the user to the blank area.

    I said "write multiple times", I mean the section can be written by small blocks, not just write it in a whole section manner.

    Leo

  • Anyway, I think it is better to always write full FLASH pages. If you get a problem with a full page, then you have the contents of that page available and can decide if you want to try again, or possibly use a different page as "spare".

    If your code constantly adds a couple of bytes at a time and there is a timing glitch somewhere in the bootloader code, then the probability of you getting hit by the glitch will increase.

    When storing log information or similar to the FLASH, i.e. the data gets available in small blocks, then it may be a good idea to define all zero and all one bytes as NULL-information. This may be used to inform the program that a couple of bytes does not work as expected, and that the software should ignore these bytes - or possibly skip to the next sector. It isn't totally foolproof, but may increase the usability of the chip.

    Hi Per,

    We add users from time to time and not in batches, so it's impossible to get a page of users and write them in to flash. If I use one page for one user I will get fired. lol. So I have to choose to update the flash in small blocks.

    I did what exactly you said to use 8 bytes blocks. And I wanted to correct any programming errors by deleting the wrong user and add it again at the blank area. But you may have already found in my example, when I was writting user 8, the user 8 was ok but the user 7 was changed. It just so hard for me to trace all the old users which one is wrong. So I want to figure out what is the root cause of the inconsistency problem. I guess something wrong with the small blocks writing, but I don't know exactly what's wrong.

    I don't think I have damaged my flash section, because I have only erased it less than 100 times, and everything is ok if I update it in a whole section manner.

    Leo

  • Your data source and destination address to IAP should be Long alligned. Add an Long allignment clause to your data buffer.

    This can be one of the reason for your problem.

    Regards

    Suvidh

  • The flash has an error correction mechanism which works in blocks of 16 bytes. So if you write 8 bytes first and another 8 bytes later, the error correction data is programmed wrong resulting in wrong data.


  • The flash has an error correction mechanism which works in blocks of 16 bytes. So if you write 8 bytes first and another 8 bytes later, the error correction data is programmed wrong resulting in wrong data.

    Thanks,

    I believe this is the problem. I found an explaination about this in a lpc discussion group.
    tech.groups.yahoo.com/.../2681

    Leo