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

Implement power management pm for CPUs #6802

Closed
20 tasks
haukepetersen opened this issue Mar 28, 2017 · 26 comments
Closed
20 tasks

Implement power management pm for CPUs #6802

haukepetersen opened this issue Mar 28, 2017 · 26 comments
Assignees
Labels
Area: pm Area: (Low) power management Platform: ARM Platform: This PR/issue effects ARM-based platforms Platform: MIPS Platform: This PR/issue effects MIPS-based platforms Platform: MSP Platform: This PR/issue effects MSP-based platforms Platform: native Platform: This PR/issue effects the native platform State: stale State: The issue / PR has no activity for >185 days

Comments

@haukepetersen
Copy link
Contributor

haukepetersen commented Mar 28, 2017

This issue is created for tracking the pm implementation status and synchronize efforts.

Steps needed for implementation:

  • implement pm interface
  • use pm_layered where applicable
  • only allow appropriate power modes in peripheral drivers (e.g. use pm_block/pm_unblock)
  • verification of power modes (e.g. using external measurement equipment)

Implementation status:

  • atmega1281 (atmega_common?)
  • atmega2560 (atmega_common?)
  • atmega328p (atmega_common?)
  • cc2538
  • cc26x0
  • cc430 (msp430_common?)
  • ezr32wg
  • kinetis_common (-> @gebart, kinetis: Implement low power modes #7897)
  • lm4f120 (stellaris_common?)
  • lpc11u34
  • lpc1768
  • lpc2387
  • mips32r2_common
  • msp430fxyz (msp430_common?)
  • native (feasible?)
  • nrf5x_common
  • sam0_common
  • sam3
  • stm32_common
  • x86
@haukepetersen haukepetersen added Area: pm Area: (Low) power management Platform: ARM Platform: This PR/issue effects ARM-based platforms Platform: MIPS Platform: This PR/issue effects MIPS-based platforms Platform: MSP Platform: This PR/issue effects MSP-based platforms Platform: native Platform: This PR/issue effects the native platform x86 labels Mar 28, 2017
@jnohlgard
Copy link
Member

The Kinetis CPUs are best implemented as a shared PM driver with possibly some small differences handled in preprocessor conditionals.

@haukepetersen
Copy link
Contributor Author

jupp, agree. Updated the list

@vincent-d
Copy link
Member

How the periph driver are supposed to implement this? I think this is straightforward for spi, block/unblock are called in acquire/release. But how do we proceed for uart for instance. Are the block/unblock supposed to be called from uart_poweron/uart_poweroff? If yes, then we need a timeout mechanism or something from the layer above to power off the uart when nothing is expected anymore. Then who is supposed to manage a wake up pin?

Same kind of questions apply to xtimer, because with the current design, once xtimer is launched most implementation would unlikely never go to any power saving mode.

@haukepetersen
Copy link
Contributor Author

pratically, it is quite simple - as long as peripherals are active, they need to block any power state that prevents them from working:
UART: block low power when activated (init and poweron), and unblock when disabled (poweroff), no need for timeouts or anything. Effect: as long as e.g. the shell is used, low power modes are blocked. This is not a problem of the uart driver, but the using module (e.g. shell).
But it might be possible for some CPUs to go to sleep and be waken up by the UART, so these can handle the blocking differently...

timer: same thing: block lowpower modes when activated and unblock when powered off (stopped) on most platforms. This indeed blocks all low-power modes as long as the xtimer is used. This is a known issue or better known conceptual gap in our timer system design. @kaspar030 and me are working on a new proposal to solve this.

@vincent-d
Copy link
Member

That's what I understood, then.

So in practical, as soon as shell or xtimer is used, we cannot reach any low power mode anymore with the current design.

For the UART, some platform will need to setup the rx pin as an external interrupt before sleeping to be able to wake up from the UART. Do you see this kind of features as part of the UART driver or as an extra layer above ?

I'm glad this topic is moving forward.

@haukepetersen
Copy link
Contributor Author

For the UART, some platform will need to setup the rx pin as an external interrupt before sleeping to be able to wake up from the UART. Do you see this kind of features as part of the UART driver or as an extra layer above ?

I'd say this would be part of the uart driver, as it is up to the driver how it behaves in context with low power modes. But we need to be careful, as we don't want to run in situations, where we lose the first char send over the UART when we need to wakeup first...

@jnohlgard
Copy link
Member

I prefer to see this not as a design flaw of xtimer, but rather a misconfiguration of the boards.
The whole purpose of #5608 was to allow low power modes while using xtimer. Many platforms have low power timers which operate at lower frequencies than 1 MHz. On Kinetis platforms, all we need to do is use the LPT instead of PIT and we get full xtimer functionality in all low power modes. I don't know the STM32 timer hardware, but I guess there must be some module or timer clocking mode which remains active in low power modes for this kind of applications.

UART:
For battery powered applications, you likely won't be using the shell anyway, so reconfigure the UART to TX only mode if you need some printfs for debugging. For low power RX, the UART drivers should probably be updated to configure the pins like @vincent-d and @haukepetersen wrote, or other platform specific wake method (RXEDGIF on Kinetis). Don't go to sleep if the UART module is in the process of receiving or transmitting a byte (handle this via interrupts or check status bits).

SPI/I2C:
Only needs to remain awake while a transmission is in progress.

@haukepetersen
Copy link
Contributor Author

but rather a misconfiguration of the boards.

Yes and no. Sure you can configure the xtimer to use some low-power timer (typically clocked by a 32khz crystal). But this would mean that xtimer looses quite some accuracy compared to running it with some xx MHz timer. Though this might be sufficient for some scenarios, this might not be an option for others -> hence this is not a generic solution. What we need is IMHO a (or a set of) high level timer API(s), that can offer both... @kaspar030 and me are currently experimenting with some approaches, we will let the community know as soon as we have some results...

@roberthartung
Copy link
Member

A submodule-based approach is shown in #7597 . Once we agreed on that, I will showcase a pm implementation for the atmega1284p (just need to merge code here, it's already finished!)

@roberthartung
Copy link
Member

I proposed a different approach in #7648 which is not dependent on the current build system!

@roberthartung
Copy link
Member

A full implementation of pm_layered can be seen in my branch for the atmega1284p and periph on atmega_common

@benpicco
Copy link
Contributor

Is someone still working on this?

@roberthartung
Copy link
Member

@benpicco yes, what's your question?

@benpicco
Copy link
Contributor

benpicco commented Mar 6, 2019

@roberthartung Well I wanted to know what is still missing to make this usable - especially switching to sleep mode when no thread is scheduled and getting woken up by RTC/slow timer when the next task is scheduled.

But as I understood now, I should wait for the new ztimer API which should make it possible to implement this?

pm for devices that are not used/active would also be interesting.

@roberthartung
Copy link
Member

roberthartung commented Mar 6, 2019

@benpicco I am using this actively today already. As listed above, I have a fully working branch for pm_layered in atmega1284p.

PM for devices is quite hard. In general the developer should take care of this, by e.g. freeing i2c/spi/... which leads to pm_unblock for this periphs, and hopefully deeper sleep modes. However, this already did not work, due to RX of UART, which always kept blocking any sleep modes except idle. See #7974

@kaspar030
Copy link
Contributor

However, this already did not work, due to RX of UART, which always kept blocking any sleep modes except idle.

I think this needs API additions. Something like "uart_rx_pause()/unpause()", which could be called by stdio_uart_read(), or something in that direction. Something that can be done the same or very similar for the other periphs.

@benpicco
Copy link
Contributor

benpicco commented Mar 6, 2019

If the device supports wake on RX you would not even need that.
You could also configure the RX pin as GPIO and wake on that.

You'd lose the first character either way, but for many applications that's fine.

@kaspar030
Copy link
Contributor

If the device supports wake on RX you would not even need that.
You could also configure the RX pin as GPIO and wake on that.

You'd lose the first character either way, but for many applications that's fine.

Yes. It would still be nice to be able to control this in some define way. Going to sleep as soon as the prompt of a shell arrives would make it unusable. Been there...

I could imagine the shell staying awake for only a couple of seconds, then saying "sleeping now, press CTRL-D a couple of times to wake up, until '>' is shown ". So after the idle timer runs out, it needs to do something.
If there's be an "uart_rx_pause()/unpause()", IMO calling those from the read() wrapper would make the most sense. Having that return on timeout is a different problem, but doable.

@benpicco
Copy link
Contributor

benpicco commented Mar 6, 2019

The shell is special, it's a debug tool not the main use case.
I'd be fine with going to sleep as soon as the prompt of a shell arrives, but once you interact with it, it could issue a pm_block() and start a timer to pm_unblock() it after some seconds unless you interact with it again. But that would be entirely contained within the shell module.

This way devices would not waste battery waiting for a shell interaction that never happens, while you can still comfortably debug it (just press enter once to wake up the device).

@kaspar030
Copy link
Contributor

but once you interact with it, it could issue a pm_block() and start a timer to pm_unblock()

Yup, but unblock what? I mean, how do we determine the pm mode that the shell should block/unblock, supporting multiple stdio_ backends over many platforms? shell is using getchar() for reading. We could of course hack "SHELL_PM_SLEEP_MODE", set that to "UART_STDIO_SLEEP_MODE", somehow populate that depending on the uart or stdio configuration, .... but it feels at least ... brittle.
In the end, only the uart implementation (hopefully) knows what's its active mode.

I'm just realizing that file descriptors don't have a way to sleep/unsleep them, either. ;(

@kaspar030
Copy link
Contributor

I'm just realizing that file descriptors don't have a way to sleep/unsleep them, either. ;(

Ah, but as long as the MCU is doing something, it won't sleep anyways. Only when the shell is waiting in the prompt (in read()) the power mode might neet to be blocked.

@benpicco
Copy link
Contributor

benpicco commented Mar 6, 2019

Huh, is this not a general problem?
In the end the sleep modes are always coupled with peripherals that are still working in that mode.
So only peripherals should be able to call pm_block() / pm_unblock() - now I see how uart_rx_pause() / unpause() makes sense. (I'd rather call it uart_allow_suspend(bool) to make the meaning more clear. Then we could shut down the peripheral clock entirely if no TX is going on, assuming we can still wake it up on RX, but that again is implementation specific.)

@roberthartung
Copy link
Member

In my case I am just unblocking/blocking the UART used in the shell and that works just fine.

@kaspar030
Copy link
Contributor

In my case I am just unblocking/blocking the UART used in the shell and that works just fine.

that's with a hard_coded value for pm_block()/unblock()?

@roberthartung
Copy link
Member

In my case I am just unblocking/blocking the UART used in the shell and that works just fine.

that's with a hard_coded value for pm_block()/unblock()?

pm_block/pm_unblock is implemented in the periph driver for uart. so you only have to know which UART is used. All I do is uart_poweroff(0); and uart_poweron(0);. See my old [https://github.com/ibr-cm/RIOT/blob/90870b47a6f33a0be42f468007834a4666bb95bb/tests/pm_wakeup_rtt/main.c](test code) and pm code.

@stale
Copy link

stale bot commented Sep 7, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

@stale stale bot added the State: stale State: The issue / PR has no activity for >185 days label Sep 7, 2019
@stale stale bot closed this as completed Oct 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: pm Area: (Low) power management Platform: ARM Platform: This PR/issue effects ARM-based platforms Platform: MIPS Platform: This PR/issue effects MIPS-based platforms Platform: MSP Platform: This PR/issue effects MSP-based platforms Platform: native Platform: This PR/issue effects the native platform State: stale State: The issue / PR has no activity for >185 days
Projects
None yet
Development

No branches or pull requests

7 participants