Skip to content
Snippets Groups Projects
Select Git revision
  • 366ca441bbe2ea1ad6432ae2ae89dc25e0f24dee
  • student default protected
2 results

bare5.rs

Blame
  • Forked from Per Lindgren / e7020e_2019
    Source project has a limited visibility.
    bare5.rs 12.13 KiB
    //! bare5.rs
    //!
    //! C Like Peripheral API
    //!
    //! What it covers:
    //! - abstractions in Rust
    //! - structs and implementations
    
    #![no_std]
    #![no_main]
    
    extern crate panic_halt;
    
    extern crate cortex_m;
    use cortex_m_rt::entry;
    use cortex_m_semihosting::{hprint, hprintln};
    
    // 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) }
            }
        }
    
        // modify (reads, modifies a field, and writes the volatile cell)
        //
        // parameters:
        // offset (field offset)
        // width  (field width)
        // value  (new value that the field should take)
        impl VolatileCell<u32> {
            #[inline(always)]
            pub fn modify(&self, offset: u8, width: u8, value: u32) {
                let mask: u32 = 2u32.pow(width as u32) - 1; // 'width' amount of binary 1s.
    
                // Mask the input
                let in_masked: u32 = value & mask;
    
                // Mask the previous value
                let previous: u32 = self.read();
                let previous_masked: u32 = previous & !(mask * 2u32.pow(offset as u32));
    
                self.write(previous_masked | (in_masked * 2u32.pow(offset as u32)));
            }   
        }   
    
        #[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
    //     hprintln!("{:b}", t.read()).unwrap();
    //     assert!(t.read() == 0b101 << 3);
    //     t.modify(4, 3, 0b10001);
    //     //    000101000
    //     //      111
    //     //      001
    //     //    000011000
    //     hprintln!("{:b}", t.read()).unwrap();
    //     assert!(t.read() == 0b011 << 3);
    
    //     // if << is used, your code will panic in dev (debug), 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
        rcc.AHB1ENR.modify(0u8, 1u8, 1u32);
        // let r = rcc.AHB1ENR.read(); // read
        // rcc.AHB1ENR.write(r | 1 << (0)); // set enable
    
        // configure PA5 as output
        gpioa.MODER.modify(10u8, 2u8, 1u32);
        // let r = gpioa.MODER.read() & !(0b11 << (5 * 2)); // read and mask
        // gpioa.MODER.write(r | 0b01 << (5 * 2)); // set output mode
        let led_odr = &gpioa.ODR;
        loop {
            // set PA5 high
            // gpioa.ODR.modify(5u8, 1u8, 1u32);
            led_odr.modify(5u8, 1u8, 1u32);
    
            // 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.ODR.modify(5u8, 1u8, 0u32);
            led_odr.modify(5u8, 1u8, 0u32);
            // gpioa.BSRRL.write(1 << 5); // clear bit, output low (turn off led)
            // gpioa.ODR.write(gpioa.ODR.read() & !(1 << 5));
            wait(10_000);
        }
    }
    
    // 0. Build and run the application.
    //
    //    > cargo build --example bare5
    //    (or use the vscode build task)
    //
    // 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` function.
    //
    //    In the loop we access PA5 through bit set/clear operations.
    //    Comment out those opertations and uncomment the the ODR accesses.
    //    (They should have the same behavior, but is a bit less efficient.)
    //
    //    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.
    // 
    //      ** ORDERING IS WRONG FOR OPTIMIZED CODE **
    //      When I run the --release build, the LED is constantly turned on. HOWEVER,
    //      if I set breakpoints and pause in the --release build, I can pause it
    //      just after the LED-turn-off call and the LED turns off, proving that the
    //      instructions work in release as they do in debug, but that it doesn't wait
    //      before running the turn-on call as it should.