Skip to content
Snippets Groups Projects
Commit 0deaa01f authored by Per Lindgren's avatar Per Lindgren
Browse files

bare4 and bare5

parent acf1041f
No related branches found
No related tags found
No related merge requests found
...@@ -288,7 +288,7 @@ ...@@ -288,7 +288,7 @@
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"servertype": "openocd", "servertype": "openocd",
"name": "bare3 (dubug)", "name": "bare3 (debug)",
"preLaunchTask": "cargo build --example bare3", "preLaunchTask": "cargo build --example bare3",
"executable": "./target/thumbv7em-none-eabihf/debug/examples/bare3", "executable": "./target/thumbv7em-none-eabihf/debug/examples/bare3",
"postLaunchCommands": [ "postLaunchCommands": [
...@@ -300,6 +300,54 @@ ...@@ -300,6 +300,54 @@
], ],
"cwd": "${workspaceRoot}" "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", "type": "cortex-debug",
"request": "launch", "request": "launch",
...@@ -377,6 +425,6 @@ ...@@ -377,6 +425,6 @@
] ]
}, },
"cwd": "${workspaceRoot}" "cwd": "${workspaceRoot}"
} },
] ]
} }
\ No newline at end of file
...@@ -147,5 +147,41 @@ ...@@ -147,5 +147,41 @@
"isDefault": true "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
//! 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)
//! 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment