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.

TMS320F28377S: Unable to write to OTP (LINKPOINTER1-3, GRABRAM, GRABSECT, CSMPASSWORD ...)

Part Number: TMS320F28377S


I need to lock the MCU memories (RAM and FLASH) by the FW running on the MCU. The idea is that after the product is built, it is powered up and a special command is transmitted to the product in order to configure the 0x78000 (OTP) addresses with linkpointer, affected memory areas and a password.

I wrote a code that does this and it's working well while I am running the code in a debug session. But as soon as I stop the debug session and power-cycle the unit the writing to OTP does not work any more. To be specific, the function Fapi_issueProgrammingCommand(...) returns Fapi_Status_Success, but the content of the written addresses is unchanged. Please check my simplified code snippet below. I've attached just the 3 important functions: SetupFlashLock(), UnlockMemory() and WriteToAddressAndCheck().

void SetupFlashLock(void)
{
    uint32_t *  zone1SelBlock_ptr;
    int16_t     zone1SelBlock_index = -1;
    uint32_t    newData[8+5];

    TP_SendChar(0x01); /*#DEL debugging */

    /* Get pointer to currently used Zone Block */
    zone1SelBlock_ptr = GetCurrentZonePointer(&zone1SelBlock_index);

    TP_SendChar(0x02); /*#DEL debugging */

    /* Determine new linkPointer value and zone1SelBlock_ptr */
    uint32_t linkPtrValue = 0xFFFFFFFF;
    zone1SelBlock_ptr = GetNewZonePointer(zone1SelBlock_index, &linkPtrValue);

    /* New LinkPointer data structure */
    newData[0] = linkPtrValue;
    newData[1] = 0;
    newData[2] = linkPtrValue;
    newData[3] = 0;

    /* Unlock memory */
    if (UnlockMemory()) /* ... see function implementation below */
        TP_SendChar(0x03); /*#DEL debugging */

    /* Program new LinkPointer1 & LinkPointer2 Values (ECC is not programmed) */
     /* ... see function implementation below */
    if (WriteToAddressAndCheck((uint32_t *)0x78000,
                               (uint16_t *)&newData,
                               8,
                               0,
                               0,
                               Fapi_DataOnly,
                               10000))
    {
        TP_SendChar(0xF1); /*#DEL debugging */

        /* Program new LinkPointer3 Value (ECC is not programmed) */
         /* ... see function implementation below */
        if (WriteToAddressAndCheck((uint32_t *)0x78008,
                                   (uint16_t *)&newData,
                                   4,
                                   0,
                                   0,
                                   Fapi_DataOnly,
                                   10000))
        {
            TP_SendChar(0xF2); /*#DEL debugging */

            /* New data */
            newData[0] = 0x000000FF;  /* Zx-EXEONLYRAM */
            newData[1] = 0x00007FFF;  /* Zx-EXEONLYSECT */
            newData[2] = 0x10005555;  /* Zx-GRABRAM */
            newData[3] = 0x15555555;  /* Zx-GRABSECT */
            newData[4] = D_FLASH_LOCK_KEY0;  /* Zx-CSMPSWD0 */
            newData[5] = D_FLASH_LOCK_KEY1;  /* Zx-CSMPSWD1 */
            newData[6] = D_FLASH_LOCK_KEY2;  /* Zx-CSMPSWD2 */
            newData[7] = D_FLASH_LOCK_KEY3;  /* Zx-CSMPSWD3 */

            /* Program new ZONE SECTORS */
             /* ... see function implementation below */
            if (WriteToAddressAndCheck(zone1SelBlock_ptr,
                                       (uint16_t *)&newData,
                                       8,
                                       0,
                                       0,
                                       Fapi_AutoEccGeneration,
                                       10000))
            {
                TP_SendChar(0xF3); /*#DEL debugging */

                /* Program new PASSWORD */
                 /* ... see function implementation below */
                if (WriteToAddressAndCheck(zone1SelBlock_ptr + 4,
                                           (uint16_t *)&newData[4],
                                           8,
                                           0,
                                           0,
                                           Fapi_AutoEccGeneration,
                                           10000))
                {
                    TP_SendChar(0xF4); /*#DEL debugging */
                }
            }
        }
    }
    
    TP_SendChar(0xF5); /*#DEL debugging */

    /* Reset MCU */
    ResetMcu();
}


