diff --git a/Cargo.toml b/Cargo.toml index f4a941ca4357d201723e5d9b90462a04f4e7c3be..d06d62135f0dfab3cca45d6078cc99f7cac2810e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,15 @@ edition = "2018" panic-halt = "0.2" panic-semihosting = "0.5" cortex-m-semihosting = "0.3.5" -cortex-m = "0.6.2" aligned = "0.3.2" ufmt = "0.1.0" panic-itm = "0.4.1" nb = "0.1.2" +[dependencies.cortex-m] +verison = "0.6.2" +features = ["inline-asm"] + [dependencies.cortex-m-rt] version = "0.6.12" diff --git a/examples/bare1.rs b/examples/bare1.rs index 94b99d08183c8f821584434c8a3cd02f15efcb80..acd55f503ad2c9baaaf2fe255cd237a4d67b363e 100644 --- a/examples/bare1.rs +++ b/examples/bare1.rs @@ -3,118 +3,118 @@ //! Inspecting the generated assembly //! //! What it covers -//! - tracing over semihosting and ITM +//! - ITM tracing //! - assembly calls and inline assembly //! - more on arithmetics #![no_main] #![no_std] -extern crate panic_halt; +extern crate panic_itm; use cortex_m_rt::entry; #[entry] #[inline(never)] fn main() -> ! { - // Prepend by `x` by _ to avoid warning (never used). - // The compiler is smart enough to figure out that - // `x` is not used in any menaningful way. - - let mut _x = 0; + let mut x = core::u32::MAX - 1; loop { - _x += 1; - cortex_m::asm::nop(); cortex_m::asm::bkpt(); - _x -= 1; + x += 1; + cortex_m::asm::bkpt(); + + // prevent optimization by read-volatile (unsafe) + unsafe { + core::ptr::read_volatile(&x); + } } } // 0. Setup // For this example we will use the `nightly` compiler // to get inline assembly. -// (Inline assembly is currently not stabelized.) +// (Inline assembly is currently not stabilized.) // // > rustup override set nightly // -// In the `Corgo.toml` file, uncomment +// In the `Cargo.toml` file, uncomment // # features = ["inline-asm"] # <- currently requires nightly compiler // -// You may need/want to install addititonal components also, -// to that end look at the install section in the README.md. -// If you change toolchain, exit and re-start `vscode`. +// The first time you use the new toolchain you may need to install the target. +// > rustup target add thumbv7em-none-eabihf +// +// You may need/want to install additional components also. +// To that end look at the install section in the README.md. +// If you change toolchain, you may need to exit and re-start `vscode`. // // 1. Build and run the application // // > cargo build --example bare1 // (or use the vscode build task) // -// Look at the `hello.rs` and `itm.rs` examples to setup the tracing. +// Make sure you have followed the instructions for fifo `ITM` tracing. +// Debug using the `itm fifo (debug)` launch configuration. // -// When debugging the application it should get stuck in the -// loop, (press pause/suspend to verify this). -// what is the output in the ITM console +// When debugging the application it should hit the `bkpt` instruction. +// What happens when you continue (second iteration of the loop)? // // ** your answer here ** // -// What is the output in the semihosting (openocd) console -// ** your answer here ** -// -// Commit your answers (bare1_1) -// -// 2. Inspecting the generated assembly code -// If in `vcsode` the gdb console in DEBUG CONSOLE -// -// What is the output of: -// (gdb) disassemble +// What is the `ITM` output. // // ** your answer here ** // -// Commit your answers (bare1_2) +// Commit your answer (bare1_1) // -// 3. Now remove the comment for `cortex_m::asm::nop()`. -// Rebuild and debug, pause the program. +// 2. Inspecting the generated assembly code. +// Close and re-start the debug session. Run till you hit the `bkpt` instruction. +// +// Under DEBUG CONSOLE you find the `gdb` interface. // // What is the output of: -// (gdb) disassemble +// > disassemble // // ** your answer here ** // -// Commit your answers (bare1_3) +// How many instructions are in between the two `bkpt` instructions in the loop. +// Notice, the generated code may not be exactly what you expect :) // -// 4. Now remove the comment for `cortex_m::asm::bkpt()` -// Rebuild and debug, let the program run until it halts. +// ** your answer here ** // -// What is the output of: -// (gdb) disassemble +// Which instruction stores the local variable on the stack. // // ** your answer here ** // -// Commit your answers (bare1_4) +// Commit your answers (bare1_2) // -// 5. Release mode (optimized builds). +// 3. Release mode (optimized builds). // Rebuild `bare1.rs` in release (optimized mode). -// +// // > cargo build --example bare1 --release // (or using the vscode build task) // // Compare the generated assembly for the loop -// between the dev (unoptimized) and release (optimized) build. +// between the dev (un-optimized) and release (optimized) build. +// +// What is the output of: +// > disassemble // // ** your answer here ** // -// commit your answers (bare1_5) +// How many instructions are in between the two `bkpt` instructions. +// +// ** your answer here ** // -// Tips: The optimized build should have 3 instructions -// while the debug (dev) build should have > 20 instructions -// (both counting the inner loop only). The debug build -// should have additional code that call panic if the additon -// wraps (and in such case call panic). +// Where is the local variable stored? +// +// ** your answer here ** +// +// commit your answers (bare1_3) // // Discussion: // In release (optimized) mode the addition is unchecked, // so there is a semantic difference here in between -// the dev and release modes. This is motivited by: +// the dev and release modes. This is motivated by: // 1) efficiency, unchecked is faster // 2) convenience, it would be inconvenient to explicitly use // wrapping arithmetics, and wrapping is what the programmer @@ -122,24 +122,33 @@ fn main() -> ! { // in dev/debug mode is just there for some extra safety // if your intention is NON-wrapping arithmetics. // -// 6. *Optional +// The debug build should have additional code that checks if the addition +// wraps (and in such case call panic). In the case of the optimized +// build there should be no reference to the panic handler in the generated +// binary. Recovering from a panic is in general very hard. Typically +// the best we can do is to stop and report the error (and maybe restart). +// +// Later we will demonstrate how we can get guarantees of panic free execution. +// This is very important to improve reliability. +// +// 4. *Optional // You can pass additional flags to the Rust `rustc` compiler. // // `-Z force-overflow-checks=off` // -// Under this flag, code is never generated for oveflow checking. -// You can enable this flag (uncomment the corresponding flag in -// the `.cargo/config` file.) +// Under this flag, code is never generated for overflow checking even in +// non optimized (debug/dev) builds. +// You can enable this flag in the `.cargo/config` file. // -// What is now the disassembly of the loop (in debug mode): +// What is now the disassembly of the loop (in debug/dev mode): // // ** your answer here ** // -// commit your answers (bare1_6) +// commit your answers (bare1_4) // // Now restore the `.cargo/config` to its original state. // -// 7. *Optional +// 5. *Optional // There is another way to conveniently use wrapping arithmetics // without passing flags to the compiler. // @@ -155,22 +164,22 @@ fn main() -> ! { // // ** your answer here ** // -// commit your answers (bare1_7) +// commit your answers (bare1_5) // // Final discussion: // -// Embedded code typically is performance sensitve, hence +// Embedded code typically is performance sensitive, hence // it is important to understand how code is generated // to achieve efficient implementations. // // Moreover, arithmetics are key to processing of data, // so its important that we are in control over the -// computations. E.g. comupting checksums, hashes, cryptos etc. -// all require precise control over wrapping vs. overflow behaviour. +// computations. E.g. computing checksums, hashes, cryptos etc. +// all require precise control over wrapping vs. overflow behavior. // // If you write a library depending on wrapping arithmetics // do NOT rely on a compiler flag. (The end user might compile -// it without this flag enabled, and thus get erronous results.) +// it without this flag enabled, and thus get erroneous results.) // // NOTICE: // ------ @@ -179,11 +188,11 @@ fn main() -> ! { // restore the `Cargo.toml` (comment out the `features = ["inline-asm"]`) // // Pros and cons of nightly: -// + Acccess to new Rust features (such as inline assembly) +// + Access to new Rust features (such as inline assembly) // - No guarantee these features will work, they might change semantics, // or even be revoked. // -// The compiler itself is the same, the stable release is just a snapchot +// The compiler itself is the same, the stable release is just a snapshot // of the nightly (released each 6 week). It is the latest nightly // that passed some additional regression test, not a different compiler. // And of course, the stable has the experimental features disabled.