This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

F021 flash API on TMS570LS3137 - Fapi_EraseSector doesn't always leave the sector erased

Dear friends,

We're plagued by an occasional issue with the flash API where, after attempting to erase a sector, the erase operation nominally succeeds but the sector is not in fact blank.

In particular, this happens when trying to erase sector 1 (0x8000-0xFFFF).  (Erasing sector 0 seems to work.)  Again, the actual call to erase the sector returns a success code.  However, the Fapi_doBlankCheck() immediately after fails on the first word of the sector (0x8000); the reported value is 0xe59ff018 (instead of the expected 0xffffffff).  If I simply skip the blank check, then the subsequent program call fails, with getFsmStatus() returning INVDAT (this makes sense if the sector wasn't actually erased).

This is what my code looks like.  At the point of failure, sector_address is 0x8000, sector_size is 0x8000, 

==========
// Try: Blank check, erase, blank check, give up.
for (int attempt = 0; attempt < 2; ++attempt) {
  // Fapi_doBlankCheck() on a whole sector takes 12+ms; do it in chunks.

  status->error_code = Fapi_Status_Success;
  for (uint32_t chunk = sector_address;
      chunk < sector_address + sector_size; ) {
    int length = Min<int>(1024, sector_address + sector_size - chunk);

    Fapi_FlashStatusWordType fsw;
    status->error_code = Fapi_doBlankCheck(
        reinterpret_cast<uint32 *>(chunk),
        length / sizeof(uint32), &fsw);
    if (Failed("Fapi_doBlankCheck failed", status)) {
        status->error_code = fsw.au32StatusWord[0]; // Bad address.
        if (attempt > 0) return; // Should have succeeded by now.
        break; // Before erase; now try erasing the sector.
    }

    chunk += length;
    WatchdogServiceExternal();
  }

  if (status->error_code == Fapi_Status_Success) break; // Blank!

  // First attempt: Issue the command to erase the sector.
  CPU_SR_ALLOC();
  CPU_INT_DIS(); // No interrupts while flashing!
  status->error_code = Fapi_issueAsyncCommandWithAddress(
  Fapi_EraseSector, reinterpret_cast<uint32 *>(sector_address));
  if (Failed("Fapi_EraseSector failed", status)) {
    CPU_INT_EN();
    return;
  }

  while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy)
    WatchdogServiceExternal(); // Wait for completion.
  status->error_code = Fapi_getFsmStatus();
  CPU_INT_EN();
  if (Failed("Erase operation failed", status)) return;

  Fapi_flushPipeline();  // Defensively flush after every erase.
}
==========