bool UnlockMemory(void)
{
    bool                result      = false;
    volatile long int * CSM         = (volatile long int *)0x5F010; //CSM register file
    uint32_t *          CSMPWL;     //CSM Password location (assuming default Zone sel block)
    int16_t             zoneIndex   = -1;
    int                 temp;
    uint16_t            status;

    /* Get pointer to PASSWORD in the currently used Zone Block */
    CSMPWL = GetCurrentZonePointer(&zoneIndex) + 4;

    DCSM_CSMPasswordKey password;
    password.csmKey3 = D_FLASH_LOCK_KEY3;
    password.csmKey2 = D_FLASH_LOCK_KEY2;
    password.csmKey1 = D_FLASH_LOCK_KEY1;
    password.csmKey0 = D_FLASH_LOCK_KEY0;

    /* Disable interrupts */
    M_SYS_SMEM_ACCESS_START;

    /* TI recommended dummy read of the CSM password locations (PWL) */
    for (int i = 0; i < 4; i++)
        temp = *CSMPWL++;

    EALLOW;

    /* Note: If the password locations (CSMPWL) are all = ones (0xFFFF),
     *       then the zone will now be unsecure. If the password
     *       is not all ones (0xFFFF), then the code below is required
     *       to unsecure the CSM.
     *       Write the 128-bit password to the CSMKEY registers
     *       if this password matches that stored in the
     *       CSLPWL then the CSM will become unsecure. If it does not
     *       match, then the zone will remain secure. */
    *CSM++ = password.csmKey0; // Register Z1_CSMKEY0 at 0x5F010
    *CSM++ = password.csmKey1; // Register Z1_CSMKEY1 at 0x5F012
    *CSM++ = password.csmKey2; // Register Z1_CSMKEY2 at 0x5F014
    *CSM++ = password.csmKey3; // Register Z1_CSMKEY3 at 0x5F016

    EDIS;

    /* Check lock status */
    status = HWREGH(DCSM_Z1_BASE + DCSM_O_Z1_CR);

    /* Enable interrupts */
    M_SYS_SMEM_ACCESS_END;

    /* if ARMED bit is set and UNSECURED bit or ALLONE bit or both UNSECURED
     * and ALLONE bits are set then CSM is unsecured. Else it is secure. */
    if(          ((status & DCSM_Z1_CR_ARMED)    != 0U)
             && (((status & DCSM_Z1_CR_UNSECURE) != 0U)
             ||  ((status & DCSM_Z1_CR_ALLONE)   != 0U))   )
    {
        /* returnStatus = DCSM_STATUS_UNSECURE; */
        result = true;
    }

    return result;
}


bool WriteToAddressAndCheck
   (uint32_t *      destination_ptr,
    uint16_t *      sourceData_ptr,
    uint16_t        length,
    uint16_t *      u16EccBuffer_ptr,
    uint16_t        u16EccBufferSizeInBytes,
    Fapi_FlashProgrammingCommandsType oMode,
    uint16_t        timeout)
{
    bool result = false;

    TP_SendChar(0x11); /*#DEL debugging */

    Fapi_StatusType fapiStatus = Fapi_initializeAPI(F021_CPU0_W0_BASE_ADDRESS, 200);
    if (fapiStatus == Fapi_Status_Success)
    {
        /* Disable interrupts */
        M_SYS_SMEM_ACCESS_START;

        /* Program new values */
        fapiStatus = Fapi_issueProgrammingCommand(destination_ptr,
                                                  sourceData_ptr,
                                                  length,
                                                  u16EccBuffer_ptr,
                                                  u16EccBufferSizeInBytes,
                                                  oMode);

        /* Wait until the FLASH API has completed the program sector operation */
        while ((Fapi_checkFsmForReady() != Fapi_Status_FsmReady) && (timeout-- > 0));

        /* Enable interrupts */
        M_SYS_SMEM_ACCESS_END;
    }

    /* Check destination address for new content */
    bool dataValid = CompareContent((uint16_t *)destination_ptr, sourceData_ptr, length);

    /* Check timeout and result */
    if ((timeout > 0) && (fapiStatus == Fapi_Status_Success) && (dataValid == true))
    {
        result = true;
        TP_SendChar(0x1F); /*#DEL debugging */
    }
    else
    {
        TP_SendChar(0x1E); /*#DEL debugging */
    }

    return result;
}

Trough out the code I have placed several debugging points in order to see which part of the code gets executed when I am not running a debug session. Here is a scope screenshot that tracks the process of writing to OTP when it fails (OTP addresses do not contain new data after writing).

You can see the following execution path:
- 0x01, 0x02, 0x03,
- 0x11 (entering the WriteToAddressAndCheck(...) function),
- 0xDE, 0xDE, 0xD0, 0xD0, 0xDE, 0xDE, 0xD0, 0xD0, (checking the OTP content in the CompareContent(...) function)
- 0x1E, 0xF5.

The "0xDE" means the data at some OTP address is not equal to data written.

