Interrupts are not enabled before entering low power mode
Hi,
I'm using ThreadX (6.2.1) on STM32H750 (Cortex-M7) with AC6 toolchain. When TX_PORT_USE_BASEPRI is enabled, it can not exit low power mode when an interrupt masked by BASEPRI occurs.
I find that interrupts are not enabled before entering low power mode, in .\ports\cortex_m7\ac6\src\tx_thread_schedule.S:
__tx_ts_wait:
#ifdef TX_PORT_USE_BASEPRI
LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI
MSR BASEPRI, r1
#else
CPSID i // Disable interrupts
#endif
LDR r1, [r2] // Pickup the next thread to execute pointer
STR r1, [r0] // Store it in the current pointer
CBNZ r1, __tx_ts_ready // If non-NULL, a new thread is ready!
#ifdef TX_LOW_POWER
PUSH {r0-r3}
BL tx_low_power_enter // Possibly enter low power mode
POP {r0-r3}
#endif
#ifdef TX_ENABLE_WFI
DSB // Ensure no outstanding memory transactions
WFI // Wait for interrupt
ISB // Ensure pipeline is flushed
#endif
#ifdef TX_LOW_POWER
PUSH {r0-r3}
BL tx_low_power_exit // Exit low power mode
POP {r0-r3}
#endif
#ifdef TX_PORT_USE_BASEPRI
MOV r4, #0 // Disable BASEPRI masking (enable interrupts)
MSR BASEPRI, r4
#else
CPSIE i // Enable interrupts
#endif
B __tx_ts_wait // Loop to continue waiting
Interrupts are enabled after WFI. and in .\utility\low_power\tx_low_power.c:
VOID tx_low_power_enter(VOID)
{
...
/* Disable interrupts while we prepare for low power mode. */
TX_DISABLE
...
/* Re-enable interrupts before low power mode is entered. */
TX_RESTORE
/* User code to enter low power mode. This allows the application to power down
peripherals and put the processor in sleep mode.
*/
#ifdef TX_LOW_POWER_USER_ENTER
TX_LOW_POWER_USER_ENTER;
#endif
/* If the low power code returns, this routine returns to the tx_thread_schedule loop. */
}
Since the interrupt is disabled before calling tx_low_power_enter, TX_RESTORE in tx_low_power_enter cannot re-enable interrupts. So interrupts are not enabled before entering low power mode. It results in interrupts masked by BASEPRI not being able to make it exit from low power mode. And when using PRIMASK to disable interrupts, there is no such problem, it can still exit low power mode when any interrupt occurs.
@hadongzhu, Thanks, you are right that it cannot exit low power mode when an interrupt masked by BASEPRI occurs When TX_PORT_USE_BASEPRI is enabled. Here is a workaround.
#ifdef TX_ENABLE_WFI
#ifdef TX_PORT_USE_BASEPRI
CPSID i
MOV r1, #0
MSR BASEPRI, r1
#endif
DSB // Ensure no outstanding memory transactions
WFI // Wait for interrupt
ISB // Ensure pipeline is flushed
#ifdef TX_PORT_USE_BASEPRI
LDR r1, =TX_PORT_BASEPRI
MSR BASEPRI, r1
CPSIE i
#endif
#endif
@wangwen-4220, thank you for your reply, your workaround works fine, but I think this is not the best solution. WFI instroction means "Wait For Interrupt", and in function tx_low_power_enter, the comment for TX_RESTORE is /* Re-enable interrupts before low power mode is entered. */, but it do not enable interrupts actually. So, it's may be It may be better to enable interrupts before entering low power mode, instead of after entering low power mode, like this:
__tx_ts_wait:
#ifdef TX_PORT_USE_BASEPRI
LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI
MSR BASEPRI, r1
#else
CPSID i // Disable interrupts
#endif
LDR r1, [r2] // Pickup the next thread to execute pointer
STR r1, [r0] // Store it in the current pointer
CBNZ r1, __tx_ts_ready // If non-NULL, a new thread is ready!
#ifdef TX_PORT_USE_BASEPRI
MOV r4, #0 // Disable BASEPRI masking (enable interrupts)
MSR BASEPRI, r4
#else
CPSIE i // Enable interrupts
#endif
#ifdef TX_LOW_POWER
PUSH {r0-r3}
BL tx_low_power_enter // Possibly enter low power mode
POP {r0-r3}
#endif
#ifdef TX_ENABLE_WFI
DSB // Ensure no outstanding memory transactions
WFI // Wait for interrupt
ISB // Ensure pipeline is flushed
#endif
#ifdef TX_LOW_POWER
PUSH {r0-r3}
BL tx_low_power_exit // Exit low power mode
POP {r0-r3}
#endif
B __tx_ts_wait // Loop to continue waiting