Skip to content
Snippets Groups Projects
Select Git revision
  • 4a1f1a267837d555be0d9efb9cc2f4974ad65099
  • student default protected
2 results

bare4.rs

Blame
  • Forked from Per Lindgren / e7020e_2019
    Source project has a limited visibility.
    bare4.rs 4.92 KiB
    //! bare4.rs
    //!
    //! Access to Peripherals
    //!
    //! What it covers:
    //! - Raw pointers
    //! - Volatile read/write
    //! - Busses and clocking
    //! - GPIO
    
    #![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;               // Seen in memory map, 2.3
        pub const AHB1PERIPH_BASE: u32  = PERIPH_BASE + 0x00020000; // GPIO(A) registers, 8.4
        pub const RCC_BASE: u32         = AHB1PERIPH_BASE + 0x3800; // RCC registers, 6.3
        pub const RCC_AHB1ENR: u32      = RCC_BASE + 0x30;          // 6.3.9
        pub const GBPIA_BASE: u32       = AHB1PERIPH_BASE + 0x0000; // 8.4
        pub const GPIOA_MODER: u32      = GBPIA_BASE + 0x00;        // 8.4.1
        pub const GPIOA_BSRR: u32       = GBPIA_BASE + 0x18;        // GPIO port bit set/reset register BSRR 8.4.7
    }
    
    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, 6.3.9
        let r = read_u32(RCC_AHB1ENR); // read
        write_u32(RCC_AHB1ENR, r | 1); // set enable
    
        // configure PA5 as output, 8.4.1
        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, 8.4.7
            write_u32(GPIOA_BSRR, 1 << 5); // set bit, output hight (turn on led)
            wait(10_000);
    
            // set PA5 low, 8.4.7
            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 build --example bare4
    //    (or use the vscode build task)
    //
    // 1.  Did you enjoy the blinking?
    //
    //    ** Thouroughly **
    //
    //    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.
    //
    //    ** Compiler error E0133: "call to unsafe function is unsafe ..."
    //    Because read_volatile is defined as unsafe. **
    //
    //    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 )
    //
    //    ** The section on safety reads that the behaviour is undefined if the parameter is not valid. 
    //    The passed parameter must not be unaligned nor out of bounds. Undefined behaviour is unsafe! **
    //
    //    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?
    //
    //    ** If read/write instructions are reordered, the consequences are undefined, since
    //    what was intended to be read might not be there. And since peripherals' configurations
    //    depend on the contents of registers that are read from/written to, configurations might
    //    go wrong. **
    //
    //    Give an example in the above code, where reordering might make things go horribly wrong
    //    (hint, accessing a peripheral not being powered...)
    //
    //    ** We set port A to general purpose output mode. There is undefined, unintended behaviour
    //    if we write to the port bits without enabling it. **
    //
    //    Without the non-reording proprety of `write_volatile/read_volatile` could that happen in theory
    //    (argue from the point of data dependencies).
    //
    //    ** If we read from a register to apply a mask on it, it most probably needs to remain that value
    //    before writing the masked value to it, otherwise the masked value is not accurate. If read/writes
    //    are reordered during many reads and writes (e.g. during configuration), the configuration might end
    //    up faulty. **
    //
    //    Commit your answers (bare4_3)