//! bare0.rs //! //! Simple bare metal application //! What it covers: //! - constants //! - global (static) variables //! - checked vs. wrapping arithmetics //! - safe and unsafe code //! - making a safe API //! - assertions //! - panic handling // build without the Rust standard library #![no_std] // no standard main, we declare main using [entry] #![no_main] // Panic handler, for textual output using semihosting use panic_semihosting as _; // import entry point use cortex_m_rt::entry; // a constant (cannot be changed at run-time) const X_INIT: u32 = core::u32::MAX; // global mutable variables (changed using unsafe code) static mut X: u32 = X_INIT; static mut Y: u32 = 0; fn write_x(x: u32, i: u32) { unsafe{ X = x.wrapping_add(i); } } fn write_y(y: u32) { unsafe{ Y = y; } } fn read_y() -> u32 { unsafe{ Y } } fn read_x() -> u32 { unsafe{ X } } #[entry] fn main() -> ! { // local mutable variable (changed in safe code) let mut x = read_x(); loop { x = x.wrapping_add(1); // <- place breakpoint here (3) write_x(read_x(), 1); write_y(read_x()); assert!(x == read_x() && read_x() == read_y()); } } // Here we assume you are using `vscode` with `cortex-debug`. // // 0. Compile/build and run the example in debug (dev) mode. // // > cargo run --example bare0 // (or use vscode) // // 1. Run the program in the debugger, let the program run for a while and // then press pause. // // Look under Variables/Local what do you find. // // x: 9389056 // // In the Expressions (WATCH -vscode) view add X and Y // what do you find // // ** your answer here ** // X: 9389055 // Y: <optimized out> // // Step through one complete iteration of the loop // and see how the (Local) Variables are updated // can you foresee what will eventually happen? // // ** place your answer here ** // // x will update to 9389057 // eventually x will overflow and if rust runs in debug mode then rust will panic and // if it runs in release mode rust wont check for the overflow and just do a wrap the value // // Commit your answers (bare0_1) // // 2. Alter the constant X_INIT so that `x += 1` directly causes `x` to wrap. // What happens when `x` wraps // (Hint, look under OUTPUT/Adopter Output to see the `openocd` output.) // // ** your answer here ** // The program panics // panicked at 'attempt to add with overflow', examples/bare0.rs:42:9 // // Commit your answers (bare0_2) // // 3. Place a breakpoint at `x += 1` // // Change (both) += operations to use wrapping_add // load and run the program, what happens // ** your answer here ** // x wrapps around and become 0 // X does nothing // Now continue execution, what happens // ** your answer here ** // x continues to add up // X still does nothing // Commit your answers (bare0_3) // // (If the program did not succeed back to the breakpoint // you have some fault in the program and go back to 3.) // // 4. Change the assertion to `assert!(x == X && X == Y + 1)`, what happens? // // ** place your answer here ** // panic because the assertion fails // panicked at 'assertion failed: x == X && X == Y + 1', examples/bare0.rs:46:13 // // Commit your answers (bare0_4) // // 5. Remove the assertion and implement "safe" functions for // reading and writing X and Y // e.g. read_x, read_y, write_x, write_y // // Rewrite the program to use ONLY "safe" code besides the // read/write functions (which are internally "unsafe") // // Commit your solution (bare0_5) // // 6. *Optional // Implement a read_u32/write_u32, taking a reference to a // "static" variable // // Rewrite the program to use this abstraction instead of "read_x", etc. // // Commit your solution (bare0_6) //