Select Git revision
bare1.rs 4.73 KiB
//! bare1.rs
//!
//! Tracing and assembly code
//!
//! What it covers
//! - tracing over semihosting and ITM
//! - assembly calls and inline assembly
//! - more on arithmetics
//! ---
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m::{iprintln, Peripherals};
use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;
#[entry]
fn main() -> ! {
let mut p = Peripherals::take().unwrap();
let stim = &mut p.ITM.stim[0];
let mut x = 27;
iprintln!(stim, "x = {:?}", x);
hprintln!("x = {:?}", x).unwrap();
loop {
x += 1;
cortex_m::asm::nop();
cortex_m::asm::bkpt();
x -= 1;
}
}
// 0. Setup
// For this example we will use the `nightly` compiler
// to get true inline assembly.
//
// > rustup override set nightly
//
// 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`.
//
// 1. Build and run the application
// Look at the `hello.rs` and `itm.rs` examples to setup the tracing.
//
// 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
//
// ** 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
//
// ** your answer here **
//
// commit your answers (bare1_2)
//
// 3. Now remove the comment for `cortex_m::asm::nop()`.
// Rebuild and debug, pause the program.
// What is the output of:
// (gdb) disassemble
//
// ** your answer here **
//
// commit your answers (bare1_3)
//
// 4. Now remove the comment for `cortex_m::asm::bkpt()`
// Rebuild and debug, let the program run until it halts.
// What is the output of:
// (gdb) disassemble
//
// ** your answer here **
//
// commit your answers (bare1_4)
//
// 5. Release mode (optimized builds).
// Rebuild `bare1.rs` in release (optimized mode).
// Compare the generated assembly for the loop
// between the dev (unoptimized) and release (optimized) build.
//
// ** your answer here **
//
// commit your answers (bare1_5)
//
// Tips: The optimized build should have some 8 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).
//
// 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:
// 1) efficiency, unchecked is faster
// 2) convenience, it would be inconvenient to explicitly use
// wrapping arithmetics, and wrapping is what the programmer
// typically would expect in any case. So the check
// in dev/debug mode is just there for some extra safety
// if your intention is NON-wrapping arithmetics.
//
// 6. *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.)
//
// What is now the disassembly of the loop:
//
// ** your answer here **
//
// commit your answers (bare1_6)
//
// Now restore the `.cargo/config` to its original state.
//
// 7. *Optional
// There is another way to conveniently use wrapping arithmetics
// without passing flags to the compiler.
//
// https://doc.rust-lang.org/std/num/struct.Wrapping.html
//
// Rewrite the code using this approach.
//
// What is now the disassembly of the code in dev mode?
//
// ** your answer here **
//
// What is now the disassembly of the code in release mode?
//
// ** your answer here **
//
// commit your answers (bare1_7)
//
// Final discussion:
//
// Embedded code typically is performance sensitve, 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.
//
// 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.)