diff --git a/examples/bare1.rs b/examples/bare1.rs index 3c76d7c6f7a48c54a8ddcaf34d7c37c60b3628cf..5f077c8ff01ced929cc96c7f02d57cf38be85ffe 100644 --- a/examples/bare1.rs +++ b/examples/bare1.rs @@ -3,7 +3,7 @@ //! Inspecting the generated assembly //! //! What it covers -//! - ITM tracing +//! - Rust panic tracing using ITM //! - assembly calls and inline assembly //! - more on arithmetics @@ -45,7 +45,7 @@ fn main() -> ! { // // 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`. +// (If you change toolchain, you may need to exit and re-start `vscode`.) // // 1. Build and run the application // diff --git a/examples/bare2.rs b/examples/bare2.rs index d99f533cf37ebe3e139a9da9e8c3bafedc32e4d3..ce6acb20fb9221507961a8586a4ba8211917db1c 100644 --- a/examples/bare2.rs +++ b/examples/bare2.rs @@ -6,13 +6,14 @@ //! - Generating documentation //! - Using core peripherals //! - Measuring time using the DWT -//! - ITM tracing +//! - ITM tracing using `iprintln` +//! - Panic halt //! #![no_main] #![no_std] -extern crate panic_halt; +use panic_halt as _; use cortex_m::{iprintln, peripheral::DWT, Peripherals}; use cortex_m_rt::entry; @@ -37,7 +38,7 @@ fn main() -> ! { dwt.enable_cycle_counter(); // Reading the cycle counter can be done without `owning` access - // the DWT (since it has no side effetc). + // the DWT (since it has no side effect). // // Look in the docs: // pub fn enable_cycle_counter(&mut self) @@ -89,7 +90,7 @@ fn main() -> ! { // // ** your answer here ** // -// commit your answers (bare2_1) +// commit your answers (bare2_1) // // 3. *Optional // Inspect the generated binaries, and try stepping through the code diff --git a/examples/bare3.rs b/examples/bare3.rs index 0c0c0ae8f2915205b3808c4197c4c38f0750e795..665b4da687d3ca0285abdf1ae5268b9ba0a4c340 100644 --- a/examples/bare3.rs +++ b/examples/bare3.rs @@ -5,7 +5,7 @@ //! What it covers: //! - Types, str, arrays ([u8; usize]), slices (&[u8]) //! - Iteration, copy -//! - Semihosting (tracing) +//! - Semihosting (tracing using `hprintln` #![no_main] #![no_std] @@ -103,3 +103,46 @@ fn main() -> ! { // Implement and test your solution. // // Commit your answers (bare3_5) +// +// 6. Optional +// Rust is heavily influenced by functional languages. +// Figure out how you can use an iterator to work over both +// the `a` and `bs` to copy the content of `bs` to `a`. +// +// You may use +// - `iter` (to turn a slice into an iterator) +// - `zip` (to merge two slices into an iterator) +// - a for loop to assign the elements +// +// Commit your solution (bare3_6) +// +// 7. Optional +// Iter using `foreach` and a closure instead of the for loop. +// +// Commit your solution (bare3_7) +// +// 8. Optional* +// Now benchmark your different solutions using the cycle accurate +// DWT based approach (in release mode). +// +// Cycle count for `raw` indexing +// +// ** your answer here ** +// +// Cycle count for the primitive slice approach. +// +// ** your answer here ** +// +// Cycle count for the primitive slice approach. +// +// ** your answer here ** +// +// Cycle count for the zip + for loop approach. +// +// ** your answer here ** +// +// Cycle count for the zip + for_each approach. +// +// What conclusions can you draw, does Rust give you zero-cost abstractions? +// +// ** your answer here ** diff --git a/examples/bare4.rs b/examples/bare4.rs new file mode 100644 index 0000000000000000000000000000000000000000..07ce483f31f97174ac046b5a43a6548af79a212d --- /dev/null +++ b/examples/bare4.rs @@ -0,0 +1,134 @@ +//! bare4.rs +//! +//! Access to Peripherals +//! +//! What it covers: +//! - Raw pointers +//! - Volatile read/write +//! - Busses and clocking +//! - GPIO (a primitive abstraction) + +#![no_std] +#![no_main] + +extern crate panic_halt; + +extern crate cortex_m; +use cortex_m_rt::entry; + +// Peripheral addresses as constants +#[rustfmt::skip] +mod address { + pub const PERIPH_BASE: u32 = 0x40000000; + pub const AHB1PERIPH_BASE: u32 = PERIPH_BASE + 0x00020000; + pub const RCC_BASE: u32 = AHB1PERIPH_BASE + 0x3800; + pub const RCC_AHB1ENR: u32 = RCC_BASE + 0x30; + pub const GBPIA_BASE: u32 = AHB1PERIPH_BASE + 0x0000; + pub const GPIOA_MODER: u32 = GBPIA_BASE + 0x00; + pub const GPIOA_BSRR: u32 = GBPIA_BASE + 0x18; +} + +use address::*; + +// see the Reference Manual RM0368 (www.st.com/resource/en/reference_manual/dm00096844.pdf) +// rcc, chapter 6 +// gpio, chapter 8 + +#[inline(always)] +fn read_u32(addr: u32) -> u32 { + unsafe { core::ptr::read_volatile(addr as *const _) } + //core::ptr::read_volatile(addr as *const _) +} + +#[inline(always)] +fn write_u32(addr: u32, val: u32) { + unsafe { + core::ptr::write_volatile(addr as *mut _, val); + } +} + +fn wait(i: u32) { + for _ in 0..i { + cortex_m::asm::nop(); // no operation (cannot be optimized out) + } +} + +#[entry] +fn main() -> ! { + // power on GPIOA + let r = read_u32(RCC_AHB1ENR); // read + write_u32(RCC_AHB1ENR, r | 1); // set enable + + // configure PA5 as output + let r = read_u32(GPIOA_MODER) & !(0b11 << (5 * 2)); // read and mask + write_u32(GPIOA_MODER, r | 0b01 << (5 * 2)); // set output mode + + // and alter the data output through the BSRR register + // this is more efficient as the read register is not needed. + + loop { + // set PA5 high + write_u32(GPIOA_BSRR, 1 << 5); // set bit, output hight (turn on led) + wait(10_000); + + // set PA5 low + write_u32(GPIOA_BSRR, 1 << (5 + 16)); // clear bit, output low (turn off led) + wait(10_000); + } +} + +// 0. Build and run the application (debug build). +// +// > cargo run --example bare4 +// (or use the vscode) +// +// 1. Did you enjoy the blinking? +// +// ** your answer here ** +// +// Now lookup the data-sheets, and read each section referred, +// 6.3.11, 8.4.1, 8.4.7 +// +// Document each low level access *code* by the appropriate section in the +// data sheet. +// +// Commit your answers (bare4_1) +// +// 2. Comment out line 40 and uncomment line 41 (essentially omitting the `unsafe`) +// +// //unsafe { core::ptr::read_volatile(addr as *const _) } +// core::ptr::read_volatile(addr as *const _) +// +// What was the error message and explain why. +// +// ** your answer here ** +// +// Digging a bit deeper, why do you think `read_volatile` is declared `unsafe`. +// (https://doc.rust-lang.org/core/ptr/fn.read_volatile.html, for some food for thought ) +// +// ** your answer here ** +// +// Commit your answers (bare4_2) +// +// 3. Volatile read/writes are explicit *volatile operations* in Rust, while in C they +// are declared at type level (i.e., access to varibles declared volatile amounts to +// volatile reads/and writes). +// +// Both C and Rust (even more) allows code optimization to re-order operations, as long +// as data dependencies are preserved. +// +// Why is it important that ordering of volatile operations are ensured by the compiler? +// +// ** your answer here ** +// +// Give an example in the above code, where reordering might make things go horribly wrong +// (hint, accessing a peripheral not being powered...) +// +// ** your answer here ** +// +// Without the non-reordering property of `write_volatile/read_volatile` could that happen in theory +// (argue from the point of data dependencies). +// +// ** your answer here ** +// +// Commit your answers (bare4_3)