Do you have any suggestion to overcome this issue?

  • Hi,

    I am assuming you have confirmed that UnlockMemory() function is working correctly and returns true in standalone mode ? Apart from that I would suggest to emulate the standalone mode with debugger connected and see if that works or not. If not then you should be able to debug it. To emulate standalone please follow below steps -

    1. Load the application code you need to run.
    2. With debugger connected write 0x5AFF at address 0xD00 via CCS memory view.
    3. Issue debug reset
    4. Click on the Run

    Device should boot as standalone boot now.

    Regards,

    Vivek Singh

  • Hello Vivek,

    Thanks for your reply.

    I am sure the UnlockMemory() function is returning true in both cases (with and without debugger connected). I will post more scope images later.

    Apart from that I would suggest to emulate the standalone mode with debugger connected and see if that works or not.

    I am sorry, I do not know what you meant by "emulate the standalone mode with debugger connected". I did the steps you suggested, except I cannot find "debug reset". I've tried "CPU Reset", "System Reset" and "Restart" instead, but still the behavior was same - if the debug session was active the code worked, when inactive it did not.

    Can you please explain what is "debug reset"?

    Can you please explain how can I tell if the MCU is  running in the standalone mode?

    Regards,

    Eduard

  • Here's a couple of additional scope images.

    The first one is from when the debug session was active. Here, the OTP write operation was successful as the CompareContent() is outputting only "0xD0" bytes on the right. The big gap between "0x11" and the first "0xD0" byte is actually the Flash Write operation and it takes about 30us.

    The second image is from when the debug session was NOT active. Here, the OTP write operation was NOT successful as the CompareContent() is outputting several "0xDE" bytes. And notice that the gap between "0x11" and the first "0xDE" byte (the Flash Write operation) is now significantly shorter and it takes only about 10us.

    There's another thing to notice. The left part of both images is exactly the same - 0x01 0x02 0x21 0x2F 0x03 0x11 ... this means that UnlockMemory() function is executed in the same manner.

    In addition to that bytes 0x2F 0x03 means that the UnlockMemory() returned TRUE. In case the return value would have been FALSE, then there would have been just a single byte: 0x2E in the scope recording instead.

    The following code is from the UnlockMemory() function. Is the "if" condition correct?

    ...
    /* Check lock status */
    status = HWREGH(DCSM_Z1_BASE + DCSM_O_Z1_CR);
    
    /* if ARMED bit is set and UNSECURED bit or ALLONE bit or both UNSECURED
     * and ALLONE bits are set then CSM is unsecured. Else it is secure. */
    if(          ((status & DCSM_Z1_CR_ARMED)    != 0U)
             && (((status & DCSM_Z1_CR_UNSECURE) != 0U)
             ||  ((status & DCSM_Z1_CR_ALLONE)   != 0U))   )
    {
        /* returnStatus = DCSM_STATUS_UNSECURE; */
        result = true;
    
        TP_SendChar(0x2F); /*#DEL debugging */
    }
    else
    {
        TP_SendChar(0x2E); /*#DEL debugging */
    }
    ...

  • Hi,

    I did the steps you suggested, except I cannot find "debug reset". I've tried "CPU Reset", "System Reset" and "Restart" instead, but still the behavior was same

    Debug reset is same as CPU reset issued from CCS. In this case you should not click on restart instead click on run after reset and make sure you write correct value at address 0xD00 before running.

    Regards,

    Vivek Singh

  • Hi,

    I started a debug session, in Memory Browser I changed address 0xD00 value to 0x5AFF.

    Then I issued CPU Reset and Free Run afterwards. Then I've got the following message: "Break at address "0x3ff16a" with no debug information available, or outside of program code."

  • Hi.

    I started a debug session, in Memory Browser I changed address 0xD00 value to 0x5AFF.

    Sorry, can you try value 0xFF5A instead. 

    Regards,

    Vivek Singh

  • I tried the 0xFF5A but ended with the same message as before: "Break at address "0x3ff16a" with no debug information available, or outside of program code."

    Upon starting the debug session the memory of MCU gets unlocked by the IDE automatically, but as soon as I hit CPU reset I think the memory gets locked again and further debugging is not possible. I think this is why I am getting the message "Break at address ...".

  • Hi,

    Upon starting the debug session the memory of MCU gets unlocked by the IDE automatically,

    How it got unlocked automatically ? Have you provided the programmed password value to IDE ? Even with reset, your system should work because. Look like code is not jumping to application for some reason when running standalone. Can you check NMISHDFLG register value in CCS register view to see if there is any error captured ?

    Regards,

    Vivek Singh

  • Hi Vivek,

    I am sorry for a delayed response, I was out of office for couple of days.

    How it got unlocked automatically ? Have you provided the programmed password value to IDE ?

    Yes I provided the password to the IDE. Otherwise I would not be able to debug at all.

    Can you check NMISHDFLG register value in CCS register view to see if there is any error captured ?

    The register value is 0x0000 - check the screenshot.

    Eduard

  • Hi

    Yes I provided the password to the IDE. Otherwise I would not be able to debug at all.

    Ok, in this case does it make sense to say that when device is lock, your application is not running ? 

    If yes, then I would suggest to check below -

    • Make sure you have not allocated your stack in secure memory.
    • Make sure non-secure code is not accessing any data from secure region.

    Regards,

    Vivek Singh

  • Ok, in this case does it make sense to say that when device is lock, your application is not running ? 

    I believe that the application is running in both cases (with or without running debug session). The application gets stuck at address 0x3FF16A (see the last screenshot) only in case I start a debug session and then execute a CPU reset, or toggle the reset pin of the MCU. I believe this situation can be explained as follows:

    1. Debug session unlocks the FLASH and RAM, loads the code and executes
    2. Upon CPU reset (or NRST pin toggling) the memory gets locked again
    3. Since no unlock operation is performed the FLASH and RAM are locked and thus not accessible to IDE.
    4. Due to some reason the the application gets stuck at the specific address. I am guessing it is some kind of default TRAP handler with an infinite loop.

    But the main question is why the write-to-flash operation is successful ONLY during the active debug session?

    To your suggestion:

    • Make sure you have not allocated your stack in secure memory.
    • Make sure non-secure code is not accessing any data from secure region.

    From the technical ref. manual I've got an impression that the code from a secure region can access data only from the same secure region. Thus I included the complete RAM and FLASH in the same secure region. This includes the STACK as well - the stack is in the same secure region as the executed code. Why are you suggesting I should exclude the STACK from the secure region? This would mean the code from within the secure region would be accessing stack data in the unsecured region. Can you please explain?

    Regards,

    Eduard

  • Hi,

    Sorry for late reply -

    From the technical ref. manual I've got an impression that the code from a secure region can access data only from the same secure region. Thus I included the complete RAM and FLASH in the same secure region. This includes the STACK as well - the stack is in the same secure region as the executed code. Why are you suggesting I should exclude the STACK from the secure region? This would mean the code from within the secure region would be accessing stack data in the unsecured region. Can you please explain?

    Code in secure region can access data from it's secure region as well as non secure region and stack should be in non-secure region else any data access from non secure code will not work.

    Regards,

    Vivek Singh

  • Vivek, thank you for your reply.

    I've checked the stack location and found out that it is located in RAMM1, which is NOT secured.

    Secured RAM sections are: LS0 - LS5 and D0 - D1. These memory areas contain RAM functions and Heap.

    Any other suggestions?

    Eduard

  • That is good. Since you are able to run the application only when device is unlocked, security is causing this issue. I would suggest you to unlock the ECSL (write 64 bit matching password value in KEY0/KEY1 register using CCS register view) after issuing reset and then setup breakpoint at your main entry point and step through the code to see when it jumps to ROM code. Look like there is some security violation which is causing this issue.

    Regards,

    Vivek Singh

  • Hello Vivek,

    Unlocking ECSL using CCS register view did not help.

    Since you are able to run the application only when device is unlocked, security is causing this issue.

    I have to correct the assumption you made - my application is running in both cases if the debugger is connected (in the "unlocked" state) and when it is not connected. Only issue I have is when I execute the "Fapi_issueProgrammingCommand(...)" without the debugger connected it returns "success" but the data is not written to FLASH.

    My intention is to activate memory lock from inside the FW when the FW receives a specific command from a PC over the UART com. In order to activate the memory lock I am writing to the OTP addresses for LINKPOINTER, CSMPASSWORD, etc.

    Regards,

    Eduard

  • Hi,

    In order to activate the memory lock I am writing to the OTP addresses for LINKPOINTER, CSMPASSWORD, etc.

    You can write to these location only after unlocking the zone. When you are connected to debugger, you have zone unlocked so it works but in case of standalone (debugger not connected) zone is locked hence it is not working. 

    Regards,

    Vivek Singh

  • I unlock the zone before I attempt to write to it. I am 100% sure that the zone1 is unsecured before I write to OTP locations related to the zone1 (Z1-LINKPOINTER, ...)

    EDIT: The zone is unlocked before writing even when the debugger is disconnected.

    Regards,

    Eduard

  • Can you please contact TI FAE, if you have access to them ?

    Regards,

    Vivek Singh