Arm® TrustZone® Getting Started Application on SAM L11: Step 5

Last modified by Microchip on 2023/11/09 09:09

Add Application Code to the Secure Project

The application is already partially developed and is available in the main_l11.c file under <your unzip folder>/saml11_getting_started/dev_files/Secure. The main_l11.c file contains the application logic. It also contains placeholders that you will populate with the necessary code in the next step.

Go to the saml11_getting_started/dev_files/Secure folder and copy the pre-developed main_l11.c file.
Replace the main_l11.c file of your project available at <Your project folder>/saml11_getting_started/firmware/Secure/firmware/src by over-writing it with the copied file.
Go to the saml11_getting_started/dev_files/Secure/trustZone folder and copy the pre-developed nonsecure_entry.c file.
Replace the nonsecure_entry.c file of your project available at <Your project folder>/saml11_getting_started/firmware/Secure/firmware/src/trustZone by over-writing it with the copied file.

Open the trustzone_sam_l11_xpro_Secure project main_l11.c in MPLAB® X IDE and add the application code by following the steps:

Under the main_l11.c file, in function main, notice the call to the SYS_Initialize function.

The generated SYS_Initialize function initializes all the peripheral modules used in the Secure application, which is configured through MPLAB Code Configurator (MCC).

Information

Tip: Press the CTRL key and left click on the SYS_Initialize function. The click will open the implementation for the SYS_Initialize function as shown in the following image.

The entered code is displayed

Information

Note: NVMCTRL_InitializePM_Initialize, and EVSYS_Initialize are system-specific initialization functions necessary to run the device. MCC adds these modules by default to the project graph and generates code. These modules will be initialized to user configurations if the user configures them explicitly.

Back to Top


In the main() function, below SYS_Initialize(), add the following code to register callback event handlers.

SERCOM1_I2C_CallbackRegister(i2cEventHandler, 0);
RTC_Timer32CallbackRegister(rtcEventHandler, 0);
EIC_CallbackRegister(EIC_PIN_5,EIC_User_Handler, 0);

Following the addition of the code above, add the function call.

RTC_Timer32Start();

secure app code 2

Information

Note:

a. The SERCOM1_I2C_CallbackRegister function call registers a callback event handler with the Secure I²C Peripheral Library (PLIB). The event handler is called by the I²C PLIB when the I²C transfer is complete.

b. The RTC_Timer32CallbackRegister function call registers a Secure Real-Time Clock (RTC) callback event handler with the RTC PLIB. The callback event handler is called by the RTC PLIB when the configured time period has elapsed.

c. The EIC_CallbackRegister function call registers a Secure External Interrupt Controller (EIC) callback event handler with the EIC PLIB. The callback event handler is called by EIC PLIB when the user presses the SW0 switch.

Back to Top


Implement the registered callback event handlers for Secure RTC, Secure I²C, and Secure EIC PLIBs by adding the following code before the main() function in main_l11.c.

static void EIC_User_Handler(uintptr_t context)
{
    changeTempSamplingRate = true;      
}

static void rtcEventHandler (RTC_TIMER32_INT_MASK intCause, uintptr_t context)
{
   if (intCause & RTC_TIMER32_INT_MASK_CMP0)
    {            
        isRTCTimerExpired = true;                              
    }
}

static void i2cEventHandler(uintptr_t contextHandle)
{
   if (SERCOM1_I2C_ErrorGet() == SERCOM_I2C_ERROR_NONE)
    {
       if(i2cAddress == TEMP_SENSOR_SLAVE_ADDR)
        {
            isTemperatureRead       = true;
        }
       else if(isEEPROMReadReq == true)
        {
            isEEPROMReadReq         = false;
            eepromTemperatureDataReadStatus  = true;
        }
        isI2CFree                   = true;
    }
}

secure app code 3

Back to Top


Implement the temperature conversion function to convert the temperature value read from sensor to a readable format (Degree Celsius) by adding the following code before the main() function in main_l11.c.

