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

periph/spi: power regression #6652

Merged
merged 1 commit into from
May 15, 2017
Merged

Conversation

immesys
Copy link
Contributor

@immesys immesys commented Feb 24, 2017

This PR is more of a request for comments, although it does merge and it does work. To be honest I need help working out why the problem exists in the first place.

#4780 reworked the SPI interface but also introduced a massive power consumption regression. Before the PR, idle power on the SAMR21 was at 2.7 uA on my platform. After the PR the deep sleep (lowest power mode) is at 1.5 mA.

I rewrote the code several times blending in parts of the old code and parts of the new code and eventually discovered the following minimal change to get the old power behavior back. This is actually two changes in one. The MISO pin as IN_PD is a regression same as last time in #5868, but that's only about 65uA, not 1500uA.

Changing the code to not disable the SPI device, however, fixes the problem. We also need to then introduce code to disable it during acquire so that the baud rate changes don't require sync, but the main change is removing the shutdown in spi_release.

To be clear: without this patch, deepest sleep = 1.5mA, with the patch, deepest sleep = 2.7uA on a SAMR21 with no other significant components on the board.

But no matter how long I stare at the code I don't really see what is wrong with what is there or why the patch makes such a big difference. I assure you my testing methodology is sound, and I have verified that the device works properly (radio TX/RX) with or without the patch.

Hopefully @haukepetersen or @kaspar030 can help here?

/* disable device and put it back to sleep */
dev(bus)->CTRLA.reg &= ~(SERCOM_SPI_CTRLA_ENABLE);
while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {}
poweroff(bus);
Copy link
Member

Choose a reason for hiding this comment

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

Does it make any difference if you leave the poweroff(bus); line in?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think I tested that explicitly. I'll be back in the lab in about 4 hours and I'll test that then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you leave the poweroff(bus) in there, the power consumption remains low. It appears that it is clearing ENABLE that causes problems.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Furthermore if you put the first two lines back in (clearing ENABLE) but not the poweroff(bus), the power consumption goes high. So I don't think it is a poweroff() happening too soon after disable.

@@ -117,6 +118,10 @@ int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
/* power on the device */
poweron(bus);

/* disable the device */
dev(bus)->CTRLA.reg &= ~(SERCOM_SPI_CTRLA_ENABLE);
while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {}
Copy link
Member

Choose a reason for hiding this comment

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

Taken the fact of basically moving this from spi_release to spi_aquire makes me wonder if there is some combination where the SPI device gets configured but is enabled.

The following registers are enable-protected, meaning that they can only be written when the SPI is disabled (CTRL.ENABLE is zero):

  • Control A register (CTRLA), except Enable (CTRLA.ENABLE) and Software Reset (CTRLA.SWRST)
  • Control B register (CTRLB), except Receiver Enable (CTRLB.RXEN)
  • Baud register (BAUD)
  • Address register (ADDR)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I am understanding you right, a counterexample is that if I put the disable inside the spi_release, but leave the disable also in spi_acquire, the power consumption goes up again. So I don't think it is the fact that spi_acquire is being called while the device is enabled.

@jnohlgard
Copy link
Member

I have seen a similar issue with the SPI on Kinetis. There is a leakage current of some hundreds of microamps if the clock gate to the SPI module is turned off without first telling the SPI hardware via the module disable bit in one of the control registers. It's a completely different CPU, but I imagine on that hardware at least that the spi module needs a few clock cycles to turn off properly, which it can't of the clock is just gated off. Maybe there's something similar at play here?

@immesys
Copy link
Contributor Author

immesys commented Feb 25, 2017

@gebart but if you put the disable lines in spi_release but do not call poweroff() the power consumption still goes high. It seems it is the disabling of the SPI device not the clock domain that is causing the problems.

@olegart
Copy link
Contributor

olegart commented Feb 25, 2017

Hi

It is NOT SPI-specific issue and it is NOT leakage current.

