From 0deaa01f42d25e1ce2fedf35fc7902a1f68a2d3a Mon Sep 17 00:00:00 2001 From: Per Lindgren <per.lindgren@ltu.se> Date: Sun, 6 Jan 2019 21:56:43 +0100 Subject: [PATCH] bare4 and bare5 --- .vscode/launch.json | 52 +++++++++- .vscode/tasks.json | 36 +++++++ examples/bare4.rs | 131 ++++++++++++++++++++++++ examples/bare5.rs | 235 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 452 insertions(+), 2 deletions(-) create mode 100644 examples/bare4.rs create mode 100644 examples/bare5.rs diff --git a/.vscode/launch.json b/.vscode/launch.json index 1a6ca56..e793d8c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -288,7 +288,7 @@ "type": "cortex-debug", "request": "launch", "servertype": "openocd", - "name": "bare3 (dubug)", + "name": "bare3 (debug)", "preLaunchTask": "cargo build --example bare3", "executable": "./target/thumbv7em-none-eabihf/debug/examples/bare3", "postLaunchCommands": [ @@ -300,6 +300,54 @@ ], "cwd": "${workspaceRoot}" }, + { + "type": "cortex-debug", + "request": "launch", + "servertype": "openocd", + "name": "bare4 (debug)", + "preLaunchTask": "cargo build --example bare4", + "executable": "./target/thumbv7em-none-eabihf/debug/examples/bare4", + "postLaunchCommands": [ + "monitor arm semihosting enable" + ], + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "cwd": "${workspaceRoot}" + }, + { + "type": "cortex-debug", + "request": "launch", + "servertype": "openocd", + "name": "bare5 (debug)", + "preLaunchTask": "cargo build --example bare5", + "executable": "./target/thumbv7em-none-eabihf/debug/examples/bare5", + "postLaunchCommands": [ + "monitor arm semihosting enable" + ], + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "cwd": "${workspaceRoot}" + }, + { + "type": "cortex-debug", + "request": "launch", + "servertype": "openocd", + "name": "bare5 (release)", + "preLaunchTask": "cargo build --example bare5 --release", + "executable": "./target/thumbv7em-none-eabihf/release/examples/bare5", + "postLaunchCommands": [ + "monitor arm semihosting enable" + ], + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "cwd": "${workspaceRoot}" + }, { "type": "cortex-debug", "request": "launch", @@ -377,6 +425,6 @@ ] }, "cwd": "${workspaceRoot}" - } + }, ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8e04b2f..acffa43 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -147,5 +147,41 @@ "isDefault": true } }, + { + "type": "shell", + "label": "cargo build --example bare4", + "command": "cargo build --example bare4", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "shell", + "label": "cargo build --example bare5", + "command": "cargo build --example bare5", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "shell", + "label": "cargo build --example bare5 --release", + "command": "cargo build --example bare5 --release", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, ] } \ No newline at end of file diff --git a/examples/bare4.rs b/examples/bare4.rs new file mode 100644 index 0000000..724f355 --- /dev/null +++ b/examples/bare4.rs @@ -0,0 +1,131 @@ +//! bare4.rs +//! +//! Access to Peripherals +//! +//! What it covers: +//! - raw pointers +//! - volatile read/write +//! - busses and clocking +//! - gpio + +#![feature(uniform_paths)] // requires nightly +#![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); + } +} + +// 1. Build and run the application (debug build). +// 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-reording proprety 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) diff --git a/examples/bare5.rs b/examples/bare5.rs new file mode 100644 index 0000000..d1a67f0 --- /dev/null +++ b/examples/bare5.rs @@ -0,0 +1,235 @@ +//! bare5.rs +//! +//! C Like Peripheral API +//! +//! What it covers: +//! - abstractions in Rust +//! + +#![feature(uniform_paths)] // requires nightly +#![no_std] +#![no_main] + +extern crate panic_halt; + +extern crate cortex_m; +use cortex_m_rt::entry; + +// C like API... +mod stm32f40x { + #[allow(dead_code)] + use core::{cell, ptr}; + + #[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 GPIOA_BASE: u32 = AHB1PERIPH_BASE + 0x0000; + } + use address::*; + + pub struct VolatileCell<T> { + value: cell::UnsafeCell<T>, + } + + impl<T> VolatileCell<T> { + #[inline(always)] + pub fn read(&self) -> T + where + T: Copy, + { + unsafe { ptr::read_volatile(self.value.get()) } + } + + #[inline(always)] + pub fn write(&self, value: T) + where + T: Copy, + { + unsafe { ptr::write_volatile(self.value.get(), value) } + } + } + + // impl VolatileCell<u32> { + // #[inline(always)] + // pub fn modify(&self, offset: u8, width: u8, value: u32) { + // // your code here + // } + // } + + #[repr(C)] + #[allow(non_snake_case)] + #[rustfmt::skip] + pub struct RCC { + pub CR: VolatileCell<u32>, // < RCC clock control register, Address offset: 0x00 + pub PLLCFGR: VolatileCell<u32>, // < RCC PLL configuration register, Address offset: 0x04 + pub CFGR: VolatileCell<u32>, // < RCC clock configuration register, Address offset: 0x08 + pub CIR: VolatileCell<u32>, // < RCC clock interrupt register, Address offset: 0x0C + pub AHB1RSTR: VolatileCell<u32>, // < RCC AHB1 peripheral reset register, Address offset: 0x10 + pub AHB2RSTR: VolatileCell<u32>, // < RCC AHB2 peripheral reset register, Address offset: 0x14 + pub AHB3RSTR: VolatileCell<u32>, // < RCC AHB3 peripheral reset register, Address offset: 0x18 + pub RESERVED0: VolatileCell<u32>, // < Reserved, 0x1C + pub APB1RSTR: VolatileCell<u32>, // < RCC APB1 peripheral reset register, Address offset: 0x20 + pub APB2RSTR: VolatileCell<u32>, // < RCC APB2 peripheral reset register, Address offset: 0x24 + pub RESERVED1: [VolatileCell<u32>; 2], // < Reserved, 0x28-0x2C + pub AHB1ENR: VolatileCell<u32>, // < RCC AHB1 peripheral clock register, Address offset: 0x30 + pub AHB2ENR: VolatileCell<u32>, // < RCC AHB2 peripheral clock register, Address offset: 0x34 + pub AHB3ENR: VolatileCell<u32>, // < RCC AHB3 peripheral clock register, Address offset: 0x38 + pub RESERVED2: VolatileCell<u32>, // < Reserved, 0x3C + pub APB1ENR: VolatileCell<u32>, // < RCC APB1 peripheral clock enable register, Address offset: 0x40 + pub APB2ENR: VolatileCell<u32>, // < RCC APB2 peripheral clock enable register, Address offset: 0x44 + pub RESERVED3: [VolatileCell<u32>; 2], // < Reserved, 0x48-0x4C + pub AHB1LPENR: VolatileCell<u32>, // < RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 + pub AHB2LPENR: VolatileCell<u32>, // < RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 + pub AHB3LPENR: VolatileCell<u32>, // < RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 + pub RESERVED4: VolatileCell<u32>, // < Reserved, 0x5C + pub APB1LPENR: VolatileCell<u32>, // < RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 + pub APB2LPENR: VolatileCell<u32>, // < RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 + pub RESERVED5: [VolatileCell<u32>; 2], // < Reserved, 0x68-0x6C + pub BDCR: VolatileCell<u32>, // < RCC Backup domain control register, Address offset: 0x70 + pub CSR: VolatileCell<u32>, // < RCC clock control & status register, Address offset: 0x74 + pub RESERVED6: [VolatileCell<u32>; 2], // < Reserved, 0x78-0x7C + pub SSCGR: VolatileCell<u32>, // < RCC spread spectrum clock generation register, Address offset: 0x80 + pub PLLI2SCFGR: VolatileCell<u32>, // < RCC PLLI2S configuration register, Address offset: 0x84 + } + + impl RCC { + pub fn get() -> *mut RCC { + address::RCC_BASE as *mut RCC + } + } + + #[repr(C)] + #[allow(non_snake_case)] + #[rustfmt::skip] + pub struct GPIOA { + pub MODER: VolatileCell<u32>, // < GPIO port mode register, Address offset: 0x00 + pub OTYPER: VolatileCell<u32>, // < GPIO port output type register, Address offset: 0x04 + pub OSPEEDR: VolatileCell<u32>, // < GPIO port output speed register, Address offset: 0x08 + pub PUPDR: VolatileCell<u32>, // < GPIO port pull-up/pull-down register, Address offset: 0x0C + pub IDR: VolatileCell<u32>, // < GPIO port input data register, Address offset: 0x10 + pub ODR: VolatileCell<u32>, // < GPIO port output data register, Address offset: 0x14 + pub BSRRL: VolatileCell<u16>, // < GPIO port bit set/reset low register, Address offset: 0x18 + pub BSRRH: VolatileCell<u16>, // < GPIO port bit set/reset high register, Address offset: 0x1A + pub LCKR: VolatileCell<u32>, // < GPIO port configuration lock register, Address offset: 0x1C + pub AFR: [VolatileCell<u32>;2], // < GPIO alternate function registers, Address offset: 0x20-0x24 + } + + impl GPIOA { + pub fn get() -> *mut GPIOA { + GPIOA_BASE as *mut GPIOA + } + } +} +use stm32f40x::*; + +// see the Reference Manual RM0368 (www.st.com/resource/en/reference_manual/dm00096844.pdf) +// rcc, chapter 6 +// gpio, chapter 8 + +fn wait(i: u32) { + for _ in 0..i { + cortex_m::asm::nop(); // no operation (cannot be optimized out) + } +} + +// simple test of Your `modify` +//fn test() { + // let t:VolatileCell<u32> = unsafe { core::mem::uninitialized() }; + // t.write(0); + // assert!(t.read() == 0); + // t.modify(3, 3, 0b10101); + // // + // // 10101 + // // ..0111000 + // // --------- + // // 000101000 + // assert!(t.read() == 0b101 << 3); + // t.modify(4, 3, 0b10001); + // // 000101000 + // // 111 + // // 001 + // // 000011000 + // assert!(t.read() == 0b011 << 3); + + // if << is used, your code will panic in dev, but not in release mode + // t.modify(32, 3, 1); +//} + +// system startup, can be hidden from the user +#[entry] +fn main() -> ! { + let rcc = unsafe { &mut *RCC::get() }; // get the reference to RCC in memory + let gpioa = unsafe { &mut *GPIOA::get() }; // get the reference to GPIOA in memory + + + // test(); // uncomment to run test + idle(rcc, gpioa); + loop {} +} + +// user application +fn idle(rcc: &mut RCC, gpioa: &mut GPIOA) { + // power on GPIOA + let r = rcc.AHB1ENR.read(); // read + rcc.AHB1ENR.write(r | 1 << (0)); // set enable + + // configure PA5 as output + let r = gpioa.MODER.read() & !(0b11 << (5 * 2)); // read and mask + gpioa.MODER.write(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 + //gpioa.BSRRH.write(1 << 5); // set bit, output hight (turn on led) + gpioa.ODR.write(gpioa.ODR.read() | (1 << 5)); + + wait(10_000); + + // set PA5 low + // gpioa.BSRRL.write(1 << 5); // clear bit, output low (turn off led) + gpioa.ODR.write(gpioa.ODR.read() & !(1 << 5)); + wait(10_000); + } +} + +// 1. C like API. +// In C the .h files are used for defining interfaces, like function signatures (prototypes), +// structs and macros (but usually not the functions themselves) +// here is a peripheral abstraction quite similar to what you would find in the .h files +// provided by ST (and other companies). Actually, the file presesnted here is mostly a +// cut/paste/replace of the stm32f40x.h, just Rustified. +// +// In this case we pass mutable pointers of the peripherals to the `idle`. +// +// Rewrite the accesses in the loop to use the data register directly (and make a read/modify/write). +// +// Run and see that the program behaves the same. +// +// commit your answers (bare5_1) +// +// 2. Extend the read/write API with a modify for u32, taking the +// - address (&mut u32), +// - field offset (in bits, u8), +// - field width (in bits, u8), +// - and value (u32). +// +// Implement and check that running `test` gives you expected behavior. +// +// Change the code into using your new API. +// +// Run and see that the program behaves the same. +// +// commit your answers (bare5_2) +// +// Discussion: +// As with algebraic operations, defalut semantics differ in between +// debug/dev and release builds. E.g., debug << rhs is checked, rhs must be less +// than 32 (for 32 bit datatypes). +// +// Notice, over-shifting (where bits are spilled) is always considered legal, +// its just the shift amuount that is checked. +// There are explicit unchecked versions available if so wanted. \ No newline at end of file -- GitLab