Select Git revision
Forked from
Per Lindgren / e7020e_2021
Source project has a limited visibility.
bare1.rs 9.65 KiB
//! bare1.rs
//!
//! Inspecting the generated assembly
//!
//! 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_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;
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 inline assembly.
// (Inline assembly is currently not stabelized.)
//
// > rustup override set nightly
//
// In the `Corgo.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`.
//
// 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.
//
// 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
//
// There is no output in the ITM console
//
// What is the output in the semihosting (openocd) console
//
// There is no special output here either
//
// 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
//
//Dump of assembler code for function Reset:
// => 0x08000616 <+0>: bl 0x8000686 <DefaultPreInit>
// 0x0800061a <+4>: b.n 0x800061c <Reset+6>
// 0x0800061c <+6>: movw r0, #0
// 0x08000620 <+10>: movt r0, #8192 ; 0x2000
// 0x08000624 <+14>: movw r1, #0
// 0x08000628 <+18>: movt r1, #8192 ; 0x2000
// 0x0800062c <+22>: bl 0x8000688 <r0::zero_bss>
// 0x08000630 <+26>: b.n 0x8000632 <Reset+28>
// 0x08000632 <+28>: movw r0, #0
// 0x08000636 <+32>: movt r0, #8192 ; 0x2000
// 0x0800063a <+36>: movw r1, #0
// 0x0800063e <+40>: movt r1, #8192 ; 0x2000
// 0x08000642 <+44>: movw r2, #2580 ; 0xa14
// 0x08000646 <+48>: movt r2, #2048 ; 0x800
// 0x0800064a <+52>: bl 0x80006d0 <r0::init_data>
// 0x0800064e <+56>: b.n 0x8000650 <Reset+58>
// 0x08000650 <+58>: movw r0, #60808 ; 0xed88
// 0x08000654 <+62>: movt r0, #57344 ; 0xe000
// 0x08000658 <+66>: ldr r1, [r0, #0]
// 0x0800065a <+68>: orr.w r1, r1, #15728640 ; 0xf00000
// 0x0800065e <+72>: bl 0x800074a <core::ptr::write_volatile>
// 0x08000662 <+76>: b.n 0x8000664 <Reset+78>
// 0x08000664 <+78>: bl 0x800066a <ResetTrampoline>
// 0x08000668 <+82>: udf #254 ; 0xfe
// End of assembler dump.
//
// 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
//
//Dump of assembler code for function Reset:
// => 0x0800061e <+0>: bl 0x800068e <DefaultPreInit>
// 0x08000622 <+4>: b.n 0x8000624 <Reset+6>
// 0x08000624 <+6>: movw r0, #0
// 0x08000628 <+10>: movt r0, #8192 ; 0x2000
// 0x0800062c <+14>: movw r1, #0
// 0x08000630 <+18>: movt r1, #8192 ; 0x2000
// 0x08000634 <+22>: bl 0x8000690 <r0::zero_bss>
// 0x08000638 <+26>: b.n 0x800063a <Reset+28>
// 0x0800063a <+28>: movw r0, #0
// 0x0800063e <+32>: movt r0, #8192 ; 0x2000
// 0x08000642 <+36>: movw r1, #0
// 0x08000646 <+40>: movt r1, #8192 ; 0x2000
// 0x0800064a <+44>: movw r2, #2580 ; 0xa14
// 0x0800064e <+48>: movt r2, #2048 ; 0x800
// 0x08000652 <+52>: bl 0x80006d8 <r0::init_data>
// 0x08000656 <+56>: b.n 0x8000658 <Reset+58>
// 0x08000658 <+58>: movw r0, #60808 ; 0xed88
// 0x0800065c <+62>: movt r0, #57344 ; 0xe000
// 0x08000660 <+66>: ldr r1, [r0, #0]
// 0x08000662 <+68>: orr.w r1, r1, #15728640 ; 0xf00000
// 0x08000666 <+72>: bl 0x8000752 <core::ptr::write_volatile>
// 0x0800066a <+76>: b.n 0x800066c <Reset+78>
// 0x0800066c <+78>: bl 0x8000672 <ResetTrampoline>
// 0x08000670 <+82>: udf #254 ; 0xfe
// End of assembler dump.
//
// 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
//
// Dump of assembler code for function Reset:
// => 0x08000622 <+0>: bl 0x8000692 <DefaultPreInit>
// 0x08000626 <+4>: b.n 0x8000628 <Reset+6>
// 0x08000628 <+6>: movw r0, #0
// 0x0800062c <+10>: movt r0, #8192 ; 0x2000
// 0x08000630 <+14>: movw r1, #0
// 0x08000634 <+18>: movt r1, #8192 ; 0x2000
// 0x08000638 <+22>: bl 0x8000694 <r0::zero_bss>
// 0x0800063c <+26>: b.n 0x800063e <Reset+28>
// 0x0800063e <+28>: movw r0, #0
// 0x08000642 <+32>: movt r0, #8192 ; 0x2000
// 0x08000646 <+36>: movw r1, #0
// 0x0800064a <+40>: movt r1, #8192 ; 0x2000
// 0x0800064e <+44>: movw r2, #2580 ; 0xa14
// 0x08000652 <+48>: movt r2, #2048 ; 0x800
// 0x08000656 <+52>: bl 0x80006dc <r0::init_data>
// 0x0800065a <+56>: b.n 0x800065c <Reset+58>
// 0x0800065c <+58>: movw r0, #60808 ; 0xed88
// 0x08000660 <+62>: movt r0, #57344 ; 0xe000
// 0x08000664 <+66>: ldr r1, [r0, #0]
// 0x08000666 <+68>: orr.w r1, r1, #15728640 ; 0xf00000
// 0x0800066a <+72>: bl 0x8000756 <core::ptr::write_volatile>
// 0x0800066e <+76>: b.n 0x8000670 <Reset+78>
// 0x08000670 <+78>: bl 0x8000676 <ResetTrampoline>
// 0x08000674 <+82>: udf #254 ; 0xfe
// End of assembler dump.
//
// Commit your answers (bare1_4)
//
// 5. 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.
//
// The optimized assembly code is much longer (so long that I could not even copy it)
// It is longer since it executes instructions that the non-optimized does not make
//
// commit your answers (bare1_5)
//
// 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).
//
// 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 (in debug mode):
//
// ** 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.)
//
// NOTICE:
// ------
// You are now on a `nightly` release of the compiler for good and bad.
// You can chose to switch back to the stable channel. If so you must
// restore the `Cargo.toml` (comment out the `features = ["inline-asm"]`)
//
// Pros and cons of nightly:
// + Acccess 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
// 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.
//
// So its up to you to decide if you want to use the stable or nightly.