aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Henrik Tjäder <henrik@grepit.se> 2021-12-14 21:52:57 +0100
committerGravatar Henrik Tjäder <henrik@grepit.se> 2021-12-14 22:18:17 +0100
commit8c8f7f12c3bfc132b7fad9df80559e474ed66b66 (patch)
treeeedf420265c65ec5430e3c8bcddd5e8ca73db86a
parent3741d431bed5a4799c7fdb20de5950a0964569e5 (diff)
downloadrtic-8c8f7f12c3bfc132b7fad9df80559e474ed66b66.tar.gz
rtic-8c8f7f12c3bfc132b7fad9df80559e474ed66b66.tar.zst
rtic-8c8f7f12c3bfc132b7fad9df80559e474ed66b66.zip
Idle: Switch to NOP instead of WFI
Add example how to get old WFI behaviour
-rw-r--r--book/en/src/by-example/app_idle.md31
-rw-r--r--ci/expected/idle-wfi.run2
-rw-r--r--examples/idle-wfi.rs47
-rw-r--r--macros/src/codegen/idle.rs2
-rw-r--r--macros/src/codegen/pre_init.rs7
-rw-r--r--src/export.rs1
6 files changed, 77 insertions, 13 deletions
diff --git a/book/en/src/by-example/app_idle.md b/book/en/src/by-example/app_idle.md
index 1eb14722..66f40497 100644
--- a/book/en/src/by-example/app_idle.md
+++ b/book/en/src/by-example/app_idle.md
@@ -8,11 +8,6 @@ When present, the runtime will execute the `idle` task after `init`. Unlike
`init`, `idle` will run *with interrupts enabled* and it's not allowed to return
so it must run forever.
-When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and
-then sends the microcontroller to sleep after running `init`.
-
-[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
-
Like in `init`, locally declared resources will have `'static` lifetimes that are safe to access.
The example below shows that `idle` runs after `init`.
@@ -25,3 +20,29 @@ The example below shows that `idle` runs after `init`.
$ cargo run --target thumbv7m-none-eabi --example idle
{{#include ../../../../ci/expected/idle.run}}
```
+
+By default the RTIC `idle` task does not try to optimise for any specific targets.
+
+A common useful optimisation is to enable the [SLEEPONEXIT] and allow the MCU
+to enter sleep when reaching `idle`.
+
+>**Caution** some hardware unless configured disables the debug unit during sleep mode.
+>
+>Consult your hardware specific documentation as this is outside the scope of RTIC.
+
+The following example shows how to enable sleep by setting the
+[`SLEEPONEXIT`][SLEEPONEXIT] and providing a custom `idle` task replacing the
+default [`nop()`][NOP] with [`wfi()`][WFI].
+
+[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
+[WFI]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/WFI
+[NOP]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/NOP
+
+``` rust
+{{#include ../../../../examples/idle-wfi.rs}}
+```
+
+``` console
+$ cargo run --target thumbv7m-none-eabi --example idle-wfi
+{{#include ../../../../ci/expected/idle-wfi.run}}
+```
diff --git a/ci/expected/idle-wfi.run b/ci/expected/idle-wfi.run
new file mode 100644
index 00000000..43077763
--- /dev/null
+++ b/ci/expected/idle-wfi.run
@@ -0,0 +1,2 @@
+init
+idle
diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs
new file mode 100644
index 00000000..4a8a8dee
--- /dev/null
+++ b/examples/idle-wfi.rs
@@ -0,0 +1,47 @@
+//! examples/idle-wfi.rs
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use panic_semihosting as _;
+
+#[rtic::app(device = lm3s6965)]
+mod app {
+ use cortex_m_semihosting::{debug, hprintln};
+
+ #[shared]
+ struct Shared {}
+
+ #[local]
+ struct Local {}
+
+ #[init]
+ fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) {
+ hprintln!("init").unwrap();
+
+ // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts
+ // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
+ cx.core.SCB.set_sleepdeep();
+
+ (Shared {}, Local {}, init::Monotonics())
+ }
+
+ #[idle(local = [x: u32 = 0])]
+ fn idle(cx: idle::Context) -> ! {
+ // Locals in idle have lifetime 'static
+ let _x: &'static mut u32 = cx.local.x;
+
+ hprintln!("idle").unwrap();
+
+ debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
+
+ loop {
+ // Now Wait For Interrupt is used instead of a busy-wait loop
+ // to allow MCU to sleep between interrupts
+ // https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/WFI
+ rtic::export::wfi()
+ }
+ }
+}
diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs
index d6539316..0dededa4 100644
--- a/macros/src/codegen/idle.rs
+++ b/macros/src/codegen/idle.rs
@@ -85,7 +85,7 @@ pub fn codegen(
vec![],
None,
quote!(loop {
- rtic::export::wfi()
+ rtic::export::nop()
}),
)
}
diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs
index 42cc0552..7aaf20fc 100644
--- a/macros/src/codegen/pre_init.rs
+++ b/macros/src/codegen/pre_init.rs
@@ -122,12 +122,5 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
));
}
}
-
- // If there's no user `#[idle]` then optimize returning from interrupt handlers
- if app.idle.is_none() {
- // Set SLEEPONEXIT bit to enter sleep mode when returning from ISR
- stmts.push(quote!(core.SCB.scr.modify(|r| r | 1 << 1);));
- }
-
stmts
}
diff --git a/src/export.rs b/src/export.rs
index 48a7d06e..a124c78b 100644
--- a/src/export.rs
+++ b/src/export.rs
@@ -6,6 +6,7 @@ use core::{
pub use crate::tq::{NotReady, TimerQueue};
pub use bare_metal::CriticalSection;
pub use cortex_m::{
+ asm::nop,
asm::wfi,
interrupt,
peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST},