//! 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; use cortex_m::{iprintln, Peripherals}; use cortex_m_semihosting::hprintln; use core::num::Wrapping; #[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 p = Peripherals::take().unwrap(); //let stim = &mut p.ITM.stim[0]; let mut _x = Wrapping(0u32); let one = Wrapping(1u32); loop { _x += one; cortex_m::asm::nop(); //iprintln!(stim, "{}",_x); //hprintln!("{}", _x).unwrap(); cortex_m::asm::bkpt(); _x -= one; } } // 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 // // ** 1 ** // // What is the output in the semihosting (openocd) console // ** 1 ** // // 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 main: // 0x08000400 <+0>: sub sp, #16 // 0x08000402 <+2>: movs r0, #0 // 0x08000404 <+4>: str r0, [sp, #12] // 0x08000406 <+6>: b.n 0x8000408 <main+8> // => 0x08000408 <+8>: ldr r0, [sp, #12] // 0x0800040a <+10>: adds r1, r0, #1 // 0x0800040c <+12>: mov r2, r1 // 0x0800040e <+14>: cmp r1, r0 // 0x08000410 <+16>: str r2, [sp, #8] // 0x08000412 <+18>: bvs.n 0x800042c <main+44> // 0x08000414 <+20>: b.n 0x8000416 <main+22> // 0x08000416 <+22>: ldr r0, [sp, #8] // 0x08000418 <+24>: str r0, [sp, #12] // 0x0800041a <+26>: ldr r1, [sp, #12] // 0x0800041c <+28>: subs r2, r1, #1 // 0x0800041e <+30>: cmp r1, #1 // 0x08000420 <+32>: str r2, [sp, #4] // 0x08000422 <+34>: bvs.n 0x800043a <main+58> // 0x08000424 <+36>: b.n 0x8000426 <main+38> // 0x08000426 <+38>: ldr r0, [sp, #4] // 0x08000428 <+40>: str r0, [sp, #12] // 0x0800042a <+42>: b.n 0x8000408 <main+8> // 0x0800042c <+44>: movw r0, #1964 ; 0x7ac // 0x08000430 <+48>: movt r0, #2048 ; 0x800 // 0x08000434 <+52>: bl 0x800045c <core::panicking::panic::h4c45e71f0f614f08> // 0x08000438 <+56>: udf #254 ; 0xfe // 0x0800043a <+58>: movw r0, #2036 ; 0x7f4 // 0x0800043e <+62>: movt r0, #2048 ; 0x800 // 0x08000442 <+66>: bl 0x800045c <core::panicking::panic::h4c45e71f0f614f08> // 0x08000446 <+70>: 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 main: // 0x08000404 <+0>: sub sp, #16 // 0x08000406 <+2>: movs r0, #0 // 0x08000408 <+4>: str r0, [sp, #12] // 0x0800040a <+6>: b.n 0x800040c <main+8> // 0x0800040c <+8>: ldr r0, [sp, #12] // => 0x0800040e <+10>: adds r1, r0, #1 // 0x08000410 <+12>: mov r2, r1 // 0x08000412 <+14>: cmp r1, r0 // 0x08000414 <+16>: str r2, [sp, #8] // 0x08000416 <+18>: bvs.n 0x8000436 <main+50> // 0x08000418 <+20>: b.n 0x800041a <main+22> // 0x0800041a <+22>: ldr r0, [sp, #8] // 0x0800041c <+24>: str r0, [sp, #12] // 0x0800041e <+26>: bl 0x8000400 <cortex_m::asm::nop::h0ac7b7a3f33f2667> // 0x08000422 <+30>: b.n 0x8000424 <main+32> // 0x08000424 <+32>: ldr r0, [sp, #12] // 0x08000426 <+34>: subs r1, r0, #1 // 0x08000428 <+36>: cmp r0, #1 // 0x0800042a <+38>: str r1, [sp, #4] // 0x0800042c <+40>: bvs.n 0x8000444 <main+64> // 0x0800042e <+42>: b.n 0x8000430 <main+44> // 0x08000430 <+44>: ldr r0, [sp, #4] // 0x08000432 <+46>: str r0, [sp, #12] // 0x08000434 <+48>: b.n 0x800040c <main+8> // 0x08000436 <+50>: movw r0, #1980 ; 0x7bc // 0x0800043a <+54>: movt r0, #2048 ; 0x800 // 0x0800043e <+58>: bl 0x8000466 <core::panicking::panic::h4c45e71f0f614f08> // 0x08000442 <+62>: udf #254 ; 0xfe // 0x08000444 <+64>: movw r0, #2052 ; 0x804 // 0x08000448 <+68>: movt r0, #2048 ; 0x800 // 0x0800044c <+72>: bl 0x8000466 <core::panicking::panic::h4c45e71f0f614f08> // 0x08000450 <+76>: 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 main: // 0x08000404 <+0>: sub sp, #16 // 0x08000406 <+2>: movs r0, #0 // 0x08000408 <+4>: str r0, [sp, #12] // 0x0800040a <+6>: b.n 0x800040c <main+8> // 0x0800040c <+8>: ldr r0, [sp, #12] // 0x0800040e <+10>: adds r1, r0, #1 // 0x08000410 <+12>: mov r2, r1 // 0x08000412 <+14>: cmp r1, r0 // 0x08000414 <+16>: str r2, [sp, #8] // 0x08000416 <+18>: bvs.n 0x800043a <main+54> // 0x08000418 <+20>: b.n 0x800041a <main+22> // 0x0800041a <+22>: ldr r0, [sp, #8] // 0x0800041c <+24>: str r0, [sp, #12] // 0x0800041e <+26>: bl 0x8000400 <cortex_m::asm::nop::h0ac7b7a3f33f2667> // 0x08000422 <+30>: b.n 0x8000424 <main+32> // => 0x08000424 <+32>: bkpt 0x0000 // 0x08000426 <+34>: b.n 0x8000428 <main+36> // 0x08000428 <+36>: ldr r0, [sp, #12] // 0x0800042a <+38>: subs r1, r0, #1 // 0x0800042c <+40>: cmp r0, #1 // 0x0800042e <+42>: str r1, [sp, #4] // 0x08000430 <+44>: bvs.n 0x8000448 <main+68> // 0x08000432 <+46>: b.n 0x8000434 <main+48> // 0x08000434 <+48>: ldr r0, [sp, #4] // 0x08000436 <+50>: str r0, [sp, #12] // 0x08000438 <+52>: b.n 0x800040c <main+8> // 0x0800043a <+54>: movw r0, #1980 ; 0x7bc // 0x0800043e <+58>: movt r0, #2048 ; 0x800 // 0x08000442 <+62>: bl 0x800046a <core::panicking::panic::h4c45e71f0f614f08> // 0x08000446 <+66>: udf #254 ; 0xfe // 0x08000448 <+68>: movw r0, #2052 ; 0x804 // 0x0800044c <+72>: movt r0, #2048 ; 0x800 // 0x08000450 <+76>: bl 0x800046a <core::panicking::panic::h4c45e71f0f614f08> // 0x08000454 <+80>: 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. // // ** // Dump of assembler code for function main: // 0x08000400 <+0>: nop // => 0x08000402 <+2>: bkpt 0x0000 // 0x08000404 <+4>: b.n 0x8000400 <main> // End of assembler dump. // ** // // 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): // // ** // Dump of assembler code for function main: // 0x08000404 <+0>: sub sp, #8 // 0x08000406 <+2>: movs r0, #0 // 0x08000408 <+4>: str r0, [sp, #4] // 0x0800040a <+6>: b.n 0x800040c <main+8> // 0x0800040c <+8>: ldr r0, [sp, #4] // 0x0800040e <+10>: adds r0, #1 // 0x08000410 <+12>: str r0, [sp, #4] // 0x08000412 <+14>: bl 0x8000400 <cortex_m::asm::nop::h5c0367e982e73891> // 0x08000416 <+18>: b.n 0x8000418 <main+20> // => 0x08000418 <+20>: bkpt 0x0000 // 0x0800041a <+22>: b.n 0x800041c <main+24> // 0x0800041c <+24>: ldr r0, [sp, #4] // 0x0800041e <+26>: subs r0, #1 // 0x08000420 <+28>: str r0, [sp, #4] // 0x08000422 <+30>: b.n 0x800040c <main+8> // End of assembler dump. // ** // // 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? // // ** // Dump of assembler code for function main: // 0x080004e4 <+0>: sub sp, #8 // 0x080004e6 <+2>: movs r0, #0 // 0x080004e8 <+4>: str r0, [sp, #0] // 0x080004ea <+6>: movs r0, #1 // 0x080004ec <+8>: str r0, [sp, #4] // 0x080004ee <+10>: b.n 0x80004f0 <main+12> // 0x080004f0 <+12>: ldr r1, [sp, #4] // 0x080004f2 <+14>: mov r0, sp // 0x080004f4 <+16>: bl 0x8000490 <core::num::wrapping::_$LT$impl$u20$core..ops..arith..AddAssign$u20$for$u20$core..num..Wrapping$LT$u32$GT$$GT$::add_assign::hbb56dbef6bcfa964> // 0x080004f8 <+20>: b.n 0x80004fa <main+22> // 0x080004fa <+22>: bl 0x80004e0 <cortex_m::asm::nop::h0ac7b7a3f33f2667> // 0x080004fe <+26>: b.n 0x8000500 <main+28> // => 0x08000500 <+28>: bkpt 0x0000 // 0x08000502 <+30>: b.n 0x8000504 <main+32> // 0x08000504 <+32>: ldr r1, [sp, #4] // 0x08000506 <+34>: mov r0, sp // 0x08000508 <+36>: bl 0x80004b8 <core::num::wrapping::_$LT$impl$u20$core..ops..arith..SubAssign$u20$for$u20$core..num..Wrapping$LT$u32$GT$$GT$::sub_assign::hcf06b8f89372a809> // 0x0800050c <+40>: b.n 0x800050e <main+42> // 0x0800050e <+42>: b.n 0x80004f0 <main+12> // End of assembler dump. // ** // // What is now the disassembly of the code in release mode? // // ** // Dump of assembler code for function main: // 0x08000400 <+0>: nop // => 0x08000402 <+2>: bkpt 0x0000 // 0x08000404 <+4>: b.n 0x8000400 <main> // End of assembler dump. // ** // // 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.