-
Notifications
You must be signed in to change notification settings - Fork 2k
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
ztimer: add ztimer_ondemand module for implicit power management #17607
Changes from all commits
904dc01
a228ca7
4013dff
9fbb4d3
90b2f31
5818e5e
d22e078
a1ee7a5
88a9f4b
bf5dd34
96b7988
2b53f35
8d6d6f2
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 |
---|---|---|
|
@@ -361,6 +361,20 @@ typedef struct { | |
* @param clock ztimer clock to cancel a pending alarm, if any | ||
*/ | ||
void (*cancel)(ztimer_clock_t *clock); | ||
|
||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN | ||
/** | ||
* @brief Starts the underlying timer | ||
* @param clock ztimer clock to start | ||
*/ | ||
void (*start)(ztimer_clock_t *clock); | ||
|
||
/** | ||
* @brief Stops the underlying timer | ||
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 should not force the timer to be stopped but free the timer to be stopable when powermanagement requires that (that should be reflected in the name) (stoping and starting timer takes time sometimes we might not even know the amount (the compensation value might only be good for a average case) (e.g. if stating and stopping requires a crystal to get running or some mcu that have register sync cycles or need clock phases to align) 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. Yeah, but in that case 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. i just looked at it (seems like the nrf52 has its own PM task running in hardware), but there are other that do not, for nrf52 it might make sense to immediately stop, but for other mcu it might just free up a pmlevel. mcu not have these things if you stop their timer (for pm) they might lose the status of that timer therefor the start has to reinitialize the timer some cant be stopped (pipico) (but if you do not need the timer you may not keep the osc running when it going to sleep) 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. btw there is a possible race condition in the nrf52 hardware os that might throw of your timing if you immediately stop at 0 users (the manual warns about starting and stopping in the same PCLK16M (stop will win) ) so stopping should wait until you are quiet sure it will no be started soon. 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.
I expect
I this case the -- What is your suggestion how to improve this timer system? I'm still convinced that the direct interaction with 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. I did the same experiment on #18821 with the
-> the introduced -- And the same experiment on #18780 (with #18814 applied):
This also includes the interaction with 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. but you need to do the pm part of the pm ( which seems to be ~1us) while u do not need to to the starting and stopping of timer another 1 to 1.5 us (if you just stop you don't do pm) pm and timer stoping are different things on most mcus. Do both on your example mcu -> 2 us, do one without the other 1 us. -> have different functions for that 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. Does this need to be sorted out necessarily for this PR to go in (vs. adjusting the implementation in a follow-up)? I don't understand all the discussion's details, but it appears to me that it's about performance of a weird use case (a loop repeatedly setting a timer would be better off setting a periodic timer and clearing it on a break), whereas this PR as a whole s necessary to make ztimer usable correctly. 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, just having the empty And introducing such interfaces without any implementation behind can result in a situation where future implementations don't work with that introduced interface. I really don't want that either. 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. And the results of the test program executed on an weact-f411ce board on current master:
I'm afraid, I don't have further boards at hand. |
||
* @param clock ztimer clock to stop | ||
*/ | ||
void (*stop)(ztimer_clock_t *clock); | ||
#endif | ||
} ztimer_ops_t; | ||
|
||
/** | ||
|
@@ -373,14 +387,21 @@ struct ztimer_clock { | |
uint16_t adjust_set; /**< will be subtracted on every set() */ | ||
uint16_t adjust_sleep; /**< will be subtracted on every sleep(), | ||
in addition to adjust_set */ | ||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN | ||
uint16_t adjust_clock_start; /**< will be subtracted on every set(), | ||
if the underlying periph is in | ||
stopped state */ | ||
uint16_t users; /**< user count of this clock */ | ||
#endif | ||
#if MODULE_ZTIMER_EXTEND || MODULE_ZTIMER_NOW64 || DOXYGEN | ||
/* values used for checkpointed intervals and 32bit extension */ | ||
uint32_t max_value; /**< maximum relative timer value */ | ||
uint32_t lower_last; /**< timer value at last now() call */ | ||
ztimer_now_t checkpoint; /**< cumulated time at last now() call */ | ||
#endif | ||
#if MODULE_PM_LAYERED || DOXYGEN | ||
uint8_t block_pm_mode; /**< min. pm mode to block for the clock to run */ | ||
#if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND || DOXYGEN | ||
uint8_t block_pm_mode; /**< min. pm mode to block for the clock to run | ||
don't use in combination with ztimer_ondemand! */ | ||
#endif | ||
}; | ||
|
||
|
@@ -393,6 +414,47 @@ struct ztimer_clock { | |
void ztimer_handler(ztimer_clock_t *clock); | ||
|
||
/* User API */ | ||
/** | ||
* @brief Acquire a clock | ||
* | ||
* This will indicate the the underlying clock is required to be running. | ||
* If time differences are measured using @ref ztimer_now this will make | ||
* sure ztimer won't turn of the clock source. | ||
* | ||
* @param[in] clock ztimer clock to operate on | ||
* | ||
* @return true if this was the first acquisition on this this clock | ||
*/ | ||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN | ||
bool ztimer_acquire(ztimer_clock_t *clock); | ||
#else | ||
static inline bool ztimer_acquire(ztimer_clock_t *clock) | ||
{ | ||
(void) clock; | ||
return false; | ||
} | ||
#endif | ||
|
||
/** | ||
* @brief Release a clock | ||
* | ||
* This will indicate the the underlying clock isn't required to be running | ||
* anymore and may be turned off. | ||
* | ||
* @param[in] clock ztimer clock to operate on | ||
* | ||
* @return true if this was the last clock user | ||
*/ | ||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN | ||
bool ztimer_release(ztimer_clock_t *clock); | ||
#else | ||
static inline bool ztimer_release(ztimer_clock_t *clock) | ||
{ | ||
(void) clock; | ||
return false; | ||
} | ||
#endif | ||
|
||
/** | ||
* @brief Set a timer on a clock | ||
* | ||
|
@@ -492,6 +554,17 @@ int ztimer_msg_receive_timeout(ztimer_clock_t *clock, msg_t *msg, | |
*/ | ||
ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock); | ||
|
||
/** | ||
* @brief asserts the given clock to be active | ||
* | ||
* @internal | ||
* | ||
* @note This function is internal | ||
* | ||
* @param[in] clock ztimer clock to operate on | ||
*/ | ||
void _ztimer_assert_clock_active(ztimer_clock_t *clock); | ||
|
||
/** | ||
* @brief Get the current time from a clock | ||
* | ||
|
@@ -511,14 +584,18 @@ ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock); | |
* * Two values can only be compared when the clock has been continuously | ||
* active between the first and the second reading. | ||
* | ||
* A clock is guaranteed to be active from the time any timer is set (the | ||
* first opportunity to get a "now" value from it is the return value of @ref | ||
* ztimer_set) until the time the timer's callback returns. The clock also | ||
* stays active when timers are set back-to-back (which is the case when the | ||
* first timer's callback sets the second timer), or when they overlap (which | ||
* can be known by starting the second timer and afterwards observing that | ||
* @ref ztimer_is_set or @ref ztimer_remove returns true in a low-priority | ||
* context). | ||
* Calling @ref ztimer_acquire before using `ztimer_now()` is the preferred | ||
* way to guarantee that a clock is continuously active. Make sure to call | ||
* the corresponding @ref ztimer_release after the last `ztimer_now()` call. | ||
* | ||
* A clock is also guaranteed to be active from the time any timer is set | ||
* (the first opportunity to get a "now" value from it is the return value of | ||
* @ref ztimer_set) until the time the timer's callback returns. The clock | ||
* also stays active when timers are set back-to-back (which is the case when | ||
* the first timer's callback sets the second timer), or when they overlap | ||
* (which can be known by starting the second timer and afterwards observing | ||
* that @ref ztimer_is_set or @ref ztimer_remove returns true in a | ||
* low-priority context). | ||
* | ||
* In contrast, the clock is not guaranteed to be active if a timer is | ||
* removed and then a second one is started (even if the thread does not | ||
|
@@ -593,12 +670,19 @@ ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock); | |
* unrelated components are altered that change the systems's power | ||
* management behavior. | ||
* | ||
* @warning Make sure to call @ref ztimer_acquire(@p clock) before fetching | ||
* the clock's current time. | ||
jue89 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* @param[in] clock ztimer clock to operate on | ||
* | ||
* @return Current count on @p clock | ||
*/ | ||
static inline ztimer_now_t ztimer_now(ztimer_clock_t *clock) | ||
{ | ||
#if MODULE_ZTIMER_ONDEMAND && DEVELHELP | ||
_ztimer_assert_clock_active(clock); | ||
#endif | ||
|
||
#if MODULE_ZTIMER_NOW64 | ||
if (1) { | ||
#elif MODULE_ZTIMER_EXTEND | ||
|
@@ -627,6 +711,10 @@ static inline ztimer_now_t ztimer_now(ztimer_clock_t *clock) | |
* If the time (@p last_wakeup + @p period) has already passed, the function | ||
* sets @p last_wakeup to @p last_wakeup + @p period and returns immediately. | ||
* | ||
* @warning Make sure to call @ref ztimer_acquire(@p clock) before making use | ||
* of @ref ztimer_periodic_wakeup. After usage | ||
* @ref ztimer_release(@p clock) should be called. | ||
* | ||
* @param[in] clock ztimer clock to operate on | ||
* @param[in] last_wakeup base time stamp for the wakeup | ||
* @param[in] period time in ticks that will be added to @p last_wakeup | ||
|
@@ -652,11 +740,13 @@ void ztimer_sleep(ztimer_clock_t *clock, uint32_t duration); | |
*/ | ||
static inline void ztimer_spin(ztimer_clock_t *clock, uint32_t duration) | ||
{ | ||
ztimer_acquire(clock); | ||
uint32_t end = ztimer_now(clock) + duration; | ||
|
||
/* Rely on integer overflow. `end - now` will be smaller than `duration`, | ||
* counting down, until it underflows to UINT32_MAX. Loop ends then. */ | ||
while ((end - ztimer_now(clock)) <= duration) {} | ||
ztimer_release(clock); | ||
} | ||
|
||
/** | ||
|
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.
Let's do some bike shedding: Why not
ztimer_pm
instead ofztimer_ondemand
?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 think ondemand is a little more verbose what this pseudo module actually does. Boards making use of the driver would pull in
ztimer_ondemand_timer
ifperiph_timer
shall be turned on and off. The interaction with power management will only happen indirectly throughperiph_timer
.But of course this is only my point of view. I'm completely open to other suggestions! I don't have any strong feelings regarding the name ;-)