cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4P5: HardFault when tamper event occurs during breakpoint

frosticles
Associate II

I'm using the tamper peripheral clocked from the LSE (32.768Khz crystal), triggered by a tamper event on PC13. The core is clocked from the HSI, using the PLL to run at 120MHz.

The tamper peripheral seems to work fine under normal operation, but if an unmasked tamper event occurs during a breakpoint (paused in a debugger, or a semihosting printf for example), the core hardfaults.

This sounds it could potentially be related to Errata 2.15.1 where the RTC peripheral has incorrect behaviour when the core is stopped in a breakpoint.

I haven't tested them, but I think the RTC_Tamper examples in STM32CubeL4 should showcase this behaviour, but I'll include my code here for the avoidance of doubt:

    RCC_OscInitTypeDef RCC_OscInitStruct = {
        .OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE,
        .PLL.PLLState = RCC_PLL_NONE,
        .LSEState = RCC_LSE_ON_RTC_ONLY,
        .LSIState = RCC_LSI_OFF,
    };
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {
        .PeriphClockSelection = RCC_PERIPHCLK_RTC,
        .RTCClockSelection = RCC_RTCCLKSOURCE_LSE,
    };

    __HAL_RCC_PWR_CLK_ENABLE();
    HAL_PWR_EnableBkUpAccess();

    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
        Error_Handler();
    }

    __HAL_RCC_RTC_ENABLE();
    __HAL_RCC_RTCAPB_CLK_ENABLE();

    NVIC_SetPriority(TAMP_STAMP_IRQn, 0);
    NVIC_EnableIRQ(TAMP_STAMP_IRQn);

    RTC_TamperTypeDef stamperstructure = {
        .Tamper = RTC_TAMPER_1,
        .Trigger = RTC_TAMPERTRIGGER_LOWLEVEL,
        .Filter = RTC_TAMPERFILTER_8SAMPLE,
        .SamplingFrequency = RTC_TAMPERSAMPLINGFREQ_RTCCLK_DIV16384,
        .PrechargeDuration = RTC_TAMPERPRECHARGEDURATION_1RTCCLK,
        .TamperPullUp = RTC_TAMPER_PULLUP_DISABLE,
        .TimeStampOnTamperDetection = RTC_TIMESTAMPONTAMPERDETECTION_DISABLE,
        .NoErase = RTC_TAMPER_ERASE_BACKUP_ENABLE,
        .MaskFlag = RTC_TAMPERMASK_FLAG_DISABLE,
    };

    RTC_HandleTypeDef RtcHandle = {
        .Instance = RTC,
        .Init.HourFormat = RTC_HOURFORMAT_24,
        .Init.AsynchPrediv = LSE_VALUE / 1000,
        .Init.SynchPrediv = 1000,
    };

    if (HAL_RTC_Init(&RtcHandle) != HAL_OK) {
        Error_Handler();
    }

    /* Configure RTC Tamper Interrupt: EXTI configuration */
    __HAL_RTC_TAMPER_TIMESTAMP_EXTI_ENABLE_IT();
    __HAL_RTC_TAMPER_TIMESTAMP_EXTI_ENABLE_RISING_FALLING_EDGE();

    /* Enable interrupt on selected tamper */
    TAMP->IER |= stamperstructure.Tamper;

    if (HAL_RTCEx_SetTamper(&RtcHandle, &stamperstructure) != HAL_OK) {
        Error_Handler();
    }

As a side note, the HAL function HAL_RTCEx_SetTamper_IT does not initialise the trigger level properly in CR2, the function HAL_RTCEx_SetTamper seems correct though. I suspect the updated code from HAL_RTCEx_SetTamper just needs to be copied across to HAL_RTCEx_SetTamper_IT.

1 ACCEPTED SOLUTION

Accepted Solutions

Oh, well this is starting to make a bit more sense, the reference manual doesn't seem to explicitly say this anywhere, but the SRAM2 sector gets erased to 0's when there's a tamper detection event.

So no **** I get stack corruption when the stack is in SRAM2. What a very frustrating series of events lol.

Could the reference manual be updated to make this more clear?

 

EDIT: For posterity, the reason I thought initially this was related to breakpoints / semihosting was because I have a debug build of my project with some printf's at the start of my code that print out some versioning information etc. And that was delaying the initialization code such that the tamper event happened during a function that was going to return, so the stack being erased meant that the return address was also erased, causing a hard fault.

But when I was compiling without the debugging / semihosting printfs, the initialisation happened quickly, such that when the tamper event happened, the code was usually in the main while loop, which doesn't return, so the fact that the return address was erased didn't matter.

View solution in original post

6 REPLIES 6

> This sounds it could potentially be related to Errata 2.15.1

I don't think so. That errata sounds much to be Alarm-specific.

> if an unmasked tamper event occurs during a breakpoint, the core hardfaults.

How hardfaults exactly? Surely not *during* the breakpoint? Surely while executing code *after* the breakpoint? Can you please single-step it, maybe in disasm? Can you please post content of the fault registers when the hardfault happens?

> (paused in a debugger, or a semihosting printf for example)

I don't use semihosting so this may be a *** question, but how exactly does printf() pause program execution? Isn't printf() itself, or some transmission routine, executed on the processor?

> As a side note

Please start a new thread with this issue, so that the Cube crew does not miss it due to the tamper/hardfault discussion. Thanks.

JW

Apologies the slow reply, this is a really weird issue, but I think I've kind of got to the bottom of it. So yes I mean on return of the breakpoint, and actually I realised, after the execution of the interrupt handler.

When I get the hard fault these are the status registers:

HFSR: 0x40000000. Which I think means a forced hard fault.

CFSR: 0x20000. Which I think is an INVSTATE UsageFault.

 

After looking at it more, it seemed to be a stack corruption sort of problem. I compiled my code with the gcc stack protector (-fstack-protector-all). I ran the initialisation of the tamper peripheral, then paused the core in the debugger. I went and triggered a tamper event, then continued execution, the core entered the `TAMP_STAMP_IRQHandler` and seemed to run that fine. The core exits the interrupt handler fine, and returns to the main thread of execution, but after a few more instructions (basically until the next time it checks the stack canary) I get a stack smashing detected warning from the stack protector.

A while latter I happened to check my linker script, and noticed my memory map wasn't quite right, I had:

MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 160K
  RAM2   (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K
  RAM3   (xrw)    : ORIGIN = 0x20020000,   LENGTH = 32K
  FLASH  (rx)     : ORIGIN = 0x08000000,   LENGTH = 512K
}

While cubemx generates:

MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 320K
  RAM2    (xrw)    : ORIGIN = 0x10000000,   LENGTH = 64K
  RAM3    (xrw)    : ORIGIN = 0x20030000,   LENGTH = 128K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 1024K
}

I don't think the references to RAM2 and RAM3 are important, given they aren't mentioned anywhere else in the linker script, but anyway. The incorrect length of my RAM section meant that the stack was being placed in the SRAM2 section at 0x2002 8000 (SRAM2 is aliased to 0x2002 0000 to 0x2003 0000).

When I corrected the length of the RAM section to 320K, my stack is now placed in the SRAM3 section at 0x20050000, and I don't get any more hardfaults.

My main problem now is I have no idea why having the stack in SRAM2 would cause this problem?

Oh, well this is starting to make a bit more sense, the reference manual doesn't seem to explicitly say this anywhere, but the SRAM2 sector gets erased to 0's when there's a tamper detection event.

So no **** I get stack corruption when the stack is in SRAM2. What a very frustrating series of events lol.

Could the reference manual be updated to make this more clear?

 

EDIT: For posterity, the reason I thought initially this was related to breakpoints / semihosting was because I have a debug build of my project with some printf's at the start of my code that print out some versioning information etc. And that was delaying the initialization code such that the tamper event happened during a function that was going to return, so the stack being erased meant that the return address was also erased, causing a hard fault.

But when I was compiling without the debugging / semihosting printfs, the initialisation happened quickly, such that when the tamper event happened, the code was usually in the main while loop, which doesn't return, so the fact that the return address was erased didn't matter.

Hi @frosticles ,

Thanks for coming back with the solution. Please click on "Accept as solution" in that post so that the thread is marked as solved.

 

@Imen.D,

As @frosticles said, this feature seems to be absent from RM0432. Can you please check this?

Thanks,

JW

frosticles
Associate II

Thanks @waclawek.jan

 

I did notice that the flash read protection (RDP) talks about erasing the backup registers and SRAM2 in certain conditions. So I was wondering if the chip designers have piggy-backed off this mechanism for the tamper protection, and somewhere along the way the fact that it also erases SRAM2 had been forgotten. 

The 'L5 is in spirit successor of the 'L4 (although with a different, "more secure" core), and does feature SRAM2 erase together with backup registers erase upon Tamper. So this might've been the plan even in the 'L4+, and might've been implemented and then perhaps forgotten as you've said?

I'd love to hear ST's explanation.

JW