Closed juliobc closed 1 year ago
Hi @juliobc,
But the problem is when the ISR is fired between osKernelSuspend() and EnterSTOPMode(). What is the behavior in that case?
This depends on your implementation of EnterSTOPMode()
. Basically, on Cortex-M you have two options, WFI
or WFE
. The WFI
has the drawback that it will only wake-up on interrupts received after the instruction. This is why most examples recommend using WFE
which just clears the event-flag if it is already set but doesn't put the device into sleep.
Cheers, Jonatan
Hi @JonatanAntoni
I use WFE
to enter in stop mode. I understand that EnterSTOPMode()
returns immediately if the ISR has been fired and when osKernelResume()
is executed the Thread_test goes into running state.
Then, I understand when Idle Thread run after a while, allways EnterSTOPMode()
returns immediately because the event-flag of old ISR´s fired is not clear and then in the next loop EnterSTOPMode()
wait for a tc_wakeup time.
Thank you very much for the quickly response. It is very clear now.
Hi @JonatanAntoni
I use STMF4 micros and I have noticed that two instructions are executed inside EnterSTOPMode()
in the ST HAL libray:
void HAL_PWR_EnterSTOPMode()
{
/* Set SLEEPDEEP bit of Cortex System Control Register */
SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
/* Request Wait For Event */
__SEV();
__WFE();
__WFE();
/* Reset SLEEPDEEP bit of Cortex System Control Register */
CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
}
so, I will lost the Thread_test in ready state if that test_ISR is fired between osKernelSuspend() and EnterSTOPMode(). Is it right?
Best regards
Hi @juliobc,
Regarding the ST implementation you might want to reach out to the ST community as well.
If I understand the code sequence correctly:
__SEV(); // Force the event to be set
__WFE(); // Force the event to be cleared without going to sleep
__WFE(); // Go to sleep
So, yes, running WFE
twice will clear all events that occured before. This looks strange to me as it causes race conditions with ISRs/events coming in.
In order to clear events that have been handled, properly, it might make sense to add the clearing part of the sequence before suspending the kernel. I.e.,
while (true) {
__SEV(); // Force the event to be set
__WFE(); // Force the event to be cleared without going to sleep
tc_wakeup = osKernelSuspend();
if (tc_wakeup > 0) {
This could reduce the amount of loop-repetitions just for clearing previous events from normal operation.
Cheers, Jonatan
Hi @JonatanAntoni
I am doing some test and I have a question. I modified the function to enter in stop mode to have only one __WFE() like this:
void HAL_PWR_EnterSTOPMode()
{
/* Set SLEEPDEEP bit of Cortex System Control Register */
SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
/* Request Wait For Event */
// __SEV();
// __WFE();
__WFE();
/* Reset SLEEPDEEP bit of Cortex System Control Register */
CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
}
and then HAL_PWR_EnterSTOPMode()
exit immediately. I think this is normal because any ISR fired before has set the event flag, but now I force clearing that flag before suspending the kernel like this:
while (true) {
__SEV(); // Force the event to be set
__WFE(); // Force the event to be cleared without going to sleep
tc_wakeup = osKernelSuspend();
if (tc_wakeup > 0) {
but now, HAL_PWR_EnterSTOPMode()
exit immediately again. Then I clear again that event flag just after kernel suspend like this:
while (true) {
__SEV(); // Force the event to be set
__WFE(); // Force the event to be cleared without going to sleep
tc_wakeup = osKernelSuspend();
__SEV(); // Force the event to be set
__WFE(); // Force the event to be cleared without going to sleep
if (tc_wakeup > 0) {
and now the Stop mode seems to work correctly!! Is like the function:
/// Suspend the RTOS Kernel scheduler.
uint32_t osKernelSuspend (void) {
uint32_t ticks;
EvrRtxKernelSuspend();
if (IsException() || IsIrqMasked()) {
EvrRtxKernelError((int32_t)osErrorISR);
ticks = 0U;
} else {
ticks = __svcKernelSuspend();
}
return ticks;
}
is setting the event flag. It is possible? Could I to see the event flag in debug in anywhere in the uvision IDE to verify that? I do not found it. Thank you very much
Hi @juliobc,
Debugging low power mode is a hard challenge. Once you put the device into low power mode the debug connection typically gets lost. Furthermore, the event flag
unfortunately is not exposed anywhere, it cannot be read by software nor by the debugger.
The SVC
will set the event flag
as well, yes. I think you need to disable interrupts before running osKernelSuspend
and enable it again after wakeup. The clearing sequence after osKernelSuspend
looks prone to a race to me.
I'd give this sequence a try:
SCB->SCR = SCB->SCR | SCB_SCR_SEVONPEND_Msk; // set SCR.SEVONPEND
while (true) {
__SEV(); // Force the event to be set
__WFE(); // Force the event to be cleared without going to sleep
__disable_irq();
tc_wakeup = osKernelSuspend();
if (tc_wakeup > 0) {
// program wakeup timer
__WFE();
}
// retrieve ticks passed during sleep
osKernelResume(ticks);
__enable_irq();
}
Hi @JonatanAntoni
I have a question about that sequence. When you call to osKernelSuspend(), then are you setting the event flag? If you force the event to be set when kernel suspends then _WFE() exit immediately. Is that correct?
Hi @juliobc,
We do not "force" the event flag to be set. When calling RTOS service functions we use SuperVisorCalls (SVC) to isolate the context. This implicitly sets the event flag. Hence, I suggest to disable interrupts. Once interrupts are disabled the RTOS services are executed without SVC. Thus the event flag should not get set anymore.
Cheers, Jonatan
Hi @JonatanAntoni ,
only for curiosity, I still studying this workaround 🤦♂️
The system is working but this could be improved. I can not disabe IRQ because I can miss any external interrupt while in sleep mode. I still thinking.
Maybe I could disable IRQ only when I go into kernel suspend? Like this:
SCB->SCR = SCB->SCR | SCB_SCR_SEVONPEND_Msk; // set SCR.SEVONPEND
while (true) {
__SEV(); // Force the event to be set
__WFE(); // Force the event to be cleared without going to sleep
__disable_irq();
tc_wakeup = osKernelSuspend();
__enable_irq();
if (tc_wakeup > 0) {
// program wakeup timer
__WFE();
}
// retrieve ticks passed during sleep
osKernelResume(ticks);
}
I have a question. The line
SCB->SCR = SCB->SCR | SCB_SCR_SEVONPEND_Msk; // set SCR.SEVONPEND
is required for __SEV();
to work fine? What is the effect if I comment that line?
Please consider raising such questions at https://community.arm.com/. This question is not CMSIS related and the experts around tick-less operation do not read here.
__WFE
should wake up the device once any interrupt pending bit gets set, even if serving interrupts is disabled at that point. If consecutive external interrupts arrive faster than the device is able to resume, they still can get lost.
Hi @JonatanAntoni
ok, I will go to ARM community Thank you very much for your support.
Hi all,
I have a tick-less operation like this:
When an interrupt is fired while stop mode I can wakeup and continue working in my app. But the problem is when the ISR is fired between osKernelSuspend() and EnterSTOPMode(). What is the behavior in that case?
I have this ISR:
Theorically, when the test_ISR() is fired Thread_test is resumed from blocked state. If test_ISR() is fired between osKernelSuspend() and EnterSTOPMode(), how can I to know if there is any thread right now in ready state to avoid enter in stop mode?
Best Regards