ESM
在之前的TDA4 SDL功能安全介绍中说道ESM是一个非常重要的模块。但是个人理解不够深刻,在加上最近项目的需要又一次重新深入理解给大家详细介绍这个模块。
模块介绍
ESM可以监测MAIN,MCU,WKUP三个域所发生的支持错误。具体所支持的事件如下:
Mcal\pdk\packages\ti\csl\soc\j784s4\src\cslr_intr_mcu_esm0.h //MCU域支持事件
Mcal\pdk\packages\ti\csl\soc\j784s4\src\cslr_intr_wkup_esm0.h //WAKEUP域支持事件
Mcal\pdk\packages\ti\csl\soc\j784s4\src\cslr_intr_esm0.h //MAIN域支持
监测的反馈方式之前介绍过分为:中断或者pin脚方式。
中断分为main,wakeup,mcu三种域类型,每种类型又分为high priority,low priority,config三种功能。
high | low | config | |
WKUP | 141 | 140 | 142 |
MCU | 99 | 98 | 100 |
MAIN | 49 | 48 | 53 |
void SDL_ESM_hiInterruptHandler_MCU (uintptr_t arg);
void SDL_ESM_hiInterruptHandler_WKUP (uintptr_t arg);
void SDL_ESM_hiInterruptHandler_MAIN (uintptr_t arg);
void SDL_ESM_loInterruptHandler_MCU (uintptr_t arg);
void SDL_ESM_loInterruptHandler_WKUP(uintptr_t arg);
void SDL_ESM_loInterruptHandler_MAIN (uintptr_t arg);
void SDL_ESM_configInterruptHandler_MCU(uintptr_t arg);
void SDL_ESM_configInterruptHandler_WKUP(uintptr_t arg);
void SDL_ESM_configInterruptHandler_MAIN(uintptr_t arg);
Error Pin又分为两种:MCU_SAFETY_ERROR和SOC_SAFETY_ERROR。WKUP Domain没有任何输出引脚。
这里被TI给坑了,J784S4如果按照上面参考根本不对,需要参考J721E的datasheet,发现调试了半天wdg demo是好的,我自己是不好的。发现这块是有区别的,需要自己手写代码,参考下面代码!!!下面是J721E上面是J784S4。
代码详解
参数配置
.esmErrorConfig: 0代表group,3代表index。group可以参考下图的数组排列是个4*8的数组,每32个bit代表一个group。index代表每一个bit,bit的排列方式最低位为0,最高位为31。
.enableBitmap: 使能的bit就要参考最开始的.h文件自己定义所筛序,使能对应的事件。
.priorityBitmap: 0为低优先级,1为高优先级。
.errorpinBitmap: 代表是否使能pin脚输出。
static SDL_ESM_config CCM_Test_esmInitConfig_MCU =
{
.esmErrorConfig = {0u, 3u}, /* Self test error config */
.enableBitmap = {
0xffffffffu, 0xff0fffffu, 0x7fffffffu, 0x00000007u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events enable: except timer and self test events, and Main ESM output */
/* Temporarily disabling vim compare error as well*/
.priorityBitmap = {
0xffffffffu, 0xff0fffffu, 0x7fffffffu, 0x00000007u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events high priority: except timer, selftest error events, and Main ESM output */
.errorpinBitmap = {
0xffffffffu, 0xff0fffffu, 0x7fffffffu, 0x00000007u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events high priority: except timer, selftest error events, and Main ESM output */
};
static SDL_ESM_config CCM_Test_esmInitConfig_WKUP =
{
.esmErrorConfig = {0u, 0u}, /* Self test error config */
.enableBitmap = {
0xffffffffu, 0x00180003u, 0xffffffffu, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events enable: except clkstop events for unused clocks */
.priorityBitmap = {
0xfffffcffu, 0x00180003u, 0xffffffffu, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events high priority: except clkstop events for unused clocks,
* and make VTM WKUP_VTM0_THERM_LVL_GT_TH1_INTR_0 and
* WKUP_VTM0_THERM_LVL_LT_TH0_INTR_0 events low priority */
.errorpinBitmap = {
0xfffffcffu, 0x00180003u, 0xffffffffu, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events high priority: except clkstop for unused clocks
* and make VTM WKUP_VTM0_THERM_LVL_GT_TH1_INTR_0 and
* WKUP_VTM0_THERM_LVL_LT_TH0_INTR_0 events not output to pin */
};
static SDL_ESM_config CCM_Test_esmInitConfig_MAIN =
{
.esmErrorConfig = {0u, 0u}, /* Self test error config 20*32+8=648 ifconfig self test ecc ccm esm call back not execute but interrupt normal*/
.enableBitmap = {
0x00000000u, 0xffffffdbu, 0x7fffffffu, 0xffffffffu,
0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu,
0xffffffffu, 0xfffffffbu, 0xffffffffu, 0xffffffffu,
0xffff7fffu, 0xffffffffu, 0xffffffffu, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
#ifdef ESM_ENABLE_MAIN_CCM_INTERRUT
0x0007c1f0u, 0x000001f0u, 0x00000000u, 0x00000000u, //644~648 654~658 676~680
#else
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, //644~648 654~658 676~680
#endif
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events enable: except clkstop events for unused clocks
* and PCIE events */
.priorityBitmap = {
0x00000000u, 0xffffffdbu, 0x7fffffffu, 0xffffffffu,
0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu,
0xffffffffu, 0xfffffffbu, 0xffffffffu, 0xffffffffu,
0xffff7fffu, 0xffffffffu, 0xffffffffu, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
#ifdef ESM_ENABLE_MAIN_CCM_INTERRUT
0x0007c1f0u, 0x000001f0u, 0x00000000u, 0x00000000u, //644~648 654~658 676~680
#else
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, //644~648 654~658 676~680
#endif
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
/**< All events high priority: except clkstop events for unused clocks
* and PCIE events */
.errorpinBitmap = {
0x00000000u, 0xffffffdbu, 0x7fffffffu, 0xffffffffu,
0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu,
0xffffffffu, 0xfffffffbu, 0xffffffffu, 0xffffffffu,
0xffff7fffu, 0xffffffffu, 0xffffffffu, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
#ifdef ESM_ENABLE_MAIN_CCM_INTERRUT
0x0007c1f0u, 0x000001f0u, 0x00000000u, 0x00000000u, //644~648 654~658 676~680
#else
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, //644~648 654~658 676~680
#endif
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,
},
};
中断配置与处理
high priority和low priority最终都会调用同一个中断函数处理。
static void SDL_ESM_processInterruptSource(uint32_t esmInstBaseAddr,
SDL_ESM_IntType esmIntType,
uint32_t intSrc)
{
SDL_ESM_Instance_t *SDL_ESM_instance;
SDL_ESM_Inst esmInstType;
uint32_t groupNumber, intIndex;
int32_t isHandled = (int32_t)FLAG_NO;
SDL_ESM_selectEsmInstFromAddr(esmInstBaseAddr,
&esmInstType,
&SDL_ESM_instance);
if (intSrc != NO_EVENT_VALUE) {
if (intSrc < (BITS_PER_WORD*SDL_ESM_MAX_EVENT_MAP_NUM_WORDS)) {
SDL_ESM_getGroupNumberIndex(intSrc, &groupNumber, &intIndex);
if((SDL_ESM_instance->esmInitConfig.enableBitmap[groupNumber]
& (((uint32_t)MASK_BIT)<<intIndex)) != INVALID_BIT) {
/* Check if this is due to self test */
if((groupNumber
== SDL_ESM_instance->esmInitConfig.esmErrorConfig.groupNumber) &&
(intIndex == SDL_ESM_instance->esmInitConfig.esmErrorConfig.bitNumber)){
SDL_ESM_selfTestCallback(SDL_ESM_instance);
isHandled = (int32_t)FLAG_YES;
}
else if((SDL_ESM_instance->eccenableBitmap[groupNumber]
& (((uint32_t)MASK_BIT)<<intIndex)) != INVALID_BIT) {
isHandled = SDL_ESM_instance->eccCallBackFunction(esmInstType, esmIntType,
groupNumber, intIndex,
intSrc, SDL_ESM_instance->eccCallBackFunctionArg);
}
else if((SDL_ESM_instance->ccmenableBitmap[groupNumber]
& (((uint32_t)MASK_BIT)<<intIndex)) != INVALID_BIT) {
isHandled = SDL_ESM_instance->CCMCallBackFunction(esmInstType, esmIntType,
groupNumber, intIndex,
intSrc, SDL_ESM_instance->ccmCallBackFunctionArg);
}
else
{
isHandled = (int32_t)FLAG_NO;
}
}
if (isHandled != (int32_t)FLAG_YES)
{
(void)SDL_ESM_instance->callback(esmInstType, esmIntType,
groupNumber, intIndex,
intSrc, SDL_ESM_instance->arg);
}
(void)SDL_ESM_clearIntrStatus(esmInstBaseAddr, intSrc);
}
}
return;
}
SDL_ESM_selfTestCallback是根据.esmErrorConfig参数决定,认为这个错误的产生是我们自己模拟出来不需要任何处理与提示。
下面这两个是特殊处理的模块:
eccCallBackFunction是根据ECC模块对ESM初始化决定的。如果不初始化的情况下产生这个错误会导致进入data abort异常,这点不难理解。
SDL_ECC_initEsm(SDL_ESM_INST_MAIN_ESM0);
SDL_ECC_initEsm(SDL_ESM_INST_MCU_ESM0);
CCMCallBackFunction是根据CCM模块对ESM初始化决定的同上面ECC。
SDL_CCM_init(SDL_CCM_MCU_R5F0);
如果是不属于上述三种的错误将会走进一个通用的错误处理,也就是ESM模块本身初始化时所注册的函数。打印出来的groupChannel和index参考上面描述的。
int32_t SDL_ESM_applicationCallbackFunction(SDL_ESM_Inst esmInst,
SDL_ESM_IntType esmIntrType,
uint32_t grpChannel,
uint32_t index,
uint32_t intSrc,
uintptr_t *arg)
{
int32_t retVal = 0;
UART_printf("ESM Call back function called : instType 0x%x, intType 0x%x, " \
"grpChannel 0x%x, index 0x%x, intSrc 0x%x\n",
esmInst, esmIntrType, grpChannel, index, intSrc);
CddCCM_Int_Clear(intSrc);
return retVal;
}
我们可以调用API模拟注入错误中断,通过下面函数。
SDL_ESM_runESMInjectInstance(SDL_ESM_INST_MCU_ESM0, 1, 3);
PIN脚配置与处理
MCU1_0使能PIN脚错误监测。
void WdgApp_EnableESMErrorPin(void)
{
uint32 regVal;
/* MCU_SAFETY_ERRORn PAD configuration */
/* Unlock lock key registers for Partition 7: IO PAD
configuration registers in WKUP_CTRL_MMR */
/* write Partition 7 Lock Key 0 Register */
LLD_REG32_WR(CSL_WKUP_CTRL_MMR0_CFG0_BASE + 0x1D008, 0x68EF3490);
/* write Partition 7 Lock Key 1 Register */
LLD_REG32_WR(CSL_WKUP_CTRL_MMR0_CFG0_BASE + 0x1D00C, 0xD172BC5A);
/* Check for unlock */
regVal = LLD_REG32_RD(CSL_WKUP_CTRL_MMR0_CFG0_BASE + 0x1D008);
while ((regVal & 0x1) != 0x1U)
{
regVal = LLD_REG32_RD(CSL_WKUP_CTRL_MMR0_CFG0_BASE + 0x1D008);
}
/* Unlocking done */
regVal = LLD_REG32_RD(CSL_WKUP_CTRL_MMR0_CFG0_BASE + 0x1C110U);
regVal |= 0x00000U;
LLD_REG32_WR(CSL_WKUP_CTRL_MMR0_CFG0_BASE + 0x1C110U, regVal);
return;
}
测试修改wdg的超时时间,900ms为启动时间,剩下2s恰好为超时时间,可以看到MCU_SAFETY_ERROR一直持续拉低。
当产生错误的时候中断回掉之后可以通过LLD_ESMResetErrPin(ESM_INSTANCE);将错误取消拉高。
void WdgApp_ESMIsr(uintptr_t handle)
{
uint32 intrStatus;
RTIDwwdGetStatus(WdgApp_RtiInstance, &intrStatus);
if ((HW_GET_FIELD(intrStatus, RTI_RTIWDSTATUS_DWDST) != 1U) ||
(HW_GET_FIELD(intrStatus, RTI_RTIWDSTATUS_DWWD_ST) != 1U))
{
WdgApp_TestPassed = E_NOT_OK;
}
else
{
WdgApp_TestPassed = E_OK;
}
RTIDwwdClearStatus(WdgApp_RtiInstance, intrStatus);
LLD_ESMClearIntrStatus(ESM_INSTANCE, ESM_ERR_SIG);
LLD_ESMClearIntrStatus(WKUP_ESM_INSTANCE, WKUP_ESM_ERR_SIG);
/* Reset error pin if interrupt mode */
if (WdgApp_UserInput == 0U)
{
LLD_ESMResetErrPin(ESM_INSTANCE);
}
WdgApp_IsrFlag = 1U;
}
结语
功能安全后续也只挑几个项目相关的某块讲解,后续会更新有关安全启动的文章。最近也一直在整理安全启动的资料,里面东西还是很多。