-
Notifications
You must be signed in to change notification settings - Fork 3k
Make HAL & US tickers IRQ and idle safe #4768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,22 +26,13 @@ TIM_HandleTypeDef TimMasterHandle; | |
volatile uint32_t SlaveCounter = 0; | ||
volatile uint32_t oc_int_part = 0; | ||
|
||
static int us_ticker_inited = 0; | ||
|
||
void us_ticker_init(void) | ||
{ | ||
if (us_ticker_inited) return; | ||
us_ticker_inited = 1; | ||
|
||
TimMasterHandle.Instance = TIM_MST; | ||
|
||
HAL_InitTick(0); // The passed value is not used | ||
/* NOTE: assuming that HAL tick has already been initialized! */ | ||
} | ||
|
||
uint32_t us_ticker_read() | ||
{ | ||
if (!us_ticker_inited) us_ticker_init(); | ||
|
||
uint16_t cntH_old, cntH, cntL; | ||
do { | ||
cntH_old = SlaveCounter; | ||
|
@@ -73,7 +64,7 @@ void us_ticker_set_interrupt(timestamp_t timestamp) | |
{ | ||
// NOTE: This function must be called with interrupts disabled to keep our | ||
// timer interrupt setup atomic | ||
TimMasterHandle.Instance = TIM_MST; | ||
|
||
// Set new output compare value | ||
__HAL_TIM_SET_COMPARE(&TimMasterHandle, TIM_CHANNEL_1, timestamp & 0xFFFF); | ||
// Ensure the compare event starts clear | ||
|
@@ -178,20 +169,21 @@ void us_ticker_set_interrupt(timestamp_t timestamp) | |
|
||
void us_ticker_fire_interrupt(void) | ||
{ | ||
TimMasterHandle.Instance = TIM_MST; | ||
HAL_TIM_GenerateEvent(&TimMasterHandle, TIM_EVENTSOURCE_CC1); | ||
} | ||
|
||
void us_ticker_disable_interrupt(void) | ||
{ | ||
TimMasterHandle.Instance = TIM_MST; | ||
core_util_critical_section_enter(); | ||
__HAL_TIM_DISABLE_IT(&TimMasterHandle, TIM_IT_CC1); | ||
core_util_critical_section_exit(); | ||
} | ||
|
||
void us_ticker_clear_interrupt(void) | ||
{ | ||
TimMasterHandle.Instance = TIM_MST; | ||
__HAL_TIM_CLEAR_FLAG(&TimMasterHandle, TIM_FLAG_CC1); | ||
core_util_critical_section_enter(); | ||
__HAL_TIM_CLEAR_IT(&TimMasterHandle, TIM_IT_CC1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this MACRO change also required ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, it came in just because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok - better not change it here as this is not related to your change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just remember that when you are gonna change it for 32b to revert it again also for 16b. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
core_util_critical_section_exit(); | ||
} | ||
|
||
#endif // TIM_MST_16BIT |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,27 +23,21 @@ | |
|
||
TIM_HandleTypeDef TimMasterHandle; | ||
|
||
static int us_ticker_inited = 0; | ||
|
||
void us_ticker_init(void) | ||
{ | ||
if (us_ticker_inited) return; | ||
us_ticker_inited = 1; | ||
|
||
TimMasterHandle.Instance = TIM_MST; | ||
|
||
HAL_InitTick(0); // The passed value is not used | ||
/* NOTE: assuming that HAL tick has already been initialized! */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was not the case for a long time, we need to be sure that mbed_sdk_init is called before C++ objects creations happens, otherwise a timer/ticker object creation would fail. @0xc0170 I think that the above assumption is now ensured on all toolchains thanks to the recent rework on boot sequence - do you confirm ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that is correct. As you are referring to mbed OS 5 boot sequence. mbed 2 should also be fine. We shall add a test for this to be certain that sdk is called prior C++ ctor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shall run MBED2 tests with this change and we will see if this fails. - this will be part of non-reg when we think the PR is ready for testing |
||
} | ||
|
||
uint32_t us_ticker_read() | ||
{ | ||
if (!us_ticker_inited) us_ticker_init(); | ||
return TIM_MST->CNT; | ||
} | ||
|
||
void us_ticker_set_interrupt(timestamp_t timestamp) | ||
{ | ||
TimMasterHandle.Instance = TIM_MST; | ||
// NOTE: This function must be called with interrupts disabled to keep our | ||
// timer interrupt setup atomic | ||
|
||
// disable IT while we are handling the correct timestamp | ||
__HAL_TIM_DISABLE_IT(&TimMasterHandle, TIM_IT_CC1); | ||
// Set new output compare value | ||
|
@@ -59,14 +53,16 @@ void us_ticker_fire_interrupt(void) | |
|
||
void us_ticker_disable_interrupt(void) | ||
{ | ||
TimMasterHandle.Instance = TIM_MST; | ||
core_util_critical_section_enter(); | ||
__HAL_TIM_DISABLE_IT(&TimMasterHandle, TIM_IT_CC1); | ||
core_util_critical_section_exit(); | ||
} | ||
|
||
void us_ticker_clear_interrupt(void) | ||
{ | ||
TimMasterHandle.Instance = TIM_MST; | ||
core_util_critical_section_enter(); | ||
__HAL_TIM_CLEAR_IT(&TimMasterHandle, TIM_IT_CC1); | ||
core_util_critical_section_exit(); | ||
} | ||
|
||
#endif // !TIM_MST_16BIT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this be a case for all platforms, thus sleep and deepsleep in upper layer should include enter/exit critical section ?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably yes, but it depends on what these platforms are doing in
hal_sleep()
. If they are calling just a__WFI
and have no other constraints, then there is no need for a critical section.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my experience (although that's mainly on ARM-A), any WFI-type sleep function should always be called with interrupts disabled.
Otherwise there's a race condition - you may have been idle, which is why you were going into sleep, but as you start running the sleep routine, an interrupt wakes a thread, but then sleep goes ahead and does WFI, so we block until the next interrupt, and the woken thread doesn't run immediately.
If interrupts are disabled all the way from the "are we idle" check to the "WFI", then the sleep routine will correctly return immediately due to a pending interrupt.
So I think 0xc0170 is right - hal_sleep() should probably always be entered with interrupts disabled. (And they should have been disabled before checking if we were idle).
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunatley, in this moment
hal_sleep()
does not get called with interrupts already disabled! Not sure if this is a choice or a bug, up to you to decide.Anyway, as it is right now,
mbed-os-5.5
is not working correctly for non-debug builds, at least for STM-NUCLEO platforms.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useless to say, that this is a critical/blocking situation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you confirm this is a change in behaviour since the previous RTX? And why the distinction between debug and non-debug builds?
We'll need someone more familiar with the RTX port to comment. My analysis above was assuming a dedicated "idle" handler in an OS scheduler - it may be somewhat different if hal_sleep is just called as "normal" code in a minimum-priority idle thread.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can confirm that since
mbed-os-5.5
I am facing this issue, which I could resolve with the patch of this PR. I never checked RTX (neither v4 nor v5) source code to have a real confirmation for my assumption, though.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I further dare to confirm that:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the debug/non-debug distinction is that hal_sleep is not called at all in debug builds, for whatever reason. (Something to do with breaking semihosting mentioned in comments?)
Given that it's just called in a continuous loop from a normal minimum-priority thread, I believe that the correct pattern would be for hal_sleep to either be a simple WFE with interrupts enabled, else a more complete shutdown within a critical section using WFI - as per this patch.
So I think entry with interrupts enabled isn't necessarily wrong, but it feels like there's a clear trap for porters here. Doubly so given that hal_sleep isn't called in debug builds.
I still don't know what the change since 5.4 is though.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, change since 5.4 identified.
"release" build profile has always slept, "debug" build profile has never slept.
The behaviour of the "develop" (ie default) build profile changed between 5.4 and 5.5. In 5.4 it did not sleep - it was switched on NDEBUG. In 5.5 it does sleep - it is switched on MBED_DEBUG.
I've discussed the sleep a bit with @bulislaw, I think my previous comment is correct, so I'll recheck this and probably approve.