static uint8_t getTemperature(uint8_t* rawTempValue)
{
   int16_t temp;
   // Convert the temperature value read from sensor to readable format (Degree Celsius)
   // For demonstration purpose, temperature value is assumed to be positive.
   // The maximum positive temperature measured by sensor is +125 C
   temp = (rawTempValue[0] << 8) | rawTempValue[1];
    temp = (temp >> 7) * 0.5;
    temp = (temp * 9/5) + 32;
   return (uint8_t)temp;
}

secure app code 4

Back to Top


Implement the EEPROM write function to write the temperature value read from the sensor into EEPROM by adding the following code before the main() function in main_l11.c.

static void EEPROM_Write(int8_t temperature)
{
    eepromTxBuffer[0] = EEPROM_LOG_MEMORY_ADDR + wrIndex;
    eepromTxBuffer[1] = temperature;

   /* Submit I2C transfer to store the temperature value in EEPROM */
    SERCOM1_I2C_Write(EEPROM_I2C_SLAVE_ADDR, (void *)eepromTxBuffer, 2);

   /* Increment the EEPROM memory index */
   if (++wrIndex >= EEPROM_MAX_LOG_VALUES)
    {
        wrIndex = 0;
    }
}

secure app code 5

Back to Top


Implement the secure application function by adding the following code before the main() function in main_l11.c.

void secureApp(void)
{
   if ((isI2CFree == true) &&
       (isRTCTimerExpired == true))  //Temperature Reading from Sensor
    {
       isRTCTimerExpired = false;
       isI2CFree = false;
       i2cAddress = TEMP_SENSOR_SLAVE_ADDR;
       SERCOM1_I2C_WriteRead(TEMP_SENSOR_SLAVE_ADDR, &i2cWrData, 1, i2cRdData, 2);
    }
   if ((isI2CFree == true) &&
       (isTemperatureRead == true))
    {
       isTemperatureRead = false;
       if(changeTempSamplingRate == false)    
        {
           temperatureVal = getTemperature(i2cRdData);
           memset((char*)secureUartTxBuffer, 0x00, 100);
           sprintf((char*)secureUartTxBuffer, "Temperature = %02d F\r\n", temperatureVal);
           LED_Toggle();
       // Temperature Writing to EEPROM
           i2cAddress = EEPROM_I2C_SLAVE_ADDR;
           isI2CFree = false;
           EEPROM_Write(temperatureVal);
        }
       else
        {
           changeTempSamplingRate = false;
           if(tempSampleRate == TEMP_SAMPLING_RATE_500MS)
            {
               tempSampleRate = TEMP_SAMPLING_RATE_1S;
               sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 1 second \r\n");
               RTC_Timer32CompareSet(PERIOD_1S);
            }
           else if(tempSampleRate == TEMP_SAMPLING_RATE_1S)
            {
               tempSampleRate = TEMP_SAMPLING_RATE_2S;
               sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 2 seconds \r\n");        
               RTC_Timer32CompareSet(PERIOD_2S);                        
            }
           else if(tempSampleRate == TEMP_SAMPLING_RATE_2S)
            {
               tempSampleRate = TEMP_SAMPLING_RATE_4S;
               sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 4 seconds \r\n");        
               RTC_Timer32CompareSet(PERIOD_4S);                                        
            }    
           else if(tempSampleRate == TEMP_SAMPLING_RATE_4S)
            {
              tempSampleRate = TEMP_SAMPLING_RATE_500MS;
              sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 500 ms \r\n");        
              RTC_Timer32CompareSet(PERIOD_500MS);
            }
           else
            {
               ;
            }
        }
       temperatureStatus = true;
    }
   if ((isI2CFree == true) &&
       (isEEPROMRead == true))       //Temperature Reading from EEPROM
    {
       isI2CFree           = false;
       isEEPROMRead        = false;
       isEEPROMReadReq     = true;
       i2cAddress          = EEPROM_I2C_SLAVE_ADDR;
       eepromTxBuffer[0]   = EEPROM_LOG_MEMORY_ADDR;
       SERCOM1_I2C_WriteRead(EEPROM_I2C_SLAVE_ADDR, eepromTxBuffer, 1, eepromRxBuffer, 6);
    }
}

secure app code 6

Information

Note:

a. The code snippet (shown in the following image) to submit an I²C transfer to read the temperature sensor value when the configured time period (default 500 milliseconds) has elapsed and I²C PLIB is free. The I²C PLIB calls back the callback event handler (registered in Step 2 above) when the submitted request is complete.

The code snippet to submit an I²C transfer to read the temperature sensor value is displayed

b. The code snippet (shown in the following image) to prepare the received temperature value from the sensor to be displayed on the serial terminal, to store the temperature value in the EEPROM memory. This complete action will happen only if the temperature sensor value is ready and I²C PLIB is free.

The code snippet to prepare the received temperature value from the sensor to be displayed on the serial terminal is displayed

c. The code snippet (shown in the following image) to implement the change of sampling rate and prepare a message for the change on the serial terminal when the user presses the SW0 switch.

The code snippet to implement the change of sampling rate and prepare a message for the change on the serial terminal when the user presses the SW0 switch is displayed

d. The code snippet (shown in the following image) to submit an I²C transfer to read the last five temperature values stored in the EEPROM memory when a read request is received from the non-secure application and I²C PLIB is free.

The code snippet to submit an I²C transfer to read the last five temperature values stored in the EEPROM memory is displayed

 

The complete secure functionality must run in the while(1) super loop of the secure main() function in main_l11.c.

secureApp();

secure app code 11

Information

Note:

  • The non-secure application can access the secure applications by calling Non-Secure Callable (NSCs) functions. So, you need to create an NSC in the non-secure entry region to a Secure function by using the cmse_nonsecure_entry attribute when a secure developer wants to give them access to a secure function to a non-secure developer.
  • When both secure and non-secure applications are running on the Arm® TrustZone® device, the secure main stack will be redirected to the non-secure main stack once after the secure application initialization completes. So, you need to create an NSC to run the complete secure application from the non-secure application.

Back to Top


Under Source Files > trustZone, in the nonsecure_entry.c file, implement the following non-secure callables to access and request the secure application from the non-secure application.

extern void secureApp(void);

/* Non-secure callable (entry) function */
bool __attribute__((cmse_nonsecure_entry)) readTemperatureData(uint8_t *lcluartTxBuffer)
{
   bool lcltemperatureStatus = temperatureStatus;
   if(lcltemperatureStatus == true)
    {
        memset((char*)lcluartTxBuffer, 0x00, 100);
        memcpy(lcluartTxBuffer, secureUartTxBuffer, strlen((const char *)&secureUartTxBuffer[0]));
        temperatureStatus   = false;
    }
   return (lcltemperatureStatus);
}
void __attribute__((cmse_nonsecure_entry)) readEEPROMTemperatureDataReq(uint8_t *temperatureBuf)
{
    isEEPROMRead   = true;
    eepromRxBuffer = temperatureBuf;
   return ;
}
bool __attribute__((cmse_nonsecure_entry)) getEEPROMTemperatureDataReadStatus(uint8_t *LclWrIndex)
{
   bool lclEEPROMTempDataReadStatus    = eepromTemperatureDataReadStatus;
    eepromTemperatureDataReadStatus     = false;
   *LclWrIndex                         = wrIndex;
   return (lclEEPROMTempDataReadStatus);
}
void __attribute__((cmse_nonsecure_entry)) secureAppEntry(void)
{
    secureApp();
}

secure app code 12

Following the addition of the code above, add the header

#include <stdint.h>

Code example

Information

a. The readTemperatureData() function copies the current temperature sensor value into requested non-secure application buffer.

b. The readEEPROMTemperatureDataReq() function registers a read request to read last five temperature values stored in EEPROM from the non-secure application.

c. The getEEPROMTemperatureDataReadStatus() function checks the completion status of the last five stored temperature values read from the EEPROM.

d. The secureAppEntry() function runs the complete secure functionality from the non-secure application.

Back to Top