If you have any digital input with integrated Schmitt trigger (most DIs have it) connected to MCU GPIO without external pull-up/pull-down and then switch MCU to STOP or STANDBY with GPIOs disabled (in AIN or other High-Z mode), this input will be de facto left floated. Which means, Schmitt trigger will switch randomly between 0 and 1 according to some external noise on a pin, consuming a lot of energy — as CMOS logic consumes when it switches.

To achieve lowest possible power consumption, you have to:
a) do not left any MCU GPIOs without external pull-up/pull-down as Digital IN, their behavior will be as described above. Switch them to AIN, DOUT, DIN with pull-up, etc.

b) if you have any external devices connected to MCU, DO NOT switch corresponding digital outputs to High-Z mode. That means, for SPI devices keep NSS high, SCK and MOSI high or low, for UART devices keep TX high, et cetera.

c) if you need STANDBY mode (when GPIO is unpowered in most MCUs), use external pull-ups or pull-downs.

After that, you do not need to keep any specific interfaces (SPI, UART, etc.) clocked or even powered in sleep.

We implemented automatic configuration of commonly used interfaces in our firmware, check lpm_before_i_go_to_sleep() here: https://github.com/unwireddevices/RIOT/blob/stm32l1_lpm/cpu/stm32l1/lpm_arch.c

With STM32L151CCU6, we have 1.65 uA in STOP + RTC mode.

@immesys
Copy link
Contributor Author

immesys commented Feb 25, 2017

@olegart with respect, I am well aware of that phenomenon, you are preaching to the choir. That only applies to the MISO line, and it is only responsible for about 70uA of current. It was I who filed #5868.

I put a note in this diff saying that while changing the MISO to IN_PD was necessary and does reduce power consumption, that doing so in isolation only removes 70uA.

To be clear, that was the very first line I changed, and the power went from 1530 +- 10 uA to about 1440 +- uA so there is clearly something else at play.

Also @olegart, our system current with this patch is 2.56 +- 0.02 uA, without needing a manual "stop-the-world" function.

So with respect, this definitely IS an SPI issue, and it probably is not just a floating pin.

@immesys
Copy link
Contributor Author

immesys commented Feb 25, 2017

But @olegart does make a good point that there is no benefit to disabling the SPI module on spi_release. Sure we can remove the APBC clock but do we need the disable? We can just disable it in acquire before we mess with the baud.

If I made a table of empirical power consumption in every sleep mode on the SAMR21 with and without disabling the SPI module, would that be enough to convince the maintainers that we should merge a variant of this? My hunch is that it doesn't save power and it clearly causes problems.

@olegart
Copy link
Contributor

olegart commented Feb 25, 2017

Oh, yes. my mistake. Sincere apologies.

As far as I understand Atmel SAM architecture, there's no need to configure PU/PD on unused pins; disabling input buffer (setting INEN bit to 0) when CPU is going to sleep is enough. Also, it should be the preferred method of reducing power consumption — PD may cause additional power consumption in sleep if there's an external pull-up for some reason.

Those actions should be taken by power manager, not SPI driver, more or less as we implemented it.

Regarding SPI power consumption, additional tests are needed to understand the reason behind it. Disabling peripheral should not cause power drain by itself.

As I understood, you don't have external SPI devices connected. Can you please switch all GPIOs in use by SPI (MISO, MOSI, NSS, SCK) manually to digital input mode with INEN = 0 before putting MCU to sleep and check power consumption again?

@immesys
Copy link
Contributor Author

immesys commented Feb 25, 2017

The SAMR21 has a radio connected on the SPI bus internally in the package, so the bus is used.

@olegart
Copy link
Contributor

olegart commented Feb 25, 2017

Ok. so you need to manually configure before sleep:

  • SPI peripheral disabled, SPI clock disabled
  • NSS = digital out, 1 (I believe they use inverted chip select logic, as everyone else)
  • MOSI = digital out, 0
  • SCK = digital out, 0
  • MISO = digital in, PD or, better, INEN = 0