Like I said, this doesn't always seem to happen -- it comes and goes with unrelated code changes -- we're trying to narrow down what triggers this (whether it's related to the data being flashed, or the code doing the flashing, or what).  Meanwhile, is there anything that could cause this kind of behavior?

  • I should point out, just in case it's relevant:

    When the failure happens, we have used MEMSW=0x05 to swap the RAM and Flash memory blocks.  (This is to enable us to run a bootloader from RAM, including interrupt vectors, while updating the flash.)  I don't think I've ever seen it when flashing without MEMSW, but that could be coincidental.  We're still, of course, passing in logical flash addresses (0-based), not the swapped addresses.

    (I notice in another thread that Fapi_flushPipeline() is reported to perform dummy reads on fixed addresses, 0x000, 0x100, 0x200, 0x300.  In MEMSW mode, those would be mapped to RAM, not flash.  Not sure if that would be a problem.)

  • What is the value of FMSTAT register when the sector erase fails? 

    What version of the API are you using?

    The only failures I have seen are trying to issue an erase sector to an address not in the active bank (this condition does not generate any notification from the device.  FMSTAT would be 0), sector has not been enabled (FMSTAT indicates sector lock error, FMSTAT == 0x11), or trying to issue an erase command while the FSM is busy (FSM will ignore all erase and program commands in this state).

    Fapi_flushPipeline() is only valid when Flash is mapped to address 0.  With RAM and Flash swapped, you would need to do 2 dummy reads from Flash with remapped addresses at least 128 bytes apart to cause the pipeline to be flushed.

    Also, note that the F021 Flash API does not require interrupts to be disabled while performing operations.

  • Thanks for the quick followup!

    FMSTAT (this is what Fapi_getFsmStatus() returns, right?) is 0 after the sector erase.

    This is API version 1.51.0 (I'm re-downloading and diffing to double check this now).

    We definitely call Fapi_setActiveFlashBank with the correct value; I'll triple check that but I'm pretty confident.

    I will add my own FlushPipeline() that does the dummy reads.  (Does it matter which bank/sector they read from?)  However, that should not be required between Fapi_EraseSector and Fapi_doBlankCheck?

    And yeah, the interrupt disabling was a defensive measure added when debugging previous flash problems.  I should probably remove it.

  • Normally, flushing the pipeline is not needed between erase and blank check as the blank check function has a call to Fapi_flushPipeline(), but in this memory swapped scenario, you would need to explicitly flush the pipeline.

    I will look at modifying this behavior in a future version of the API.

    Also, you should be checking the FSM (using Fapi_checkFsmForReady() ) to see if it is busy before issuing an erase command if you are not sure about the FSM state.

  • OK, I replaced Fapi_flushPipeline() with my own custom FlushPipeline():

    =====
    uint32_t MaybeSwapAddress(uint32_t address) {
      // If MEMSW is set, map unswapped address into swapped space.
      // See TR 2.5.1.42, "Bus Matrix Module Control Register"
      if (BMMCR1_bit()->MEMSW == 0x5) {
        // See TR 2.3.2, "Memory Map Table"
        if (address >= 0 && address <= 0x00FFFFFF) {
          return address + 0x08000000;
        } else if (address >= 0x08000000 && address <= 0x0BFFFFFF) {
          return address - 0x08000000;
        }
      }
      // TODO(egnor): Protect sensitive areas?  (What's dangerous to read?)
      return address;
    }
    // Flush the flash pipeline.  (Fapi_flushPipeline() doesn't work with MEMSW.)
    void FlushPipeline() {
      const volatile uint8_t *flash =
          reinterpret_cast<uint8_t *>(MaybeSwapAddress(0));
      (void) flash[0x000];
      (void) flash[0x100];
      (void) flash[0x200];
      (void) flash[0x300];
    =====
    This gets called after every erase operation (before the re-verification).
    I have also added a defensive check on the FSM status:
    =====
              ...
              while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady)
                WatchdogServiceExternal();  // Wait for any previous operation.
              status->error_code = Fapi_issueAsyncCommandWithAddress(
                  Fapi_EraseSector, reinterpret_cast<uint32 *>(sector_address));
              ...
    =====
    Also, just to be certain, I call Fapi_setActiveFlashBank() before every erase-sector call.
    Finally, I have also verified that we are definitely using version 1.51.
    Alas, it still fails in exactly the same way.  I'll continue to sanity check all of my values...
  • Can you post a memory dump from 0xFFF87000 - 0xFFF873FF before and after issuing the erase command?

  • OK, I have verified the following variable values:

    fb = 0

    s = 1

    sector_address = 0x8000

    And I now do this defensive series of operations:

    =====

              while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady)
                WatchdogServiceExternal();  // Wait for any previous operation.
              status->error_code = Fapi_setActiveFlashBank(fb);
              if (Failed("Fapi_setActiveFlashBank failed", status)) return;
              // First attempt: Issue the command to erase the sector.
              status->error_code = Fapi_issueAsyncCommandWithAddress(
                  Fapi_EraseSector, reinterpret_cast<uint32 *>(sector_address));
              if (Failed("Fapi_EraseSector failed", status)) return;
              while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy)
                WatchdogServiceExternal();  // Wait for completion.
              status->error_code = Fapi_getFsmStatus();
              if (Failed("Erase operation failed", status)) return;
              FlushPipeline();  // Defensively flush after every sector erase.

    =====

    But yeah, still no dice.

  • Also a map file of where the API functions are located would be useful.

  • Before the erase command:

    (gdb) monitor mdw 0xFFF87000 0x100
    0xfff87000: 00000311 00000100 000a070a 00000000 00000000 00000000 00000000 00000000 
    0xfff87020: 00000000 00000000 0007ffe0 0007ffe0 00000000 00000000 0000007c 0000000f 
    0xfff87040: 00003fff 00fc807f 00c80001 00000000 00000000 00000000 00000000 00000000 
    0xfff87060: 00000000 00000000 00000000 000a0000 00000000 00000000 00000000 00005400 
    0xfff87080: 0000000b 01ec0091 01c00000 00000000 0000071b 00009000 00000004 0700010a 
    0xfff870a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff870c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff870e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87100: 00010104 00000103 00000000 00000000 00000000 00000000 0000000a 00000000 
    0xfff87120: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 
    0xfff87140: ffffffff 00000001 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87160: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87180: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff871a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff871c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff871e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87200: 00000001 00000c00 00000004 00000000 00002222 0000f000 00002a2a 000021a1 
    0xfff87220: 00000313 00000054 00001300 00000013 00000000 00001300 00000000 00000000 
    0xfff87240: 00000098 00009470 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87260: 00000000 00000000 012c00c8 01ec03e8 00000000 00000000 0000001f 00004500 
    0xfff87280: 00000000 00000000 00000002 00000000 00000000 00000000 00000000 00000000 
    0xfff872a0: 00000000 00000000 30a0d73b 00000000 00000000 000a000a 00030002 00000000 
    0xfff872c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff872e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87300: 00000000 00000000 050a070a 00000000 00000000 00000000 00000000 00000000 
    0xfff87320: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87340: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87360: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87380: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff873a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff873c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff873e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 

    And for good measure:

    (gdb) monitor mdw 0x8000 16
    0x00008000: 1130f2c0 0100f7fe fea7e0b7 68bbb2da 68fb73da f7fffe2a 460268fb f103030c 
    0x00008020: 46104619 f04f0206 f7fefdf4 4602683b 46106879 461af7fe fded4603 4618f7ff 
    (gdb) print/x sector_address
    $2 = 0x8000
    (gdb) print/x fb
    $3 = 0x0

    ===================

    After the erase command:

    (gdb) monitor mdw 0xFFF87000 0x100
    0xfff87000: 00000311 00000100 000a070a 00000000 00000000 00000000 00000000 00000000 
    0xfff87020: 00000000 00000000 0007ffe0 0007ffe0 00000000 00007fff 0000007c 0000000f 
    0xfff87040: 00003fff 00fc807f 00c80001 00000000 00000000 00000000 00000000 00000000 
    0xfff87060: 00000000 00000000 00000000 000a0000 00000000 00000000 00000000 00005400 
    0xfff87080: 0000000b 01ec0091 01c00000 00000000 0000071b 00009000 00000004 0700010a 
    0xfff870a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff870c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff870e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87100: 00010104 00000103 00000000 00000000 00008000 00000000 0000000a 00000000 
    0xfff87120: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 
    0xfff87140: ffffffff 00000001 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87160: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87180: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff871a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff871c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff871e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87200: 00000001 00000c00 00000004 00000006 00002222 0000f000 00002a2a 000021a1 
    0xfff87220: 00000313 00000054 00001300 00000013 00000000 00001300 00000000 00000000 
    0xfff87240: 00000098 00009470 00000000 00000000 00000000 00000000 00000000 00000001 
    0xfff87260: 00000000 00000000 012c00c8 01ec03e8 00000000 012c03e8 0000001f 00004500 
    0xfff87280: 00000000 00000000 00000002 00000000 00000000 00000000 00000000 00000000 
    0xfff872a0: 0000fff0 00000011 30a0d73b 00000000 00000000 000a000a 00030002 00000000 
    0xfff872c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff872e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87300: 00000000 00000000 050a070a 00000000 00000000 00000000 00000000 00000000 
    0xfff87320: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87340: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87360: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff87380: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff873a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff873c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    0xfff873e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    For bonus purposes:
    (gdb) monitor mdw 0x8000 16
    0x00008000: 1130f2c0 0100f7fe fea7e0b7 68bbb2da 68fb73da f7fffe2a 460268fb f103030c 
    0x00008020: 46104619 f04f0206 f7fefdf4 4602683b 46106879 461af7fe fded4603 4618f7ff 
    (gdb) print status->error_code
    $11 = {data = 0}
    Here are addresses of [_]Fapi* functions from the output linker map:
                    0x0000000000006aa8                Fapi_serviceWatchdogTimer
                    0x0000000000006ab8                Fapi_setupEepromSectorEnable
                    0x0000000000006bac                Fapi_setupBankSectorEnable
                    0x0000000000014520                Fapi_issueProgrammingCommand
                    0x000000000001464c                Fapi_getDeviceInfo
                    0x00000000000146a0                Fapi_getBankSectors
                    0x0000000000014748                Fapi_issueAsyncCommandWithAddress
                    0x0000000000014770                Fapi_doVerify
     .text:_Fapi_loopRegionForValue
                    0x0000000000014782                _Fapi_loopRegionForValue
                    0x0000000000014832                Fapi_flushPipeline
     .text:_Fapi_checkRegionForValue
                    0x0000000000014854                _Fapi_checkRegionForValue
                    0x000000000001495c                Fapi_doBlankCheck
     .text:_Fapi_scaleCycleValues
                    0x00000000000149d8                _Fapi_scaleCycleValues
     .text:_Fapi_divideUnsignedLong
                    0x00000000000149e8                _Fapi_divideUnsignedLong
                    0x0000000000014a08                Fapi_calculateFletcherChecksum
     .text:_Fapi_calculateOtpChecksum
                    0x0000000000014a32                _Fapi_calculateOtpChecksum
                    0x0000000000014a3c                Fapi_waitDelay
                    0x0000000000014a6c                Fapi_calculateEcc
                    0x0000000000014a84                Fapi_initializeAPI
     .text:_Fapi_setupSectorsForWrite
                    0x0000000000014ad0                _Fapi_setupSectorsForWrite
     .text:_Fapi_issueFsmCommand
                    0x0000000000014b14                _Fapi_issueFsmCommand
     .text:_Fapi_setupFlashStateMachine
                    0x0000000000014e68                Fapi_setActiveFlashBank
                    0x0000000000014e94                Fapi_getFsmStatus
                    0x0000000000014ea0                Fapi_isAddressEcc
                    0x0000000000014ef0                Fapi_checkFsmForReady
                    0x000000000002c698                Fapi_GlobalInit
    I'll respond to you out-of-thread to see if there's more data I can get you.

  • Update:

    Turns out a large part of my confusion is that doBlankCheck() doesn't seem to support MEMSW.  The sector has actually been erased for a while -- but doBlankCheck() was failing because of MEMSW.  (When I pass doBlankCheck() a swapped address, it works.  What would you recommend?  Pass it a swapped address, or roll my own blank check?)

    Nevertheless, my original problem was a real problem; it surfaced as the INVDAT error at program time; doBlankCheck() was only added for diagnosis purposes.  However, this invalidates much of my previous "I made these changes, and it still fails..." debugging.  So I'm starting back at square 1, and trying to reproduce the problem.  It's possible that the pipeline-flush workaround may have solved the original issue.

    I think it's safe to say that the situation when using the F021 library in MEMSW mode is confusing, at least :-)

  • Hello Dan,

    I apologize as I should have snapped to the addresses thing sooner as I had to handle that in my memory swapped code.  Programming and erasing are done through the registers in the FMC which requires use of the absolute address, whereas reading from Flash goes through the bus which would require using the memory mapped addresses.  Therefore if you had passed the memory mapped address to Fapi_doBlankCheck(), it would have worked on the range you selected.  In my code, I do the following:

                    if(u32StartAddress < 0xF0000000)

                    {

                                    u32StartAddress |= 0x08000000;

                    }

    as only the main Flash Addresses are remapped for read.  Bank 7, Bank 7 ECC, Customer OTP, Customer OTP ECC and main Flash ECC still use their non-swapped addresses.

     As you found out, to truly do a blank check, you need to check both the main memory addresses and the corresponding ECC addresses and for those checks to be completely correct, you at a minimum need to disable ECC correction before performing these operations.  As main array and its ECC are on the same row, you could theoretically just do a blank check on the ECC region, but as address is included in the ECC calculation, 1 out of every 256 addresses, 0xFF is an valid ECC value.