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.

LP-MSPM0G3507: Flashctl writes wrong data to flash and general flash behavior is "picky"

Part Number: LP-MSPM0G3507

Tool/software:

 Hi 

i am currently writing a NVM stack for the mentioned micro, where the lowest layer (Abstr_ * in my nomenclature) is a generic-ified version of your flashctl example.

I have had an issue relating to MSPM0L1306: MCU goes into Default Handler while writing in Flash Memory - Arm-based microcontrollers forum - Arm-based microcontrollers - TI E2E support forums

which seems to be attributed to the way i was accessing the flash (memcpy just like a for loop accessing the flash in array like fashion [see example 1 below] did cause trouble while access as in example 2 [see below] did not. it appears that any read to the flash has to initially align to a 4byte boundary though 

New is how ever that the written contents are not exactly as they should be consider the write Function in snippet 1 below and consider the example usage as shown in the screenshots below (note how the alignment of writes to 8byte boundaries is ensured!)

in this example nothing had been written yet and the flash at the locations in question is still erased as expected

notice how the 4 is not written as 4 but rather as C 

notice how the issue is reproducable in the 8bytes following the 1st ones

notice how the value C had been written as 4 here

notice how this is also reproducable

Please let me know how the data committed to flash can be ensured to match the data requested to be written. 

 

Example 1:

    volatile static  uint8_t serialnumberPCB[20] __attribute__((used));
    for(uint8_t i=0;i<20;i++)
    {
        /*
        eepromEntries_ProductionInfo[ProductionInfo_EntryEnumerator_SerialnumberPCB].contentsInNVM.asU8Ptr is defined as a union as following
         
        typedef union
        {
                void * const asVoidPtr;
                uint8_t * const asU8Ptr;
                uint16_t * const asU16Ptr;
                uint32_t * const asU32Ptr;
        } ValuePtr_t;
        via assigning (void*)(0x0001F00B) to the asVoidPtr member else where before the loop is called
        */
        serialnumberPCB[i] = eepromEntries_ProductionInfo[ProductionInfo_EntryEnumerator_SerialnumberPCB].contentsInNVM.asU8Ptr[i]; 
    }
  
Example 2:
    const uint8_t offsetInFlash2 = 8;//4;//12;//16//64;//68;
    DEBUG_VOLATILE static uint8_t flashContentsDifferentMemcopyAlgo[20] __attribute__((used));
    // vvvv This copy algorithm works even with the offsets that did not work for the first algorithm vvvvvv
    // vvvv This algorithm is what is used in the Abstr function below internally too since all the Abstr calls above worked it is assumed that the algorithm works for any offset for aslong as the offset is a multiple of 4 (eg offset 11 does not work [tested]) vvvvv
    const uint32_t startAddress = eepromBlocks[EEPROM_Block_Enumerator_ProductionInfo].blockBaseAddress + offsetInFlash2; //<eepromBlocks[EEPROM_Block_Enumerator_ProductionInfo].blockBaseAddress is a u32 set to 0x0001f000 else where
    const uint32_t endAddress = startAddress + 20;
    for(uint32_t address = startAddress; address < endAddress; address++)
    {
        flashContentsDifferentMemcopyAlgo[address-startAddress] = *((uint8_t*)address);
    }  

Snippet 1:
Success_t Abstr_EEPROM_writeDataEntryOfSize(Abstr_EEPROM_Block_t * const block, const uint16_t offsetWithinBlock, const uint16_t entrySize)
{
    assert(block != NULL);
    // definition of DEBUG_VOLATILE is done in project settings in compiler defined symbols as DEBUG_VOLATILE=volatile 
    // and inserted to get the variables to be observed not optimized away
    DEBUG_VOLATILE uint32_t checkStartAddress = block->blockBaseAddress + (uint32_t)offsetWithinBlock;
    // address for write must be 64bit aligned, but most values are less then 64bits wide thus the surrounding bytes must be checked if they are writable without erase too (assumption is that they have been changed, if they are unchanged the check would intrinsically succeed which is a desirable property...)
    checkStartAddress = ROUND_DOWN_TO_NEAREST_MULTIPLE_OF(checkStartAddress, 8);
    DEBUG_VOLATILE const uint32_t checkEndAddress = ROUND_UP_TO_NEAREST_MULTIPLE_OF(block->blockBaseAddress + (uint32_t)offsetWithinBlock + (uint32_t)entrySize, 8);
    // define below is defined as 1 at start of containing file. 
    #if EEPROM_LOCATION_ERASED_MEANS_FF == 1
       
        for(uint32_t address=checkStartAddress; address < checkEndAddress; address++) // while the writes can only be performed 64bit wise the read can be done at smaller sizes too thus do the check byte wise here
        {
            const uint8_t helperByteInFlash = *((uint8_t *) address);
            const uint8_t helperCurrentlyCheckedBufferArrayByte = block->blockData[address - block->blockBaseAddress];

            // command has passed; no need to exit the loop prematurely
            // determine if the value can be directly written here

            // only additional zeros can be created by programming; yet thinking of generating additional ones felt easier --> work with inverted values here
            // when inverted requested value OR-ed together with inverted current value yields the requested value then it can be written without prior erase
            const bool canValueBeWrittenDirectly = (((~helperByteInFlash) | (~helperCurrentlyCheckedBufferArrayByte)) == (~helperCurrentlyCheckedBufferArrayByte));

            if(canValueBeWrittenDirectly == false)
            {
                return Abstr_EEPROM_OperationNotPossible; // if one byte of the value can not be written without prior erase the entire multi byte value can not be written without prior erase. thus no check for the possibility of the other bytes is needed.
            }
            else
            {
                // until the current byte all bytes can be written without erase. no need to exit the loop prematurely
            }
        }

        // if this is reached the loop above has been completed ie the function has not been left prematurely ie. the value can be written directly
        DL_FlashCTL_unprotectSector(FLASHCTL, block->blockBaseAddress, DL_FLASHCTL_REGION_SELECT_MAIN);

        DL_FLASHCTL_COMMAND_STATUS gCmdStatus = DL_FLASHCTL_COMMAND_STATUS_FAILED; // pessimist approach

        DEBUG_VOLATILE const uint16_t dataBufferSourceIndex = checkStartAddress - block->blockBaseAddress;
        DEBUG_VOLATILE const uint16_t numOf32BitWordsToBeWritten = (checkEndAddress - checkStartAddress) / 4;

        gCmdStatus = DL_FlashCTL_programMemoryFromRAM(FLASHCTL, checkStartAddress, (uint32_t *) &block->blockData[dataBufferSourceIndex], numOf32BitWordsToBeWritten, DL_FLASHCTL_REGION_SELECT_MAIN);
        if(gCmdStatus != DL_FLASHCTL_COMMAND_STATUS_PASSED)
        {
            return Abstr_EEPROM_FlashAccessFailed;
        }
        else
        {
            // command has passed; no need to exit the loop prematurely
        }

        return Abstr_EEPROM_Success;

    #else
    #error option not implemented
    #endif
}
  • EDIT: 

    also let me know how the reading can be performed in a way that guarantees the MCU is not falling into the default handler

    i came up with a read function like in snippet 1 below and used that for the initial check in my write function -- which works, yet used standalone the code branches to the default handler still- interessting enough i use that same read function in my block read function that is called in the start of my minimal example from the original question (changed the implementation of the blockRead function that was already there to use the function from snippet 1) and while the initial readBlock in the beginning of my minimal example works, a subsequent readBlock somewhere in between my writes does not

    For the understanding of my snippets consider the round_up and round_down macros to be defined as 
    #define ROUND_UP_TO_NEAREST_MULTIPLE_OF(x,y) ((x)%(y)!=0?(((x)/(y))+1)*(y):(x))
    #define ROUND_DOWN_TO_NEAREST_MULTIPLE_OF(x,y) ((x)<(y)?0:(x)-((x)%(y)))
    Snippet 1:
    void HW_Flash_readBytes(uint32_t fromAddress, uint8_t *const toBuffer, const uint16_t count)
    {
        assert(toBuffer != NULL);


        const uint32_t startAddress = ROUND_DOWN_TO_NEAREST_MULTIPLE_OF(fromAddress, 4); // the flash can only be read with a loop of the form below when the read is initiated from an address that is aligned to a 4 byte boundary
        const uint32_t endAddress = fromAddress + (uint32_t)count;

        for(uint32_t address = startAddress; address < endAddress; address++)
        {
            volatile const uint8_t byteFromFlash = *((uint8_t*)address);
            if(address < fromAddress)
            {
                // since the address from which the read is initiated must be aligned to a 4 byte boundary the address may be less than the starting address --> the additional bytes are not of interest thus they should be ommitted
                // nothing to do here
            }
            else
            {
                // address is in the requested range -- store the byte in the buffer. the buffer must be guaranteed to be of sufficent size by the user.
                toBuffer[address - fromAddress] = byteFromFlash; // since the assignment only takes place when the address is greater or equal than the fromAdress the indexing here should always yield a valid array index.
            }
        }
    }
  • Hi, 

    also let me know how the reading can be performed in a way that guarantees the MCU is not falling into the default handler

    In datasheet, Table 8-4. Memory Organization, You can read Flash from ECC Uncorrected region: 0x0040.0000 to 0x0041.FFF8.

    In this region, FlashCTL won't check it's ECC value.

    You can declare an NMIhandler interrupt function like this, to hanlder the Flash ECC error during the reading.

    DEDERRADDR is the memory address that encountered an error. and DeadAddress is uint32_t.

    void NMI_Handler(void)
    {
        __asm("NOP");
        DeadAddress = SYSCTL->SOCLOCK.DEDERRADDR;
        __BKPT(0);
    }

    For other question, I won't review code here, but I did see data write errors occur. Please feel free to ask me question.

    Regards,

    Helic

  • yet as i neither perform a write with ECC (as per my write function), nor do i read via flashctl (as i did not find a read function in flashctl -- only a read and verify function which is not what i need) I dont see how this should have any impact on the issue.

  • Hi, 

    yet as i neither perform a write with ECC (as per my write function), nor do i read via flashctl (as i did not find a read function in flashctl -- only a read and verify function which is not what i need) I dont see how this should have any impact on the issue.

    I recommended to use ECC write function. 

    Any way to read Flash will cause NMI if there is ECC error.

    For other debugging suggestions, I recommend narrowing down the problem to the low layer driver. 

    Regards,

    Helic

  • SOLUTION:

    Writing with ECC was not an option due to substantially increased Flash usage with ECC 

    Resetting the ECC fault (if it occurs) in a custom NMI Handler like

    void NMI_Handler(void)

    {
        // reset the ECC Error that may occur during reading the flash in order to not cause falling to the default handler effectively deadloacking the system
        __asm("nop\r\n");
        SYSCTL->SOCLOCK.NMIICLR |= SYSCTL_NMIICLR_FLASHDED_MASK; // writing a one clears the fault...
    }
    mitigates the deadlock on fault (even after reset without flash overwrite) situation.
    reading from flash when no ECC is written must use the non ECC section or otherwise there will be phantom flash errors as the read from the regular section will do ecc regardless of if it had been previously written or not