MCUXpresso_LPC804/boards/lpcxpresso804/demo_apps/safety_iec60730b/safety_cm0_lpc.c

1086 lines
43 KiB
C

/*
* Copyright 2021 NXP.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "safety_cm0_lpc.h"
#if FMSTR_SERIAL_ENABLE
#include "freemaster.h"
#endif
#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;
const uint32_t c_ramStart = (uint32_t)__RAM_start__;/* symbol from Linker command file */
const uint32_t c_ramEnd = (uint32_t)__RAM_end__; /* symbol from Linker command file */
const uint32_t c_romStart = (uint32_t)__ROM_start__;/* symbol from Linker command file */
const uint32_t c_romEnd = (uint32_t)__ROM_end__;/* symbol from Linker command file */
/* The safety-related memory marker */
extern uint32_t Image$$ER_IROM3$$Limit;
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)__PC_test_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 u_programCounterTestFlag = (uint32_t)&m_pc_test_flag;
#define PC_TEST_FLAG ((uint32_t *) u_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 uint16_t __checksum; /* calculated by Linker */
const uint32_t c_checksumStart @ "checksum_start_mark";
const uint32_t c_checksumEnd @ "checksum_end_mark";
extern uint32_t __ROM_start__; /* from Linker configuration file */
const uint32_t c_romStart = (uint32_t)&__ROM_start__;
extern uint32_t __ROM_end__; /* from Linker configuration file */
const uint32_t c_romEnd = (uint32_t)&__ROM_end__;
extern uint32_t __RAM_start__; /* from Linker configuration file */
const uint32_t c_ramStart = (uint32_t)&__RAM_start__;
extern uint32_t __RAM_end__; /* from Linker configuration file */
const uint32_t c_ramEnd = (uint32_t)&__RAM_end__;
#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 __RAM_start__; /* symbol from Linker command file */
const uint32_t c_ramStart = (uint32_t)&__RAM_start__;
extern uint32_t __RAM_end__; /* symbol from Linker command file */
const uint32_t c_ramEnd = (uint32_t)&__RAM_end__;
extern uint32_t __ROM_start__ ; /* symbol from Linker command file */
const uint32_t c_romStart = (uint32_t)&__ROM_start__ ;
extern uint32_t __ROM_end__ ; /* symbol from Linker command file */
const uint32_t c_romEnd = (uint32_t)&__ROM_end__ ;
/* Safety-related FLASH boundaries. */
extern uint32_t m_safety_flash_end;
extern uint32_t __PC_test_start__ ; /* symbol from Linker command file */ ; /* 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)&__PC_test_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
*/
#include "safety_test_items.h"
void SafetyWatchdogTest(safety_common_t *psSafetyCommon, wd_test_t *psSafetyWdTest)
{
#if WATCHDOG_ENABLED
uint32_t counterLimitHigh;
uint32_t counterLimitLow;
/*For debbugging tinit toggleted pin */
PortInit(g_dio_safety_test_items[0]->pPort_byte, g_dio_safety_test_items[0]->pPort_dir,
g_dio_safety_test_items[0]->pPort_Iocon, PIN_DIRECTION_OUT, g_dio_safety_test_items[0]->pinNum, PIN_PULL_UP,
g_dio_safety_test_items[0]->gpio_clkc_shift);
PortInit(g_dio_safety_test_items[1]->pPort_byte, g_dio_safety_test_items[1]->pPort_dir,
g_dio_safety_test_items[1]->pPort_Iocon, PIN_DIRECTION_OUT, g_dio_safety_test_items[1]->pinNum, PIN_PULL_UP,
g_dio_safety_test_items[1]->gpio_clkc_shift);
// set pin 1 do log 1
*(g_dio_safety_test_items[1]->pPort_byte) = 1;
__asm("nop");
*(g_dio_safety_test_items[1]->pPort_byte) = 0; // set pin 1 do log 0
// set pin 1 do log 1
*(g_dio_safety_test_items[0]->pPort_byte) = 1;
__asm("nop");
*(g_dio_safety_test_items[0]->pPort_byte) = 0; // set pin 1 do log 0
/* 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;
/*Library variables 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;
#ifdef MR_TIMER_USE
MRTInit();
#else
CTIMERInit();
#endif
if (!(*(RESET_DETECT_REGISTER)&RESET_DETECT_MASK)) /* 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 */
// set pin 1 do log 1
*(g_dio_safety_test_items[0]->pPort_byte) = 1;
__asm("nop");
*(g_dio_safety_test_items[0]->pPort_byte) = 0; // set pin 1 do log 0
#ifdef MR_TIMER_USE
FS_WDOG_Setup_WWDT_LPC_mrt(WATCHDOG_TEST_VARIABLES, CHANNEL_ZERO);
#else
FS_WDOG_Setup_WWDT_LPC(WATCHDOG_TEST_VARIABLES);
#endif
}
if (*(RESET_DETECT_REGISTER)&RESET_DETECT_MASK) /* if WD reset --- because of debugging--- in real it should be
after every non-POR reset */
{
// set pin 2 do log 1
*(g_dio_safety_test_items[1]->pPort_byte) = 1;
__asm("nop");
*(g_dio_safety_test_items[1]->pPort_byte) = 0; // set pin 2 do log 0
psSafetyCommon->WDOG_test_result = FS_WDOG_Check_WWDT_LPC(
counterLimitHigh, counterLimitLow, WATCHDOG_RESETS_LIMIT, ENDLESS_LOOP_ENABLE, WATCHDOG_TEST_VARIABLES);
// set pin 2 do log 1
*(g_dio_safety_test_items[1]->pPort_byte) = 1;
__asm("nop");
*(g_dio_safety_test_items[1]->pPort_byte) = 0; // set pin 2 do log 0
if (psSafetyCommon->WDOG_test_result != FS_PASS) /* WDOG can return more error message*/
{
// set pin 1 do log 1
*(g_dio_safety_test_items[0]->pPort_byte) = 1;
__asm("nop");
*(g_dio_safety_test_items[0]->pPort_byte) = 0; // set pin 1 do log 0
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) / 2);
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 */
ReferenceTimerInit();
}
/*!
* @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 */
// TODO in case of return "in progress" ?
}
}
/*!
* @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)
{
/* Enable clock to CRC module */
#if HW_FLASH_TEST
#ifdef _LPC824_H_
SYSCON->SYSAHBCLKCTRL |= SYSCON_SYSAHBCLKCTRL_CRC(1);
#elif defined(_LPC51U68_H_)
SYSCON->AHBCLKCTRLSET[0] |= (1U << 21U);
#else
SYSCON->SYSAHBCLKCTRL0 |= SYSCON_SYSAHBCLKCTRL0_CRC(1);
#endif
#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->checksum16 = __checksum;
#else /* KEIL + MCUXpresso */
psFlashConfig->startAddress = c_sfsCRC.ui32FlashStart;
psFlashConfig->endAddress = c_sfsCRC.ui32FlashEnd;
psFlashConfig->checksum16 = (uint16_t)((c_sfsCRC.ui32CRC) & 0x0000FFFF);
#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 + 1) ? 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 ((psFlashConfig->startAddress + psFlashConfig->size) < c_romEnd) /* protection against hard fault */
{
#if HW_FLASH_TEST
psSafetyCommon->FLASH_test_result = FS_CM0_FLASH_HW16_LPC(psFlashConfig->startAddress, psFlashConfig->size,
CRC_BASE, psFlashConfig->startConditionSeed);
#else
psSafetyCommon->FLASH_test_result = FS_CM0_FLASH_SW16(psFlashConfig->startAddress, psFlashConfig->size,
CRC_BASE, psFlashConfig->startConditionSeed);
#endif
}
if ((uint16_t)psSafetyCommon->FLASH_test_result != psFlashConfig->checksum16)
{
psSafetyCommon->safetyErrors |= FLASH_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @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 ((psFlashCrc->actualAddress + psFlashCrc->blockSize) < c_romEnd) /* protection against hard fault */
{
#if HW_FLASH_TEST
psFlashCrc->partCrc =
FS_CM0_FLASH_HW16_LPC(psFlashCrc->actualAddress, psFlashCrc->blockSize, CRC_BASE, psFlashCrc->partCrc);
#else
psFlashCrc->partCrc =
FS_CM0_FLASH_SW16(psFlashCrc->actualAddress, psFlashCrc->blockSize, 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 ((uint16_t)psFlashCrc->partCrc ==
psFlashConfig->checksum16) /* checksum must be same as calculated in linker */
{
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)
{
/* protection against hardfault */
if ((c_ramStart <= psSafetyRamTest->ramTestStartAddress) && (c_ramStart <= psSafetyRamTest->backupAddress) &&
(c_ramEnd > psSafetyRamTest->ramTestEndAddress) &&
((c_ramEnd + 1) >= (psSafetyRamTest->defaultBlockSize + psSafetyRamTest->backupAddress)))
{
psSafetyCommon->RAM_test_result = FS_CM0_RAM_AfterReset(
psSafetyRamTest->ramTestStartAddress, psSafetyRamTest->ramTestEndAddress, psSafetyRamTest->defaultBlockSize,
psSafetyRamTest->backupAddress, FS_CM0_RAM_SegmentMarchC);
}
else
{
psSafetyCommon->safetyErrors |= RAM_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
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)
{
/* protection against hardfault */
if ((c_ramStart <= psSafetyRamTest->ramTestStartAddress) && (c_ramStart <= psSafetyRamTest->backupAddress) &&
(c_ramEnd > psSafetyRamTest->ramTestEndAddress) &&
(c_ramEnd > (psSafetyRamTest->blockSize + psSafetyRamTest->backupAddress)))
{
if (c_ramEnd > (psSafetyRamTest->actualAddress + psSafetyRamTest->blockSize))
psSafetyCommon->RAM_test_result =
FS_CM0_RAM_Runtime(psSafetyRamTest->ramTestStartAddress, psSafetyRamTest->ramTestEndAddress,
(uint32_t *)&psSafetyRamTest->actualAddress, psSafetyRamTest->blockSize,
psSafetyRamTest->backupAddress, FS_CM0_RAM_SegmentMarchC);
}
else
{
psSafetyCommon->safetyErrors |= RAM_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* Test if whole RAM was tested, only for */
if (psSafetyRamTest->ramTestStartAddress == psSafetyRamTest->actualAddress)
runtime_test_finish_flag |=
VARIABLE_RUNTIME_PASSED; /*Variable for development run test, useless for final application */
if (psSafetyCommon->RAM_test_result == FS_FAIL_RAM)
{
psSafetyCommon->safetyErrors |= RAM_TEST_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @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_CM0_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_CM0_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_CM0_CPU_NonStackedRegister();
if (psSafetyCommon->CPU_non_stacked_test_result == FS_FAIL_CPU_NONSTACKED_REGISTER)
{
psSafetyCommon->safetyErrors |= CPU_NONSTACKED_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* CONTROL */
psSafetyCommon->CPU_control_test_result = FS_CM0_CPU_Control();
if (psSafetyCommon->CPU_control_test_result == FS_FAIL_CPU_CONTROL)
{
psSafetyCommon->safetyErrors |= CPU_CONTROL_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
/* SP main */
FS_CM0_CPU_SPmain();
/* SP process */
FS_CM0_CPU_SPprocess();
/* PRIMASK */
psSafetyCommon->CPU_primask_test_result = FS_CM0_CPU_Primask();
if (psSafetyCommon->CPU_primask_test_result == FS_FAIL_CPU_PRIMASK)
{
psSafetyCommon->safetyErrors |= CPU_PRIMASK_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
}
/*!
* @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)
{
psSafetyCommon->CPU_primask_test_result = FS_CM0_CPU_Primask();
if (psSafetyCommon->CPU_primask_test_result == FS_FAIL_CPU_PRIMASK)
{
psSafetyCommon->safetyErrors |= CPU_PRIMASK_ERROR;
SafetyErrorHandling(psSafetyCommon);
}
FS_CM0_CPU_SPmain();
}
/*!
* @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_CM0_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_CM0_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_CM0_STACK_Init function from IEC60730 library.
*
* @param void - macros from header files define the parameters
*
* @return None
*/
void SafetyStackTestInit(void)
{
FS_CM0_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_CM0_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)
{
PortInit(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);
}
PortInit(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)
{
PortInit(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_IN,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
PortInit(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 < 50; 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);
}
PortInit(pTestedPin->pPort_byte, pTestedPin->pPort_dir, pTestedPin->pPort_Iocon, PIN_DIRECTION_OUT,
pTestedPin->pinNum, PIN_PULL_UP, pTestedPin->gpio_clkc_shift);
PortInit(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)
{
PortInit(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 < 50; 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);
}
PortInit(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_A5(g_aio_safety_test_items[index], (fs_aio_a5_t *)TESTED_ADC);
break;
case FS_AIO_PROGRESS:
FS_AIO_ReadResult_A5(g_aio_safety_test_items[index], (fs_aio_a5_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 /* FMSTR_SERIAL_ENABLE */
}
#endif
}