What's the power consumption in this case?

If it's down to 2 uA, it's clearly GPIO issue. If not, it's probably internal SPI issue.

@immesys
Copy link
Contributor Author

immesys commented Feb 25, 2017

With:

  • SPI peripheral enabled, SPI clock disabled (APBCMASK)
  • NSS, MOSI, SCK digital out
  • MISO digital in with PD

Power consumption: 2.56uA

With:

  • SPI peripheral disabled, SPI clock disabled (APBCMASK)
  • NSS, MOSI, SCK digital out
  • MISO digital in with PD

Power consumption: 1.4 mA

@jnohlgard
Copy link
Member

I would love to see a table of empirical measurements of the power consumption.
Btw, what do you get if you disable the module but keep the spi clock domain enabled?

@immesys
Copy link
Contributor Author

immesys commented May 10, 2017

Did anybody fix this in the latest release or is the problem still there?

@immesys
Copy link
Contributor Author

immesys commented May 10, 2017

Ok I verified the problem is still there in 2017.04. Are no power consumption tests done on a release? How could something this massive slip through?

I have rebased this fix on top of 2017.04.

@haukepetersen
Copy link
Contributor

Sorry for jumping into this discussion so late.

Are no power consumption tests done on a release?

Not really (or only very sporadically on the iotlab). This is a huge problem that we are aware off and that we are working on (but not quite the priority that I would wish for). The goal is indeed to include power measurements into the CI system, and as the next-gen CI (Murdock 2) is ready now, this is one of the next steps. Also there are some other conceptual gaps we have to close, most prominently xtimer preventing almost all platforms from going to deep sleep...

Regarding this issue: seems like I missed enabling the PD resistor for the MISO line when remodeling the SPI driver, there was no reason besides slppyness on my side for this -> #7037 for quick re-merge

The issue with disabling the peripheral before clock-gating is another topic. From the datasheet I was under the impression, that we would achieve the best energy savings when properly disabling the peripheral before clock gating it. But I guess if practice shows that this makes matters worse, I'd say we go with the proposed solution and only disable the peripheral for a short amount of time while configuring the bus clock speed. @immesys would this work?

@immesys
Copy link
Contributor Author

immesys commented May 11, 2017

Yeah, the PR in its form here is exactly what I use on my systems and I get low power out of it. The SPI also works well without any problems.

We have a set of changes to xtimer that make it suitable for low power on samr21, if you are interested I could try put together a PR for your perusal. The biggest is that switch the source to OSCULP using RTT instead of a high power timer.

@haukepetersen
Copy link
Contributor

Oh, missed yesterday that the PR is already doing what I wrote (miss-read the diff and somehow had in mind that the disable is done in spi_init...), so never mind.

I would be o.k. to merge this PR (almost) as is -> just the // style comment should be changed to the /* */ style used in RIOT. So either fixing the comment and we close #7037 or you remove the two lines and we merge #7037 - I don't care.

@@ -102,7 +102,8 @@ void spi_init(spi_t bus)

void spi_init_pins(spi_t bus)
{
gpio_init(spi_config[bus].miso_pin, GPIO_IN);
//MISO must always have PD/PU, see #5968. This is a ~65uA difference
Copy link
Contributor

Choose a reason for hiding this comment

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

for github formalities: please change to c-style comment, or remove these two lines in favor of #7037.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, fixed. I changed this comment style and combined the commits for cleaner merging.

@thomaseichinger thomaseichinger added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label May 12, 2017
@thomaseichinger
Copy link
Member

@immesys thanks for putting pressure on the low power aspect. I think it makes sense to go with the full change set here.

CI tests passed, as soon as @haukepetersen approves the your fixes we are ready to go.

@kaspar030 should we port this to the 2017.04 branch too?

@haukepetersen
Copy link
Contributor

ACK and to

@haukepetersen haukepetersen merged commit 5d5d4dc into RIOT-OS:master May 15, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants