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?



