2017-04-15 09:36 AM
Dear all,
During a porting of an RTOS on a Discovery 476 board I discovered a serious problem with the stm32l476xx.h file provided by ST. All the elements of timers structure are defined as a 32-bits. So, after having spent a couple of hours in debugging my porting I discovered that some elements HAVE to be accessed in a
16-bits
and not in a32-bits
. In particular, the SR register accessed in 32-bits crashe my system with an 'Hard Fault Exception'. In the documentation the SR register is described to be 16-bits. So, I modified the original structure of the package and now everything work as expected. Here is my modification.The original package (that crash the interruption acknowledge)
typedef struct {
...
volatile
uint32_t
SR;...
} TIM_TypeDef;
The modified package (that works)
typedef struct {
...
volatile
uint16_t
SR;volatile
uint16_t
RESERVED0;...
} TIM_TypeDef;
I pointed different peripheral structures and I discovered that potentially I can have the same problem (of course, this depends on how the silicon supports the 32-bit accesses for 16-bit wide registers).
Now I have serious doubts regarding the quality of the packages provided by ST!
Regards
Edo
2017-04-15 11:37 AM
I'm not using the L4's, but can't say I've seen the other STM32 parts being that sensitive.
2017-04-15 02:01 PM
I have just tried on a stm32F777 and stm32F429 targets and those SoC seem to work fine with 32-bits and 16-bits accesses. For the moment only the stm32L476 seems to be sensitive to this problem.
2017-04-15 04:59 PM
Show us a minimal but complete compilable code exhibiting the problem.
JW
2017-04-16 11:45 AM
The stated issue as framed should be easy enough to replicate. I'd need to dig up a STM32L476G-DISCO
2017-04-17 02:08 AM
Hi Jan and Clive,
Not easy to debug. The problem is more nasty than expected. The crash occurs inside a software interruption responsible to pass parameters to a µKernel. It cannot be easily isolated. Here is the µKernel routine (in red the instructions that generate the Hard Fault Exception if the SR is accessed as 32-bit and not as 16-bits as reported in the documentation). The disassembled code do not emphasise possible problems.
Of course, you can download the entire package for testing at this location
Thank you for your suggestions
Edo
/*!
* \brief EXTI3_IRQHandler
*
* - TRAP
* - Save the context
* - Save the context stack
* - Process the message sent
* - Change the context
* - Give another timeout to the process
* - Recover another context
*
* - Save the full stack frame (uKOS like)
*
* Normal stack frame Stack frame with fpu
* IOFF IOFF
*
* -> Message +36 +108
* -> Message +32 +104
*
* -> FPSCR
* -> S15..S0
* -> xPSR xPSR
* -> PC PC
* -> LR(R14) LR(R14)
* -> R12 R12
* -> R3..R0 R3..R0 Block stacked by an exception
*
* -> R11..R4 R11..R4
* -> BASEPRI BASEPRI
* -> S31..S16
* -> LR(R14) LR(R14) Block stacked manually
*
* !!! Do not generate prologue/epilogue sequences
*
*/
void EXTI3_IRQHandler(void) __attribute__ ((naked)) __attribute__ ((optimize('Os')));
void EXTI3_IRQHandler(void) {
// Interruption ACK
EXTI->PR1 |= (1<<BKERNSWIMSG);
// Recover the swi message
// r0 contains the stack
__asm__ volatile (
'cpsid i \n' //
'mrs r0,psp \n' // r0 = stack
'add r1,r0,#36 \n' // r1 = message address (normal stack frame)
'tst r14,#0x10 \n' //
'it eq \n' //
'addeq r1,r0,#(36+72) \n' // r1 = message address (stack frame with fpu regs)
'ldr r1,[r1] \n' // r1 = message
'str r1,%0 \n' // Save the message on the vKern_message location
:
: 'm' (vKern_message)
: KSAVEREGISTERS
);
// Save the registers r4..r11 and the basepri
// r0 contains the stack
__asm__ volatile (
'mrs r1,basepri \n' // r1 = basepri
'stmdb r0!,{r1,r4-r11} \n' // Save the register list
'tst r14,#0x10 \n' //
'it eq \n' //
'vstmdbeq r0!,{s16-s31} \n' // If used, save the fp registers
'stmdb r0!,{r14} \n' // Save the EXCEPTION return
'msr psp,r0 \n' //
'str r0,%0 \n' // Save the stack of the old process
'cpsie i \n' //
:
: 'm' (vKern_stackProcess)
: KSAVEREGISTERS
);
// INT acknowledge and new time for the next process
// Change the context and prepare the next process
// - Stop time of the process
// - Clear the interruption
// - Disable the timer
// - Change the context
// - Start time of the process
_timeStop();
if (TIM2->SR & TIM_SR_UIF) {
TIM2->SR &= ~TIM_SR_UIF;
}
TIM2->CR1 &= ~TIM_CR1_CEN;
sche_callBackTrap(vKern_message);
_timeStart();
#if (defined(__WITHSTAT__))
stat_statistic(vKern_backwardProcess, \
vTimeStart, vTimeStop, vTimeLastStart, vTimeException);
vTimeException = 0;
#endif
// Restore the registers r4..r11 and the basepri
__asm__ volatile (
'cpsid i \n' //
'ldr r0,%0 \n' // Recover the stack of the new process
'ldmia r0!,{r14} \n' // Recover the EXCEPTION return
'tst r14,#0x10 \n' //
'it eq \n' //
'vldmiaeq r0!,{s16-s31} \n' // If used, restore the fp registers
'ldmia r0!,{r1,r4-r11} \n' // Restore the register list
'msr psp,r0 \n' // New stack
'msr basepri,r1 \n' // Restore the basepri
:
: 'm' (vKern_stackProcess)
: KSAVEREGISTERS
);
// Return
__asm__ volatile (
'cpsie I \n' //
'dmb \n' //
'dsb \n' //
'isb \n' //
'bx lr \n' // Return
);
}
2017-04-17 04:56 PM
I just tried to add
if (TIM3->SR & TIM_SR_UIF) {
TIM3->SR &= ~TIM_SR_UIF;
}
into while(1) loop in main() in the 'breathing blinky' for the 'L476 DISCO on www.efton.sk/STM32 . No change in behaviour, no hardfault.
I believe the change in behaviour after changing the access width is coincidental and the bug is elsewhere in your code. While TIMx_SR is indeed 16-bit, as are most of the TIM registers, RM0351 allows to access using 32-bit access (e.g. in 27.4, 'The peripheral registers can be accessed by half-words (16-bit) or words (32-bit).')
I recommend you to check the SCB's fault-related registers and properly track back the stack to the crash origin when the fault happens.
JW
PS. Not a problem here, but generally, don't RMW TIMx_SR to avoid risk of loss of a flag being set by HW during the RMW - just write a mask to clear the bits you want, they are deliberately of rc_w0 type.