Skip to content
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

timer: cortex-m systick: add idle timer #63187

Merged
merged 1 commit into from
Nov 7, 2023

Conversation

niedzwiecki-dawid
Copy link
Member

@niedzwiecki-dawid niedzwiecki-dawid commented Sep 28, 2023

Some chips, that use Cortex-M SysTick as the system timer, disable a clock in a low power mode, that is the input for the SysTick e.g. STM32Fx family.

It blocks enabling power management for these chips. The wake-up function doesn't work and the time measurement is lost.

Add an additional IDLE timer that handles these functionality when the system is about to enter IDLE. It has to wake up the chip and update the cycle counter by time not measured by the SysTick. The IDLE timer has to support counter API (setting alarm and reading current value).

Related:
#61631
#60558
#19755

Copy link
Member

@erwango erwango left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for submitting this. I have a quite similar need for a different use case:

In other STM32 series which have a low power timer which helps counting tick in lp modes (stop modes in STM32 nomenclature), we would need the same functionality to enter deeper sleep states (standby mode) where even LP timer would lose its clock and only RTC could be used (common point with your use case).

My understanding is that to implement my use case, one option would be to reproduce the nearly* same piece of code in drivers/timer/stm32_lptim_timer.c. Then maybe others would need the same implementation in their own timer driver.

So here is my (not blocking) suggestion:
Since this piece of code is destined to be duplicated in multiple timer drivers, I wonder if it could be implemented as a "generic timer back up service", rather than a specific cortex_m_systick one.

*Note: in my case, I would not need an extra logic:
Using lptimer, I would be able to enter some of the sleep states and only the deepest one would need to trigger use of counter/RTC wakeup. So the idle parameter will not be sufficient and I would need way to distinguish next state: stop or standby.
I guess this extra functionality can be added later on in a "generic timer back up service".

@niedzwiecki-dawid
Copy link
Member Author

Thanks for the comment.

Very interesting case. However, I've tried to extract common code of the "back up service" for all timers and my conclusion is that it is not a good idea. 2 main points: the code uses a local variable, and it is important to read values of the two timers as soon as possible (no calculation between).

The one thing would make sense, I think I can change the config and chosen string to CORTEX_IDLE_TIMER, to use it by all drivers.

Please let me know what you think.

@niedzwiecki-dawid
Copy link
Member Author

Do you know who would be able to review this PR?

@erwango
Copy link
Member

erwango commented Oct 4, 2023

2 main points: the code uses a local variable, and it is important to read values of the two timers as soon as possible (no calculation between).

What about setting variable scope to global?

@erwango
Copy link
Member

erwango commented Oct 4, 2023

Do you know who would be able to review this PR?

@anangl and @andyross

@erwango
Copy link
Member

erwango commented Oct 4, 2023

and .. @teburd :) !

@niedzwiecki-dawid
Copy link
Member Author

Using lptimer, I would be able to enter some of the sleep states and only the deepest one would need to trigger use of counter/RTC wakeup. So the idle parameter will not be sufficient and I would need way to distinguish next state: stop or standby.

I've been thinking about your case. IMO, there is a simply way to handle that. You can compare the measurement of IDLE and system timers. If a difference is bigger than ~10%/1-2 IDLE timer ticks, it means the system timer was stopped and needs correction. Does it make sense?

@erwango
Copy link
Member

erwango commented Oct 6, 2023

Using lptimer, I would be able to enter some of the sleep states and only the deepest one would need to trigger use of counter/RTC wakeup. So the idle parameter will not be sufficient and I would need way to distinguish next state: stop or standby.

I've been thinking about your case. IMO, there is a simply way to handle that. You can compare the measurement of IDLE and system timers. If a difference is bigger than ~10%/1-2 IDLE timer ticks, it means the system timer was stopped and needs correction. Does it make sense?

Actually my point is that the timer to use depends on the sleep duration (defined by low power state residency). Since standby mode (and use of RTC counter) would require longer time to exit (and potentially also enter), I would go in standby mode only if time allows and, for shorter timeout periods, I would use STOP Mode / LPTIM. This knowledge is available in sys_clock_set_timeout caller.

@niedzwiecki-dawid
Copy link
Member Author

Make sense

@niedzwiecki-dawid
Copy link
Member Author

Friendly ping :-) Please review if you can, this change is very important for my work.

@teburd
Copy link
Collaborator

teburd commented Oct 12, 2023

This is a pretty significant change to the cortex-m timer, and that's a widely used critical piece of software in Zephyr.

I get the idea here, though I'm unsure about this idea of the timer depending on the counter API and driver.

@npitre is honestly probably the best person to review this

@teburd teburd requested a review from npitre October 12, 2023 15:07
@niedzwiecki-dawid
Copy link
Member Author

niedzwiecki-dawid commented Oct 13, 2023

This is a pretty significant change to the cortex-m timer, and that's a widely used critical piece of software in Zephyr.

