diff --git a/examples/timing_resources.rs b/examples/timing_resources.rs new file mode 100644 index 0000000000000000000000000000000000000000..f35c33ac2bb8e483f144d7ebbaf701ecf85e67c9 --- /dev/null +++ b/examples/timing_resources.rs @@ -0,0 +1,122 @@ +//! examples/timing_resources.rs + +// #![deny(unsafe_code)] +// #![deny(warnings)] +#![no_main] +#![no_std] + +use core::ptr::read_volatile; +use cortex_m::{asm, peripheral::DWT}; +use panic_halt as _; +use stm32f4::stm32f411; + +#[rtic::app(device = stm32f411)] +const APP: () = { + struct Resources { + // A resource + dwt: DWT, + } + + #[init] + fn init(mut cx: init::Context) -> init::LateResources { + // Initialize (enable) the monotonic timer (CYCCNT) + cx.core.DCB.enable_trace(); + cx.core.DWT.enable_cycle_counter(); + init::LateResources { dwt: cx.core.DWT } + } + + #[idle(resources = [dwt])] + fn idle(mut cx: idle::Context) -> ! { + unsafe { cx.resources.dwt.cyccnt.write(0) }; + asm::bkpt(); + rtic::pend(stm32f411::Interrupt::EXTI0); + asm::bkpt(); + loop { + continue; + } + } + + #[task(binds = EXTI0)] + fn exti0(_cx: exti0::Context) { + asm::bkpt(); + } +}; + +// Now we are going to have a look at the scheduling of RTIC tasks +// +// First create an objdump file: +// > cargo objdump --example timing_resources --release --features nightly -- --disassemble > timing_resources.objdump +// +// Lookup the EXTI0 symbol (RTIC binds the exti0 task to the interrupt vector). +// +// You should find something like: +// +// 08000232 <EXTI0>: +// 8000232: 00 be bkpt #0 +// 8000234: 00 20 movs r0, #0 +// 8000236: 80 f3 11 88 msr basepri, r0 +// 800023a: 70 47 bx lr +// +// The application triggers the `exti0` task from `idle`, let's see +// how that pans out. +// +// > cargo run --example timing_resources --release --features nightly +// Then continue to the first breakpoint instruction: +// (gdb) c +// timing_resources::idle (cx=...) at examples/timing_resources.rs:32 +// 32 asm::bkpt(); +// +// (gdb) x 0xe0001004 +// 0 +// +// Here we see, that we have successfully set the cycle counter to zero. +// The `rtic::pend(stm32f411::Interrupt::EXTI0)` "emulates" the +// arrival/triggering of an external interrupt associated with +// the `exti0` task. +// +// (gdb) c +// timing_resources::APP::EXTI0 () at examples/timing_resources.rs:13 +// 13 #[rtic::app(device = stm32f411)] +// +// Since `exti0` has a default prio = 1, it will preempt `idle` (at prio = 0), +// and the debugger breaks in the `exti0` task. +// +// (gdb) x 0xe0001004 +// +// [Your answer here] +// +// (gdb) disassemble +// +// [Your answer here] +// +// You should see that we hit the breakpoint in `exti0`, and +// that the code complies to the objdump EXTI disassembly. +// +// Confer to the document: +// https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/beginner-guide-on-interrupt-latency-and-interrupt-latency-of-the-arm-cortex-m-processors +// +// What was the software latency observed to enter the task? +// +// [Your answer here] +// +// Does RTIC infer any overhead? +// +// [Your answer here] +// +// Now we can continue to measure the round trip time. +// +// (gdb) c +// timing_resources::idle (cx=...) at examples/timing_resources.rs:34 +// 34 asm::bkpt(); +// +// (gdb) x 0xe0001004 +// +// [Your answer here] +// +// Looking at the EXTI0 (exti0) code, we see two additional +// instructions used to restore the BASEPRI register. +// This OH will be removed in next release of RTIC. +// So we can conclude RTIC to have a 2-cycle OH (in this case). +// (In the general case, as we will see later restoring BASEPRI +// is actually necessary so its just this corner case that is +// sub-optimal.) diff --git a/examples/timing_task.rs b/examples/timing_task.rs new file mode 100644 index 0000000000000000000000000000000000000000..9736b6084855089a6276cf6cd31e833a31f76829 --- /dev/null +++ b/examples/timing_task.rs @@ -0,0 +1,120 @@ +//! examples/timing_task.rs + +#![deny(warnings)] +#![no_main] +#![no_std] + +use cortex_m::{asm, peripheral::DWT}; +use panic_halt as _; +use stm32f4::stm32f411; + +#[rtic::app(device = stm32f411)] +const APP: () = { + struct Resources { + dwt: DWT, + } + + #[init] + fn init(mut cx: init::Context) -> init::LateResources { + // Initialize (enable) the monotonic timer (CYCCNT) + cx.core.DCB.enable_trace(); + cx.core.DWT.enable_cycle_counter(); + init::LateResources { dwt: cx.core.DWT } + } + + #[idle(resources = [dwt])] + fn idle(cx: idle::Context) -> ! { + unsafe { cx.resources.dwt.cyccnt.write(0) }; + asm::bkpt(); + rtic::pend(stm32f411::Interrupt::EXTI0); + asm::bkpt(); + loop { + continue; + } + } + + #[task(binds = EXTI0)] + fn exti0(_cx: exti0::Context) { + asm::bkpt(); + } +}; + +// Now we are going to have a look at the scheduling of RTIC tasks +// +// First create an objdump file: +// > cargo objdump --example timing_task --release --features nightly -- --disassemble > timing_task.objdump +// +// Lookup the EXTI0 symbol (RTIC binds the exti0 task to the interrupt vector). +// +// You should find something like: +// +// 08000232 <EXTI0>: +// 8000232: 00 be bkpt #0 +// 8000234: 00 20 movs r0, #0 +// 8000236: 80 f3 11 88 msr basepri, r0 +// 800023a: 70 47 bx lr +// +// The application triggers the `exti0` task from `idle`, let's see +// how that pans out. +// +// > cargo run --example timing_task --release --features nightly +// Then continue to the first breakpoint instruction: +// (gdb) c +// timing_task::idle (cx=...) at examples/timing_task.rs:28 +// 28 asm::bkpt(); +// +// (gdb) x 0xe0001004 +// 0 +// +// Here we see, that we have successfully set the cycle counter to zero. +// The `rtic::pend(stm32f411::Interrupt::EXTI0)` "emulates" the +// arrival/triggering of an external interrupt associated with +// the `exti0` task. +// +// (gdb) c +// timing_task::APP::EXTI0 () at examples/timing_task.rs:11 +// 11 #[rtic::app(device = stm32f411)] +// +// Since `exti0` has a default prio = 1, it will preempt `idle` (at prio = 0), +// and the debugger breaks in the `exti0` task. +// (Notice, RTIC translates logical priorities to hw priorities for you.) +// +// (gdb) x 0xe0001004 +// +// [Your answer here] +// +// (gdb) disassemble +// +// [Your answer here] +// +// You should see that we hit the breakpoint in `exti0`, and +// that the code complies to the objdump EXTI disassembly. +// +// Confer to the document: +// https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/beginner-guide-on-interrupt-latency-and-interrupt-latency-of-the-arm-cortex-m-processors +// +// What was the software latency observed to enter the task? +// +// [Your answer here] +// +// Does RTIC infer any overhead for launching the task? +// +// [Your answer here] +// +// Now we can continue to measure the round trip time. +// +// (gdb) c +// timing_resources::idle (cx=...) at examples/timing_resources.rs:34 +// 34 asm::bkpt(); +// +// (gdb) x 0xe0001004 +// +// [Your answer here] +// +// Looking at the EXTI0 (exti0) code, we see two additional +// instructions used to restore the BASEPRI register. +// This OH will be removed in next release of RTIC. +// So we can conclude RTIC to have a 2-cycle OH (in this case). +// (In the general case, as we will see later restoring BASEPRI +// is actually necessary so its just this corner case that is +// sub-optimal.) diff --git a/src/main.rs b/src/main.rs index 61992fe7a7b131997246067bfe33f2174767250a..bc85ce18358c7480181753a839d9eb6a9d7862d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,37 +15,19 @@ use stm32f4; #[rtic::app(device = stm32f4)] const APP: () = { #[init] - fn init(cx: init::Context) { - // Cortex-M peripherals - let _core: cortex_m::Peripherals = cx.core; - - // Device specific peripherals - // let _device: lm3s6965::Peripherals = cx.device; - - // // Safe access to local `static mut` variable - // let _x: &'static mut u32 = X; + fn init(_cx: init::Context) { + rtt_init_print!(); rprintln!("init"); } #[idle] fn idle(_cx: idle::Context) -> ! { - // // static mut X: u32 = 0; - - // // // Cortex-M peripherals - // // let _core: cortex_m::Peripherals = cx.core; - - // // // Device specific peripherals - // // // let _device: lm3s6965::Peripherals = cx.device; - - // // // Safe access to local `static mut` variable - // // let _x: &'static mut u32 = X; rprintln!("idle"); panic!("panic"); loop { continue; - // cortex_m::asm::wfi(); // does not work for some reason } } };