MCUXpresso_LPC55S69/boards/lpcxpresso55s69/demo_apps/safety_iec60730b/cm33_core0/safety_cm33_lpc.c

1113 lines
43 KiB
C

/*
* Copyright 2021 NXP.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "safety_cm33_lpc.h"
#include "freemaster.h"
#if (defined(__GNUC__) && ( __ARMCC_VERSION >= 6010050)) /* KEIL */
#include "linker_config.h"
#endif
#if defined(__IAR_SYSTEMS_ICC__) /* IAR */
#pragma section = ".safety_ram"
#pragma section = ".checksum"
#pragma location = ".checksum"
#endif
/*******************************************************************************
* Definitions
******************************************************************************/
#ifndef CRC_BASE
#define CRC_BASE 0x0u /*Function API for HW and SW CRC is the same, due to this reason, we must define this symbol, even though it is not used */
#endif
/*******************************************************************************
* Variables
******************************************************************************/
#if (defined(__GNUC__) && ( __ARMCC_VERSION >= 6010050)) /* KEIL */
extern uint16_t crcPostbuild; /* defined in main.c */
const uint32_t c_wdBackupAddress = (uint32_t)m_wd_test_backup;
#define WATCHDOG_TEST_VARIABLES ((fs_wdog_test_t *) c_wdBackupAddress)
const uint32_t c_programCounterTestFlag = (uint32_t)m_pc_test_flag;
#define PC_TEST_FLAG ((uint32_t *) c_programCounterTestFlag)
const uint32_t c_safetyErrorCodeAddress = (uint32_t)m_safety_error_code;
#define SAFETY_ERROR_CODE ((uint32_t *) c_safetyErrorCodeAddress)
const uint32_t c_backupAddress = (uint32_t)m_ram_test_backup;
/* put values from extern symbols to const variables */
const uint32_t c_stackTestFirstAddress = (uint32_t)m_stack_test_p_2;
const uint32_t c_stackTestSecondAddress = (uint32_t)m_stack_test_p_3;
/* The safety-related FLASH end border marker. */
extern uint32_t Load$$ER_IROM3$$Limit;
/* The safety-related FLASH CRC value. */
fs_crc_t c_sfsCRC __attribute__((used, section(".flshcrc"))) =
{
.ui16Start = 0xA55AU,
.ui32FlashStart = (uint32_t)__ROM_start__,
.ui32FlashEnd = (uint32_t)&Load$$ER_IROM3$$Limit,
.ui32CRC = (uint32_t)FS_CFG_FLASH_TST_CRC,
.ui16End = 0x5AA5U
};
#else /* IAR + MCUXpresso */
extern uint32_t m_wd_test_backup; /* from Linker configuration file */
const uint32_t c_wdBackupAddress = (uint32_t)&m_wd_test_backup;
#define WATCHDOG_TEST_VARIABLES ((fs_wdog_test_t *) c_wdBackupAddress)
extern uint32_t m_ram_test_backup; /* symbol from Linker configuration file */
const uint32_t c_backupAddress = (uint32_t)&m_ram_test_backup;
extern uint32_t m_pc_test_flag; /* from Linker configuration file */
const uint32_t programCounterTestFlag = (uint32_t)&m_pc_test_flag;
#define PC_TEST_FLAG ((uint32_t *) programCounterTestFlag)
extern uint32_t m_safety_error_code; /* from Linker configuration file */
const uint32_t c_safetyErrorCodeAddress = (uint32_t)&m_safety_error_code;
#define SAFETY_ERROR_CODE ((uint32_t *) c_safetyErrorCodeAddress)
extern uint32_t m_stack_test_p_2; /* symbol from Linker configuration file */
extern uint32_t m_stack_test_p_3; /* symbol from Linker configuration file */
/* put values from extern symbols to const variables */
const uint32_t c_stackTestFirstAddress = (uint32_t)&m_stack_test_p_2;
const uint32_t c_stackTestSecondAddress = (uint32_t)&m_stack_test_p_3;
#if defined(__IAR_SYSTEMS_ICC__) /* IAR */
extern const uint32_t __checksum; /* calculated by Linker */
const uint32_t c_checksumStart @ "checksum_start_mark";
const uint32_t c_checksumEnd @ "checksum_end_mark";
#else /* MCUXpresso */
extern uint16_t crcPostbuild; /* defined in main.c */
extern uint32_t stack_test_block_size; /* from Linker command file */
#define STACK_TEST_BLOCK_SIZE (uint32_t)&stack_test_block_size
extern uint32_t ram_test_backup_size; /* from Linker command file */
#define RAM_TEST_BACKUP_SIZE (uint32_t)&ram_test_backup_size
extern uint32_t __ROM_start__; /* symbol from Linker command file */
extern uint32_t m_safety_flash_end; /* symbol from Linker command file */
/* The safety-related FLASH CRC value. */
fs_crc_t c_sfsCRC __attribute__((used, section(".flshcrc"))) =
{
.ui16Start = 0xA55AU,
.ui32FlashStart = (uint32_t) &__ROM_start__,
.ui32FlashEnd = (uint32_t) &m_safety_flash_end,
.ui32CRC = (uint32_t)FS_CFG_FLASH_TST_CRC,
.ui16End = 0x5AA5U
};
#endif
#endif
/** This variable is used as flag for automatic "run" test during example development,
these test have a 3 return value:
1. All tests pass - wait until all after/reset and runtime test passed
2. Safety errror - return from SafetyErrorHandling() function
3. timeout - if case of that all tests are not finished until setted timeout */
#define CLOCK_PASSED (1 << 0)
#define INVARIABLE_RUNTIME_PASSED (1 << 1)
#define VARIABLE_RUNTIME_PASSED (1 << 2)
#define ALL_RUNTIME_PASSED 0x07
/***************************************************
* BIT MASK
* b.0 - Clock test
* b.1 - Invariable memory RunTime
* b.2 - Variable memory Runtime
***************************************************/
uint32_t runtime_test_finish_flag = 0; /* no test performed */
/* This function is only for development validation -
* indicate that runtime test run at leaset one. */
static void test_end(void);
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Safety watchdog test.
*
* This function is used to test the Watchdog.
* Sets up LPTMR for the test.
* Calculates limit values for watchdog timeout.
* Performs the watchdog test.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psSafetyWdTest - The pointer of the Safety Watchdog test structure
* @param peClockFreq - The pointer of the clock name enumeration
*
* @return None
*/
void SafetyWatchdogTest(safety_common_t *psSafetyCommon, wd_test_t *psSafetyWdTest)
{
#if WATCHDOG_ENABLED
uint32_t counterLimitHigh;
uint32_t counterLimitLow;
uint32_t runTestCondition;
uint32_t checkTestCondition;
/* calculate counter limit values */
psSafetyWdTest->wdTestTemp1 = ((uint64_t)WATCHDOG_TIMEOUT_VALUE * (uint64_t)WD_REF_TIMER_CLOCK_FREQUENCY);
psSafetyWdTest->wdTestExpected = psSafetyWdTest->wdTestTemp1 / (uint32_t)WATCHDOG_CLOCK;
psSafetyWdTest->wdTestTolerance = (psSafetyWdTest->wdTestExpected * (uint32_t)WD_TEST_TOLERANCE) / (uint32_t)100;
psSafetyWdTest->wdTestLimitHigh = psSafetyWdTest->wdTestExpected + psSafetyWdTest->wdTestTolerance;
psSafetyWdTest->wdTestLimitLow = psSafetyWdTest->wdTestExpected - psSafetyWdTest->wdTestTolerance;
counterLimitHigh = psSafetyWdTest->wdTestLimitHigh;
counterLimitLow = psSafetyWdTest->wdTestLimitLow;
/* Safety library structure initialization */
WATCHDOG_TEST_VARIABLES->RefTimerBase = (uint32_t)WDOG_REF_TIMER_BASE;
WATCHDOG_TEST_VARIABLES->WdogBase = (uint32_t)USED_WDOG;
WATCHDOG_TEST_VARIABLES->pResetDetectRegister = (uint32_t)(RESET_DETECT_REGISTER);
WATCHDOG_TEST_VARIABLES->ResetDetectMask = (uint32_t)RESET_DETECT_MASK;
/* Conditions */
runTestCondition = WD_RUN_TEST_CONDITION;
checkTestCondition = WD_CHECK_TEST_CONDITION;
/* CTIMER initialization (96MHz) */
CTIMER_initialisation();
if (*(RESET_DETECT_REGISTER)&runTestCondition) /* if non WD reset --- because of debugging--- in real it must be
only after POR reset */
{
*SAFETY_ERROR_CODE = 0; /* clean the safety error code flag */
FS_WDOG_Setup_WWDT_LPC(WATCHDOG_TEST_VARIABLES);
}
if (*(RESET_DETECT_REGISTER)&checkTestCondition)
{
psSafetyCommon->WDOG_test_result = FS_WDOG_Check_WWDT_LPC55SXX(
counterLimitHigh, counterLimitLow, WATCHDOG_RESETS_LIMIT, ENDLESS_LOOP_ENABLE, WATCHDOG_TEST_VARIABLES);
if (psSafetyCommon->WDOG_test_result != FS_PASS) /* WDOG can return more error messages */
{
psSafetyCommon->safetyErrors |= WDOG_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
psSafetyWdTest->watchdogResets = WATCHDOG_TEST_VARIABLES->resets;
psSafetyWdTest->watchdogTimeoutCheck = WATCHDOG_TEST_VARIABLES->counter;
psSafetyWdTest->watchdogRefreshRatio = 0U;
#endif /* WATCHDOG_ENABLED */
}
/*!
* @brief Safety watchdog refresh.
*
* This function is used for adjusting of the watchdog refresh when using in fast interrupt.
*
* @param psSafetyWdTest The pointer of the Safety Watchdog test structure
*
* @return None
*/
void SafetyWatchdogRuntimeRefresh(wd_test_t *psSafetyWdTest)
{
psSafetyWdTest->watchdogRefreshRatio++;
if (psSafetyWdTest->watchdogRefreshRatio == WATCHDOG_REFRESH_RATIO)
{
#if WATCHDOG_ENABLED
Watchdog_refresh; /* refreshing the watchdog */
#endif
psSafetyWdTest->watchdogRefreshRatio = 0;
}
}
/*!
* @brief Initialization of Safety clock test.
*
* Complete Initialization of the clock test.
* Function calculates limit values.
* Cals clock test init function from the IEC60730B library.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psSafetyClockTest - The pointer of the Safety Clock test structure
* @param peClockFreq - The pointer of the clock name enumeration
*
* @return None
*/
void SafetyClockTestInit(safety_common_t *psSafetyCommon, fs_clock_test_t *psSafetyClockTest)
{
psSafetyCommon->mcgirclkFreq = REF_TIMER_CLOCK_FREQUENCY;
#if USE_WKT
psSafetyClockTest->clockTestTolerance = CLOCK_TEST_TOLERANCE;
psSafetyClockTest->clockTestExpected = START_VALUE - (psSafetyCommon->mcgirclkFreq / (uint32_t)ISR_FREQUENCY);
psSafetyClockTest->clockTestLimitHigh =
psSafetyClockTest->clockTestExpected + ((psSafetyCommon->mcgirclkFreq / (uint32_t)ISR_FREQUENCY) * 0.5);
psSafetyClockTest->clockTestLimitLow =
psSafetyClockTest->clockTestExpected - ((psSafetyCommon->mcgirclkFreq / (uint32_t)ISR_FREQUENCY) * 2);
#else /*USE CTIMER*/
psSafetyClockTest->clockTestExpected = (psSafetyCommon->mcgirclkFreq / (uint32_t)ISR_FREQUENCY);
psSafetyClockTest->clockTestTolerance =
(psSafetyClockTest->clockTestExpected * (uint32_t)CLOCK_TEST_TOLERANCE) / (uint32_t)100;
psSafetyClockTest->clockTestLimitHigh =
psSafetyClockTest->clockTestExpected + psSafetyClockTest->clockTestTolerance;
psSafetyClockTest->clockTestLimitLow = psSafetyClockTest->clockTestExpected - psSafetyClockTest->clockTestTolerance;
#endif
psSafetyClockTest->clockTestStart = 0; /* clock test result will be processed after the first interrupt occurs */
FS_CLK_Init((uint32_t *)&psSafetyClockTest->clockTestContext);
/* Initialize the reference timer */
CTIMER_initialisation();
}
/*!
* @brief Clock test function, called from interrupt.
*
* This function calls clock test function from the IEC60730B library and enable the test evaluation.
* It must be called in the Systick interrupt to catch the value of LPTMR counter.
*
* @param psSafetyClockTest - The pointer of the Safety Clock test structure
*
* @return None
*/
void SafetyClockTestIsr(fs_clock_test_t *psSafetyClockTest)
{
#if USE_WKT
FS_CLK_WKT_LPC((fs_wkt_t *)REF_TIMER_USED, (uint32_t *)&psSafetyClockTest->clockTestContext, START_VALUE);
#else
FS_CLK_CTIMER_LPC((fs_ctimer_t *)REF_TIMER_USED, (uint32_t *)&psSafetyClockTest->clockTestContext);
#endif
psSafetyClockTest->clockTestStart |= 1; /* to prevent checking of result before execution */
}
/*!
* @brief Clock test check function.
*
* This function can be called from any place of application.
* It calls the FS_CLK_Check function from the IEC60730 library
* In case of incorrect clock test result, it updates the safetyErrors variable accordingly.
* A node of program flow check is placed here.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psSafetyClockTest - The pointer of the Safety Clock test structure
* @param psSafetyProgramFlowTest - The pointer of the Program flow test structure
*
* @return None
*/
void SafetyClockTestCheck(safety_common_t *psSafetyCommon, fs_clock_test_t *psSafetyClockTest)
{
if (psSafetyClockTest->clockTestStart) /* condition is valid after the first Systick interrupt */
{
psSafetyCommon->CLOCK_test_result =
FS_CLK_Check(psSafetyClockTest->clockTestContext, psSafetyClockTest->clockTestLimitLow,
psSafetyClockTest->clockTestLimitHigh);
if (psSafetyCommon->CLOCK_test_result == FS_FAIL_CLK)
{
psSafetyCommon->safetyErrors |= CLOCK_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
runtime_test_finish_flag |= CLOCK_PASSED; /*Variable for development run test, useless for final application */
}
}
/*!
* @brief Initialization of Safety Flash test.
*
* Enable clock for HW CRC module.
* Inits the Flash test variables
*
* @param psFlashCrc - The pointer of the Flash CRC structure.
* @param psFlashConfig - The pointer of the Safety Flash test configuration structure.
*
* @return None
*/
void SafetyFlashTestInit(fs_flash_runtime_test_parameters_t *psFlashCrc,
fs_flash_configuration_parameters_t *psFlashConfig)
{
#if HW_FLASH_TEST
/* Enable clock to CRC module */
SYSCON->AHBCLKCTRLSET[0] = (1U << 21U);
#endif
#if defined(__IAR_SYSTEMS_ICC__) /* IAR */
psFlashConfig->startAddress = (uint32_t)&c_checksumStart;
psFlashConfig->endAddress =
4 + (uint32_t)&c_checksumEnd; /* We must test also last adress, due to this reason +4 in IAR */
psFlashConfig->checksum = __checksum;
#else /* KEIL + MCUXpresso */
psFlashConfig->startAddress = c_sfsCRC.ui32FlashStart;
psFlashConfig->endAddress = c_sfsCRC.ui32FlashEnd;
psFlashConfig->checksum = c_sfsCRC.ui32CRC;
#endif
psFlashConfig->size = psFlashConfig->endAddress - psFlashConfig->startAddress ;
psFlashConfig->blockSize = FLASH_TEST_BLOCK_SIZE;
psFlashConfig->startConditionSeed = (uint32_t)FLASH_TEST_CONDITION_SEED;
psFlashCrc->actualAddress = psFlashConfig->startAddress; /* start address */
psFlashCrc->partCrc = psFlashConfig->startConditionSeed; /* initial seed */
psFlashCrc->blockSize =
(psFlashConfig->size < psFlashConfig->blockSize) ? psFlashConfig->size : psFlashConfig->blockSize;
}
/*!
* @brief After-reset Flash test.
*
* This function calls the flash test function from IEC60730 library.
* Safety-related part of the flash is tested at once.
* In case of incorrect flash test result, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psFlashConfig - The pointer of the Safety Flash test configuration structure.
*
* @return None
*/
void SafetyFlashAfterResetTest(safety_common_t *psSafetyCommon, fs_flash_configuration_parameters_t *psFlashConfig)
{
#if HW_FLASH_TEST
#ifdef _LPC55S36_H_
uint16_t after_reset_crc = psFlashConfig->startConditionSeed;
psSafetyCommon->FLASH_test_result = FS_FLASH_C_HW16_K(psFlashConfig->startAddress, psFlashConfig->size, CRC_BASE,
&after_reset_crc);
if (((uint32_t)after_reset_crc != psFlashConfig->checksum) || (psSafetyCommon->FLASH_test_result != FS_PASS))
{
psSafetyCommon->safetyErrors |= FLASH_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#else
psSafetyCommon->FLASH_test_result = FS_CM33_FLASH_HW16(psFlashConfig->startAddress, psFlashConfig->size, CRC_BASE,
psFlashConfig->startConditionSeed);
if ((uint32_t)psSafetyCommon->FLASH_test_result != psFlashConfig->checksum)
{
psSafetyCommon->safetyErrors |= FLASH_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#endif
#else
psSafetyCommon->FLASH_test_result = FS_CM33_FLASH_SW16(psFlashConfig->startAddress, psFlashConfig->size, (uint32_t)CRC_BASE,
psFlashConfig->startConditionSeed);
if ((uint32_t)psSafetyCommon->FLASH_test_result != psFlashConfig->checksum)
{
psSafetyCommon->safetyErrors |= FLASH_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#endif
}
/*!
* @brief Runtime Flash test.
*
* This function calls the flash test function from IEC60730 library.
* Safety-related part of the flash is tested in sequence.
* Calls SafetyFlashTestHandling function.
* In case of incorrect flash test result, it updates the safetyErrors variable accordingly.
* A node of program flow check is placed here.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psFlashCrc - The pointer of the Flash CRC structure.
* @param psFlashConfig - The pointer of the Safety Flash test configuration structure.
* @param psSafetyProgramFlowTest - The pointer of the Program flow test structure
*
* @return None
*/
void SafetyFlashRuntimeTest(safety_common_t *psSafetyCommon,
fs_flash_runtime_test_parameters_t *psFlashCrc,
fs_flash_configuration_parameters_t *psFlashConfig)
{
/* CRC calculation for a given block of Flash memory */
#if HW_FLASH_TEST
#ifdef _LPC55S36_H_
psSafetyCommon->FLASH_test_result = FS_FLASH_C_HW16_K(psFlashCrc->actualAddress, psFlashCrc->blockSize, CRC_BASE, (uint16_t *)&psFlashCrc->partCrc);
#else
psFlashCrc->partCrc = FS_CM33_FLASH_HW16(psFlashCrc->actualAddress, psFlashCrc->blockSize, CRC_BASE, psFlashCrc->partCrc);
#endif
#else
psFlashCrc->partCrc =
FS_CM33_FLASH_SW16(psFlashCrc->actualAddress, psFlashCrc->blockSize, (uint32_t)CRC_BASE, psFlashCrc->partCrc);
#endif
if (FS_FLASH_FAIL == SafetyFlashTestHandling(psFlashCrc, psFlashConfig))
{
psSafetyCommon->safetyErrors |= FLASH_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @brief Handling of flash test when used in runtime.
*
* The function updates the flash test variables, when flash is tested in sequence.
*
* @param __checksum - Constant that is calculated by Linker and stored in Flash.
* @param psFlashCrc - The pointer of the Flash CRC structure.
* @param psFlashConfig - The pointer of the Safety Flash test configuration structure.
*
* @return Result of the flash test: FS_ST_FLASH_FAIL or FS_ST_FLASH_PASS
*/
uint32_t SafetyFlashTestHandling(fs_flash_runtime_test_parameters_t *psFlashCrc,
fs_flash_configuration_parameters_t *psFlashConfig)
{
psFlashCrc->actualAddress += psFlashCrc->blockSize; /* set the actual address for testing */
if (psFlashCrc->actualAddress == psFlashConfig->endAddress) /* if all the addresses were tested... */
{
if ((uint32_t)psFlashCrc->partCrc ==
psFlashConfig->checksum) /* checksum must be same as calculated in linker */
{
psFlashCrc->finalCrc = (uint32_t)psFlashCrc->partCrc; /* Store final CRC value */
psFlashCrc->partCrc = psFlashConfig->startConditionSeed; /* set start seed as input for CRC calculation */
psFlashCrc->actualAddress = psFlashConfig->startAddress; /* set start address */
psFlashCrc->blockSize = psFlashConfig->blockSize; /* size of block for CRC testing */
runtime_test_finish_flag |=
INVARIABLE_RUNTIME_PASSED; /*Variable for development run test, useless for final application */
return FS_FLASH_PASS;
}
else
{
return FS_FLASH_FAIL;
}
}
else
{
if (psFlashConfig->endAddress - psFlashCrc->actualAddress <
psFlashConfig->blockSize) /* set size of last block */
{
psFlashCrc->blockSize =
psFlashConfig->endAddress - psFlashCrc->actualAddress; /* arrange the block size for remaining memory */
}
return FS_FLASH_PASS;
}
}
/*!
* @brief Initialization of Safety RAM test.
*
* Inits the RAM test variables
*
* @param psSafetyRamTest - The pointer of the RAM test structure.
* @param pSafetyRamStart - The pointer of the RAM test start address.
* @param pSafetyRamEnd - The pointer of the RAM test end address.
*
* @return None
*/
void SafetyRamTestInit(fs_ram_test_t *psSafetyRamTest, uint32_t *pSafetyRamStart, uint32_t *pSafetyRamEnd)
{
psSafetyRamTest->ramTestStartAddress = (uint32_t)pSafetyRamStart;
psSafetyRamTest->ramTestEndAddress = (uint32_t)pSafetyRamEnd;
psSafetyRamTest->defaultBlockSize = RAM_TEST_BACKUP_SIZE;
psSafetyRamTest->blockSize = RAM_TEST_BLOCK_SIZE;
psSafetyRamTest->actualAddress = psSafetyRamTest->ramTestStartAddress;
#if (defined(__GNUC__) && (__ARMCC_VERSION >= 6010050)) /* KEIL */
psSafetyRamTest->backupAddress = (uint32_t)m_ram_test_backup;
#else /* IAR + MCUXpresso */
psSafetyRamTest->backupAddress = (uint32_t)&m_ram_test_backup;
#endif
}
/*!
* @brief After-reset RAM test.
*
* This function calls the RAM test function from IEC60730 library.
* Safety-related part of the RAM is tested at once.
* In case of incorrect RAM test result, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psSafetyRamTest - The pointer of the Safety RAM test structure.
*
* @return None
*/
void SafetyRamAfterResetTest(safety_common_t *psSafetyCommon, fs_ram_test_t *psSafetyRamTest)
{
psSafetyCommon->RAM_test_result = FS_CM33_RAM_AfterReset(
psSafetyRamTest->ramTestStartAddress, psSafetyRamTest->ramTestEndAddress, psSafetyRamTest->defaultBlockSize,
psSafetyRamTest->backupAddress, FS_CM33_RAM_SegmentMarchC);
if (psSafetyCommon->RAM_test_result == FS_FAIL_RAM)
{
psSafetyCommon->safetyErrors |= RAM_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @brief Runtime RAM test.
*
* This function calls the RAM test function from IEC60730 library.
* Safety-related part of the RAM is tested in sequence.
* Calls SafetyFlashTestHandling function.
* In case of incorrect RAM test result, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psSafetyRamTest - The pointer of the Safety RAM test structure.
*
* @return None
*/
void SafetyRamRuntimeTest(safety_common_t *psSafetyCommon, fs_ram_test_t *psSafetyRamTest)
{
psSafetyCommon->RAM_test_result =
FS_CM33_RAM_Runtime(psSafetyRamTest->ramTestStartAddress, psSafetyRamTest->ramTestEndAddress,
(uint32_t *)&psSafetyRamTest->actualAddress, psSafetyRamTest->blockSize,
psSafetyRamTest->backupAddress, FS_CM33_RAM_SegmentMarchX);
if (psSafetyCommon->RAM_test_result == FS_FAIL_RAM)
{
psSafetyCommon->safetyErrors |= RAM_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* Test if whole RAM was tested, only for example validation */
if (psSafetyRamTest->ramTestStartAddress == psSafetyRamTest->actualAddress)
runtime_test_finish_flag |=
VARIABLE_RUNTIME_PASSED; /*Variable for development run test, useless for final application */
}
/*!
* @brief Program counter test.
*
* This function uses two addresses: first is defined in linker file (fs_cm0_pc_object.o),
* second address comes as function argument (must be RAM address).
* Both addresses must be defined by the developer and suitable to test all of the possible PC bits.
* This test cannot be interrupted.
* In case of incorrect PC test result, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param pattern - RAM address, it can vary with multiple function calls
*
* @return None
*/
void SafetyPcTest(safety_common_t *psSafetyCommon, uint32_t pattern)
{
psSafetyCommon->PC_test_result = FS_CM33_PC_Test(pattern, FS_PC_Object, (uint32_t *)PC_TEST_FLAG);
if (psSafetyCommon->PC_test_result == FS_FAIL_PC)
{
psSafetyCommon->safetyErrors |= PC_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @brief After-reset CPU registers test.
*
* This function calls the CPU test functions from IEC60730 library.
* All the registers are tested at once.
* In case of incorrect flash test result, it updates the safetyErrors variable accordingly.
* See IEC60730 library documentation for CPU errors handling !
*
* @param psSafetyCommon - The pointer of the Common Safety structure
*
* @return None
*/
void SafetyCpuAfterResetTest(safety_common_t *psSafetyCommon)
{
/* stacked CPU registers */
psSafetyCommon->CPU_reg_test_result = FS_CM33_CPU_Register();
if (psSafetyCommon->CPU_reg_test_result == FS_FAIL_CPU_REGISTER)
{
psSafetyCommon->safetyErrors |= CPU_REGISTERS_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* non-stacked CPU registers */
psSafetyCommon->CPU_non_stacked_test_result = FS_CM33_CPU_NonStackedRegister();
if (psSafetyCommon->CPU_non_stacked_test_result == FS_FAIL_CPU_NONSTACKED_REGISTER)
{
psSafetyCommon->safetyErrors |= CPU_NONSTACKED_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* SP main Secure */
FS_CM33_CPU_SPmain_S();
/* SP main limit Secure */
FS_CM33_CPU_SPmain_Limit_S();
/* SP process Secure */
FS_CM33_CPU_SPprocess_S();
/* SP process limit Secure */
FS_CM33_CPU_SPprocess_Limit_S();
/* PRIMASK Secure */
psSafetyCommon->CPU_primask_s_test_result = FS_CM33_CPU_Primask_S();
if (psSafetyCommon->CPU_primask_s_test_result == FS_FAIL_CPU_PRIMASK)
{
psSafetyCommon->safetyErrors |= CPU_PRIMASK_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* Special Secure */
psSafetyCommon->CPU_special_s_test_result = FS_CM33_CPU_Special8PriorityLevels_S();
if (psSafetyCommon->CPU_special_s_test_result == FS_FAIL_CPU_SPECIAL)
{
psSafetyCommon->safetyErrors |= CPU_SPECIAL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#if (defined(_LPC55S69_CM33_CORE0_H_) || defined(_LPC55S06_H_)) /* If device supports TrustZone */
/* CONTROL Secure */
psSafetyCommon->CPU_control_s_test_result = FS_CM33_CPU_Control_S();
if (psSafetyCommon->CPU_control_s_test_result == FS_FAIL_CPU_CONTROL)
{
psSafetyCommon->safetyErrors |= CPU_CONTROL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* CONTROL Non-Secure */
psSafetyCommon->CPU_control_ns_test_result = FS_CM33_CPU_Control_NS();
if (psSafetyCommon->CPU_control_ns_test_result == FS_FAIL_CPU_CONTROL)
{
psSafetyCommon->safetyErrors |= CPU_CONTROL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* SP main Non-Secure */
FS_CM33_CPU_SPmain_NS();
/* SP main limit Non-Secure */
FS_CM33_CPU_SPmain_Limit_NS();
/* SP process Non-Secure */
FS_CM33_CPU_SPprocess_NS();
/* SP process limit Non-Secure */
FS_CM33_CPU_SPprocess_Limit_NS();
/* PRIMASK Non-Secure */
psSafetyCommon->CPU_primask_ns_test_result = FS_CM33_CPU_Primask_NS();
if (psSafetyCommon->CPU_primask_ns_test_result == FS_FAIL_CPU_PRIMASK)
{
psSafetyCommon->safetyErrors |= CPU_PRIMASK_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* Special Non-Secure */
psSafetyCommon->CPU_special_ns_test_result = FS_CM33_CPU_Special8PriorityLevels_NS();
if (psSafetyCommon->CPU_special_ns_test_result == FS_FAIL_CPU_SPECIAL)
{
psSafetyCommon->safetyErrors |= CPU_SPECIAL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#else
/* CONTROL */
psSafetyCommon->CPU_control_s_test_result = FS_CM33_CPU_Control();
if (psSafetyCommon->CPU_control_s_test_result == FS_FAIL_CPU_CONTROL)
{
psSafetyCommon->safetyErrors |= CPU_CONTROL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#endif /* If device supports TrustZone */
#if __FPU_PRESENT
psSafetyCommon->CPU_fpu_test_result = FS_CM33_CPU_Float1();
if (psSafetyCommon->CPU_fpu_test_result == FS_FAIL_CPU_FLOAT_1)
{
psSafetyCommon->safetyErrors |= CPU_FLOAT_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
psSafetyCommon->CPU_fpu_test_result = FS_CM33_CPU_Float2();
if (psSafetyCommon->CPU_fpu_test_result == FS_FAIL_CPU_FLOAT_2)
{
psSafetyCommon->safetyErrors |= CPU_FLOAT_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#endif
}
/*!
* @brief Uninterruptible test of CPU registers.
*
* This function calls the CPU test functions from IEC60730 library.
* The function must be called from an interrupt with highest priority.
* In case of incorrect flash test result, it updates the safetyErrors variable accordingly.
* See IEC60730 library documentation for CPU errors handling !
*
* @param psSafetyCommon - The pointer of the Common Safety structure
*
* @return None
*/
void SafetyCpuIsrTest(safety_common_t *psSafetyCommon)
{
/* PRIMASK Secure */
psSafetyCommon->CPU_primask_s_test_result = FS_CM33_CPU_Primask_S();
if (psSafetyCommon->CPU_primask_s_test_result == FS_FAIL_CPU_PRIMASK)
{
psSafetyCommon->safetyErrors |= CPU_PRIMASK_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* Special Secure */
psSafetyCommon->CPU_special_s_test_result = FS_CM33_CPU_Special8PriorityLevels_S();
if (psSafetyCommon->CPU_special_s_test_result == FS_FAIL_CPU_SPECIAL)
{
psSafetyCommon->safetyErrors |= CPU_SPECIAL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* SP main Secure */
FS_CM33_CPU_SPmain_S();
/* SP main limit Secure */
FS_CM33_CPU_SPmain_Limit_S();
#if (defined(_LPC55S69_CM33_CORE0_H_) || defined(_LPC55S06_H_)) /* If device supports TrustZone */
/* PRIMASK Non-Secure */
psSafetyCommon->CPU_primask_ns_test_result = FS_CM33_CPU_Primask_NS();
if (psSafetyCommon->CPU_primask_ns_test_result == FS_FAIL_CPU_PRIMASK)
{
psSafetyCommon->safetyErrors |= CPU_PRIMASK_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* Special Non-Secure */
psSafetyCommon->CPU_special_ns_test_result = FS_CM33_CPU_Special8PriorityLevels_NS();
if (psSafetyCommon->CPU_special_ns_test_result == FS_FAIL_CPU_SPECIAL)
{
psSafetyCommon->safetyErrors |= CPU_SPECIAL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
#endif /* If device supports TrustZone */
}
/*!
* @brief Interruptible test of CPU registers.
*
* This function calls the CPU test functions from IEC60730 library.
* The function can be called from the background loop.
* In case of incorrect flash test result, it updates the safetyErrors variable accordingly.
* See IEC60730 library documentation for CPU errors handling !
*
* @param psSafetyCommon - The pointer of the Common Safety structure
*
* @return None
*/
void SafetyCpuBackgroundTest(safety_common_t *psSafetyCommon)
{
psSafetyCommon->CPU_reg_test_result = FS_CM33_CPU_Register();
if (psSafetyCommon->CPU_reg_test_result == FS_FAIL_CPU_REGISTER)
{
psSafetyCommon->safetyErrors |= CPU_REGISTERS_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
psSafetyCommon->CPU_non_stacked_test_result = FS_CM33_CPU_NonStackedRegister();
if (psSafetyCommon->CPU_non_stacked_test_result == FS_FAIL_CPU_NONSTACKED_REGISTER)
{
psSafetyCommon->safetyErrors |= CPU_NONSTACKED_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @brief Initialization of Stack test.
*
* This function calls FS_CM33_STACK_Init function from IEC60730 library.
*
* @param void - macros from header files define the parameters
*
* @return None
*/
void SafetyStackTestInit(void)
{
FS_CM33_STACK_Init(STACK_TEST_PATTERN, c_stackTestFirstAddress, c_stackTestSecondAddress, STACK_TEST_BLOCK_SIZE);
}
/*!
* @brief Stack test.
*
* This function calls the STACK test function from IEC60730 library
* Stack is tested for underflow and overflow condition.
* In case of incorrect Stack test result, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
*
* @return None
*/
void SafetyStackTest(safety_common_t *psSafetyCommon)
{
psSafetyCommon->STACK_test_result = FS_CM33_STACK_Test(STACK_TEST_PATTERN, c_stackTestFirstAddress,
c_stackTestSecondAddress, STACK_TEST_BLOCK_SIZE);
if (psSafetyCommon->STACK_test_result == FS_FAIL_STACK)
{
psSafetyCommon->safetyErrors |= STACK_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @brief SafetyDIOTestInit
*
* Check if every item of input array has valid pin definition.
* It also fills the pcr variable with appropriate address (pin control register address), which is used in DIO
* test.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param *pTestItems - Array of pointers to the DIO test items (pin definitions)
*
* @return None
*/
void SafetyDIOTestInit(safety_common_t *psSafetyCommon, fs_dio_test_lpc_t *pTestItems[])
{
/* Nothing to do here, just because of compatibility */
}
/*!
* @brief Digital Input/Output test.
*
* This function calls output test functions from IEC60730 library
* In case of incorrect test result, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param pTestedPin - The pointer to the DIO test item structure (pin definition)
*
* @return None
*/
void SafetyDigitalOutputTest(safety_common_t *psSafetyCommon, fs_dio_test_lpc_t *pTestedPin)
{
PortSetup(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_OUT,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
psSafetyCommon->DIO_output_test_result = FS_DIO_Output_LPC(pTestedPin, DIO_WAIT_CYCLE);
if ((psSafetyCommon->DIO_output_test_result) != FS_PASS)
{
psSafetyCommon->safetyErrors |= DIO_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
PortSetup(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_OUT,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
}
/*!
* @brief Digital Input/Output Short to Adjacent pins test
*
* This function calls digital io short test SET and GET functions from IEC60730 library
* In case of incorrect test conditions, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param *pTestedPin - The pointer to the DIO test item structure (pin definition)
* @param *pAdjPin - The pointer of the DIO test item structure for Adjacent pin (pin definition)
* @param pinValue - logical 1 or logical 0 will be set on the tested pin
*
* @return None
*/
void SafetyDigitalInputOutput_ShortAdjTest(safety_common_t *psSafetyCommon,
fs_dio_test_lpc_t *pTestedPin,
fs_dio_test_lpc_t *pAdjPin,
uint32_t pinValue)
{
PortSetup(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_IN,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
PortSetup(pAdjPin->pPort_byte, pAdjPin->pPort_dir, pAdjPin->pPort_Iocon, PIN_DIRECTION_OUT, pAdjPin->pinNum,
PIN_PULL_UP, pAdjPin->gpio_clkc_shift);
psSafetyCommon->DIO_short_test_result = FS_DIO_ShortToAdjSet_LPC(pTestedPin, pAdjPin, pinValue, DIO_BACKUP);
if ((psSafetyCommon->DIO_short_test_result) != FS_PASS)
{
psSafetyCommon->safetyErrors |= DIO_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* if needed, place some delay loop here */
for (int i = 0; i < 200; i++)
{
__asm("nop");
}
psSafetyCommon->DIO_input_test_result = FS_DIO_InputExt_LPC(pTestedPin, pAdjPin, pinValue, DIO_BACKUP);
if ((psSafetyCommon->DIO_input_test_result) != FS_PASS)
{
psSafetyCommon->safetyErrors |= DIO_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
PortSetup(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_OUT,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
PortSetup(pAdjPin->pPort_byte, pAdjPin->pPort_dir, pAdjPin->pPort_Iocon, PIN_DIRECTION_OUT, pAdjPin->pinNum,
PIN_PULL_UP, pAdjPin->gpio_clkc_shift);
}
/*!
* @brief Digital Input/Output Short to Supply test.
*
* This function calls digital io short test SET and GET functions from IEC60730 library
* In case of incorrect test conditions, it updates the safetyErrors variable accordingly.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param *pTestedPin - The pointer to the DIO test item structure (pin definition)
* @param polarity - macro DIO_SHORT_TO_VDD_TEST or DIO_SHORT_TO_GND_TEST
*
* @return None
*/
void SafetyDigitalInputOutput_ShortSupplyTest(safety_common_t *psSafetyCommon,
fs_dio_test_lpc_t *pTestedPin,
uint8_t polarity)
{
PortSetup(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_OUT,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
psSafetyCommon->DIO_short_test_result = FS_DIO_ShortToSupplySet_LPC(pTestedPin, polarity, DIO_BACKUP);
if ((psSafetyCommon->DIO_short_test_result) != FS_PASS)
{
SafetyErrorHandling(psSafetyCommon);
}
/* if needed, place some delay loop here */
for (int i = 0; i < 200; i++)
{
__asm("nop");
}
psSafetyCommon->DIO_input_test_result = FS_DIO_InputExt_LPC(pTestedPin, pTestedPin, polarity, DIO_BACKUP);
if ((psSafetyCommon->DIO_input_test_result) != FS_PASS)
{
psSafetyCommon->safetyErrors |= DIO_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
PortSetup(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_OUT,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
}
/*!
* @brief ADC test.
*
* This function calls functions from IEC60730 library to perform ADC test.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
*
* @return None
*/
void SafetyAnalogTest(safety_common_t *psSafetyCommon)
{
static int index = 0; /* Iteration variable for going through all ADC test items */
/* If state is FS_AIO_SCAN_COMPLETE, check limits, otherwise function has not effect */
psSafetyCommon->AIO_test_result = FS_AIO_LimitCheck(g_aio_safety_test_items[index]->RawResult, &(g_aio_safety_test_items[index]->Limits), &(g_aio_safety_test_items[index]->state));
switch (psSafetyCommon->AIO_test_result)
{
case FS_AIO_INIT:
FS_AIO_InputSet_A1(g_aio_safety_test_items[index], (fs_aio_a1_t *)TESTED_ADC);
break;
case FS_AIO_PROGRESS:
FS_AIO_ReadResult_A1(g_aio_safety_test_items[index], (fs_aio_a1_t *)TESTED_ADC);
break;
case FS_PASS: /* successfull execution of test, call the trigger function again */
if( g_aio_safety_test_items[++index] == NULL)
{
index = 0; /* again first channel*/
}
g_aio_safety_test_items[index]->state = FS_AIO_INIT;
break;
case FS_FAIL_AIO:
SafetyErrorHandling(psSafetyCommon);
break;
default:
__asm("NOP");
break;
}
}
/*!
* @brief Handling of the safety functions that must be called in interrupt routine.
*
* This function switches between safety functions that are called in interrupt
*
* @param psSafetyCommon - The pointer of the Common Safety structure
* @param psSafetyRamTest - The pointer of the Safety RAM test structure.
* @param psSafetyRamStackTest - The pointer of the Safety RAM test structure for Stack area.
*
* @return None
*/
void SafetyIsrFunction(safety_common_t *psSafetyCommon,
fs_ram_test_t *psSafetyRamTest,
fs_ram_test_t *psSafetyRamStackTest)
{
switch (psSafetyCommon->fastIsrSafetySwitch)
{
case 0: /* CPU registers test that cannot be interrupted */
SafetyCpuIsrTest(psSafetyCommon);
break;
case 1: /* Program counter test */
#if PC_TEST_ENABLED
SafetyPcTest(psSafetyCommon, PC_TEST_PATTERN);
#endif
break;
case 2: /* RAM March test for safety related RAM space */
SafetyRamRuntimeTest(psSafetyCommon, psSafetyRamTest);
break;
case 3: /* RAM March test for memory occupied by the Stack */
SafetyRamRuntimeTest(psSafetyCommon, psSafetyRamStackTest);
break;
default:
__asm("nop");
break;
}
psSafetyCommon->fastIsrSafetySwitch++;
if (psSafetyCommon->fastIsrSafetySwitch == 4)
psSafetyCommon->fastIsrSafetySwitch = 0;
}
/* Function for example validation during development */
void development_test_terminate(void)
{
static uint8_t runtime_counter = 0;
if (runtime_test_finish_flag == ALL_RUNTIME_PASSED)
{
runtime_test_finish_flag = 0;
runtime_counter++;
if (runtime_counter == 10)
{
test_end(); /* Mark that runtime test run */
}
}
}
/* This function is only for example validation during development */
static void test_end(void)
{
__asm("nop");
}
/*!
* @brief Handling with a safety error.
*
* This function stores the code of recognized safety error into the dedicated RAM memory that is deleted only
* after POR. If SAFETY_ERROR_ACTION macro is defined, interrupts are disabled and function waits for watchdog reset.
*
* @param psSafetyCommon - The pointer of the Common Safety structure
*
* @return None
*/
void SafetyErrorHandling(safety_common_t *psSafetyCommon)
{
*SAFETY_ERROR_CODE = psSafetyCommon->safetyErrors;
test_end(); /* Only for example validation. Indicate that some error occured and test can be stoped */
#if SAFETY_ERROR_ACTION
__asm("CPSID i"); /* disable interrupts */
while (1)
{
#if FMSTR_SERIAL_ENABLE
FMSTR_Poll(); /* Freemaster cummunication */
#endif
}
#endif
}