I get the idea here, though I'm unsure about this idea of the timer depending on the counter API and driver.

@npitre is honestly probably the best person to review this

Thanks for your response.
That's true :-) However, the change impacts only users that add the idle timer. Each part of code I've added is inside ifdef #ifdef CONFIG_CORTEX_M_SYSTICK_IDLE_TIMER. Of course, it doesn't mean it can be buggy.

@npitre
Copy link
Collaborator

npitre commented Oct 14, 2023 via email

@niedzwiecki-dawid
Copy link
Member Author

niedzwiecki-dawid commented Oct 16, 2023

What does the idle_lock protect against?

When we enter the sys_clock_idle_exit function, the SysTick is already running. The LOAD register hasn’t been changed while setting the timeout, so we can not be sure about its value. That means the SysTick IRQ can occur any time. cycle_count is updated in the sys_clock_idle_exit so we can not be interrupted. The second important thing is that these two measurements

		counter_get_value(idle_timer, &idle_timer_post);
		systick_diff = cycle_count + elapsed() - cycle_pre_idle;

have to happen one after another, without any interruption.

EDIT: I can see now, that sys_clock_idle_exit is called with interrupts disabled, so that means idle_lock is not needed. Is that true?

@npitre
Copy link
Collaborator

npitre commented Oct 16, 2023

What does the idle_lock protect against?

When we enter the sys_clock_idle_exit function, the SysTick is already
running. The LOAD register hasn’t been changed while setting the timeout,
so we can not be sure about its value. That means the SysTick IRQ can
occur any time. cycle_count is updated in the sys_clock_idle_exit so
we can not be interrupted.

Still, my question remains. Unless you fear concurrent reentrancy into
sys_clock_idle_exit(), the idle_lock gives you no protection other than
simply disabling interrupts on the local CPU. I doubt there are many SMP
systems using the systick timer out there but still. It looks to me that
you'd better be using the existing lock instead.

Some chips, that use Cortex-M SysTick as the system timer, disable a
clock in a low power mode, that is the input for the SysTick e.g.
STM32Fx family.

It blocks enabling power management for these chips. The wake-up
function doesn't work and the time measurement is lost.

Add an additional IDLE timer that handles these functionality when the
system is about to enter IDLE. It has to wake up the chip and update the
cycle counter by time not measured by the SysTick. The IDLE timer has to
support counter API (setting alarm and reading current value).

Signed-off-by: Dawid Niedzwiecki <dawidn@google.com>
@niedzwiecki-dawid
Copy link
Member Author

Thanks for the responses. Disabling interrupts was what I needed, but after making sure the sys_clock_idle_exit is called with the interrupts disabled, the lock is not needed anymore.

Anyway, the existing lock couldn't be used, because the sys_clock_announce function is called, which calls the sys_clock_set_timeout function that uses the lock.

Copy link
Collaborator

@npitre npitre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand it all and I don't have access to affected platforms, but those changes are all confined inside CONFIG_CORTEX_M_SYSTICK_IDLE_TIMER so that is safe to those who don't need it.

@niedzwiecki-dawid
Copy link
Member Author

I don't fully understand it all and I don't have access to affected platforms, but those changes are all confined inside CONFIG_CORTEX_M_SYSTICK_IDLE_TIMER so that is safe to those who don't need it.

Thanks :-) Is there anything I should explain more in details?

@niedzwiecki-dawid
Copy link
Member Author

Hello :-) do you know anyone else who could review the PR and approve it? Or I should do something more.

@teburd
Copy link
Collaborator

teburd commented Oct 26, 2023

Hello :-) do you know anyone else who could review the PR and approve it? Or I should do something more.

This needs maintainer approval (@andyross)

@niedzwiecki-dawid
Copy link
Member Author

@andyross can I somehow speed up review? :-)

Copy link
Contributor

@andyross andyross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Came here on a ping. This all seems reasonable to me, a few notes, but the logic seems fine.

static uint32_t idle_timer_pre_idle;

/* Idle timer used for timer while entering the idle state */
static const struct device *idle_timer = DEVICE_DT_GET(DT_CHOSEN(zephyr_cortex_m_idle_timer));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that you're predicating this on a DTS counter device already, you could do this entirely with devicetree and avoid the extra kconfig.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, but I would like to keep the kconfig with the help for additional documentation. If you want, I can change the config to non user-configurable one as a follow-up PR. Does it make sense?

There are chips e.g. STMFX family that use SysTick as a system timer,
but SysTick is not clocked in low power mode. These chips usually have
another timer that is not stopped, but it has lower frequency e.g.
RTC, thus it can't be used as a main system timer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to document the required interface for this somewhere. "The idle timer must be a counter device chosen in device tree via...", etc...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirement is mentioned later in the help message

The chosen IDLE timer node has to support setting alarm from the counter API.

@carlescufi carlescufi merged commit 457d437 into zephyrproject-rtos:main Nov 7, 2023
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants