diff options
Diffstat (limited to 'src/peripheral/mod.rs')
-rw-r--r-- | src/peripheral/mod.rs | 494 |
1 files changed, 398 insertions, 96 deletions
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index c0cb299..c6e8faa 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -1,144 +1,446 @@ //! Core peripherals //! -//! # Notes -//! -//! - Although the `*_mut()` functions always return a valid/live reference, the API doesn't prevent -//! the user from creating multiple mutable aliases. It's up to the user to ensure that no -//! unsynchonized concurrent access is performed through these references. -//! -//! # Caveats -//! -//! - The API doesn't check if the value passed to `write` is valid (e.g. reserved bits are not -//! modified) or not. It's up to the user to verify that. -//! //! # References //! //! - ARMv7-M Architecture Reference Manual (Issue E.b) - Chapter B3 -pub mod cpuid; -pub mod dcb; -pub mod dwt; -pub mod fpb; -pub mod fpu; -pub mod itm; -pub mod mpu; -pub mod nvic; -pub mod scb; -pub mod syst; -pub mod tpiu; +use core::cell::UnsafeCell; +use core::marker::PhantomData; +use core::ptr; + +use volatile_register::{RO, RW, WO}; +use interrupt::Nr; + +#[cfg(test)] mod test; -const CPUID: usize = 0xE000_ED00; -const DCB: usize = 0xE000_EDF0; -const DWT: usize = 0xE000_1000; -const FPB: usize = 0xE000_2000; -const FPU: usize = 0xE000_EF30; -const ITM: usize = 0xE000_0000; -const MPU: usize = 0xE000_ED90; -const NVIC: usize = 0xE000_E100; -const SCB: usize = 0xE000_ED04; -const SYST: usize = 0xE000_E010; -const TPIU: usize = 0xE004_0000; +/// CPUID +pub const CPUID: Peripheral<Cpuid> = unsafe { Peripheral::new(0xE000_ED00) }; + +/// Debug Control Block +pub const DCB: Peripheral<Dcb> = unsafe { Peripheral::new(0xE000_EDF0) }; + +/// Data Watchpoint and Trace unit +pub const DWT: Peripheral<Dwt> = unsafe { Peripheral::new(0xE000_1000) }; + +/// Flash Patch and Breakpoint unit +pub const FPB: Peripheral<Fpb> = unsafe { Peripheral::new(0xE000_2000) }; + +/// Floating Point Unit +pub const FPU: Peripheral<Fpu> = unsafe { Peripheral::new(0xE000_EF30) }; + +/// Instrumentation Trace Macrocell +pub const ITM: Peripheral<Itm> = unsafe { Peripheral::new(0xE000_0000) }; + +/// Memory Protection Unit +pub const MPU: Peripheral<Mpu> = unsafe { Peripheral::new(0xE000_ED90) }; + +/// Nested Vector Interrupt Controller +pub const NVIC: Peripheral<Nvic> = unsafe { Peripheral::new(0xE000_E100) }; + +/// System Control Block +pub const SCB: Peripheral<Scb> = unsafe { Peripheral::new(0xE000_ED04) }; + +/// SysTick: System Timer +pub const SYST: Peripheral<Syst> = unsafe { Peripheral::new(0xE000_E010) }; + +/// Trace Port Interface Unit; +pub const TPIU: Peripheral<Tpiu> = unsafe { Peripheral::new(0xE004_0000) }; // TODO stand-alone registers: ICTR, ACTLR and STIR -/// `&cpuid::Registers` -pub fn cpuid() -> &'static cpuid::Registers { - unsafe { ::deref(CPUID) } +/// A peripheral +pub struct Peripheral<T> + where T: 'static +{ + address: usize, + _marker: PhantomData<&'static mut T>, } -/// `&dcb::Registers` -pub fn dcb() -> &'static dcb::Registers { - unsafe { ::deref(DCB) } -} +impl<T> Peripheral<T> { + /// Creates a new peripheral + /// + /// `address` is the base address of the register block + pub const unsafe fn new(address: usize) -> Self { + Peripheral { + address: address, + _marker: PhantomData, + } + } -/// `&mut dcb::Registers` -pub unsafe fn dcb_mut() -> &'static mut dcb::Registers { - ::deref_mut(DCB) + /// Returns a pointer to the register block + pub fn get(&self) -> *mut T { + self.address as *mut T + } } -/// `&dwt::Registers` -pub fn dwt() -> &'static dwt::Registers { - unsafe { ::deref(DWT) } +/// CPUID register block +#[repr(C)] +pub struct Cpuid { + /// CPUID base + pub base: RO<u32>, + reserved0: [u32; 15], + /// Processor Feature + pub pfr: [RO<u32>; 2], + /// Debug Feature + pub dfr: RO<u32>, + /// Auxiliary Feature + pub afr: RO<u32>, + /// Memory Model Feature + pub mmfr: [RO<u32>; 4], + /// Instruction Set Attribute + pub isar: [RO<u32>; 5], + reserved1: u32, + /// Cache Level ID + pub clidr: RO<u32>, + /// Cache Type + pub ctr: RO<u32>, + /// Cache Size ID + pub ccsidr: RO<u32>, + /// Cache Size Selection + pub csselr: RO<u32>, } -/// `&mut dwt::Registers` -pub unsafe fn dwt_mut() -> &'static mut dwt::Registers { - ::deref_mut(DWT) +/// DCB register block +#[repr(C)] +pub struct Dcb { + /// Debug Halting Control and Status + pub dhcsr: RW<u32>, + /// Debug Core Register Selector + pub dcrsr: WO<u32>, + /// Debug Core Register Data + pub dcrdr: RW<u32>, + /// Debug Exception and Monitor Control + pub demcr: RW<u32>, } -/// `&fpb::Registers` -pub fn fpb() -> &'static fpb::Registers { - unsafe { ::deref(FPB) } +/// DWT register block +#[repr(C)] +pub struct Dwt { + /// Control + pub ctrl: RW<u32>, + /// Cycle Count + pub cyccnt: RW<u32>, + /// CPI Count + pub cpicnt: RW<u32>, + /// Exception Overhead Count + pub exccnt: RW<u32>, + /// Sleep Count + pub sleepcnt: RW<u32>, + /// LSU Count + pub lsucnt: RW<u32>, + /// Folded-instruction Count + pub foldcnt: RW<u32>, + /// Program Counter Sample + pub pcsr: RO<u32>, + /// Comparators + pub c: [Comparator; 16], + reserved: [u32; 932], + /// Lock Access + pub lar: WO<u32>, + /// Lock Status + pub lsr: RO<u32>, } -/// `&mut fpb::Registers` -pub unsafe fn fpb_mut() -> &'static mut fpb::Registers { - ::deref_mut(FPB) +/// Comparator +#[repr(C)] +pub struct Comparator { + /// Comparator + pub comp: RW<u32>, + /// Comparator Mask + pub mask: RW<u32>, + /// Comparator Function + pub function: RW<u32>, + reserved: u32, } -/// `&fpu::Registers` -pub fn fpu() -> &'static fpu::Registers { - unsafe { ::deref(FPU) } +/// FPB register block +#[repr(C)] +pub struct Fpb { + /// Control + pub ctrl: RW<u32>, + /// Remap + pub remap: RW<u32>, + /// Comparator + pub comp: [RW<u32>; 127], + reserved: [u32; 875], + /// Lock Access + pub lar: WO<u32>, + /// Lock Status + pub lsr: RO<u32>, } -/// `&mut fpu::Registers` -pub unsafe fn fpu_mut() -> &'static mut fpu::Registers { - ::deref_mut(FPU) +/// FPU register block +#[repr(C)] +pub struct Fpu { + reserved: u32, + /// Floating Point Context Control + pub fpccr: RW<u32>, + /// Floating Point Context Address + pub fpcar: RW<u32>, + /// Floating Point Default Status Control + pub fpdscr: RW<u32>, + /// Media and FP Feature + pub mvfr: [RO<u32>; 3], } -/// `&itm::Registers` -pub fn itm() -> &'static itm::Registers { - unsafe { ::deref(ITM) } +/// ITM register block +#[repr(C)] +pub struct Itm { + /// Stimulus Port + pub stim: [Stim; 256], + reserved0: [u32; 640], + /// Trace Enable + pub ter: [RW<u32>; 8], + reserved1: [u32; 8], + /// Trace Privilege + pub tpr: RW<u32>, + reserved2: [u32; 15], + /// Trace Control + pub tcr: RW<u32>, + reserved3: [u32; 75], + /// Lock Access + pub lar: WO<u32>, + /// Lock Status + pub lsr: RO<u32>, } -/// `&mut itm::Registers` -pub unsafe fn itm_mut() -> &'static mut itm::Registers { - ::deref_mut(ITM) +/// Stimulus Port +pub struct Stim { + register: UnsafeCell<u32>, } -/// `&mpu::Registers` -pub fn mpu() -> &'static mpu::Registers { - unsafe { ::deref(MPU) } -} +impl Stim { + /// Writes an `u8` payload into the stimulus port + pub fn write_u8(&self, value: u8) { + unsafe { ptr::write_volatile(self.register.get() as *mut u8, value) } + } -/// `&mut mpu::Registers` -pub unsafe fn mpu_mut() -> &'static mut mpu::Registers { - ::deref_mut(MPU) -} + /// Writes an `u16` payload into the stimulus port + pub fn write_u16(&self, value: u16) { + unsafe { ptr::write_volatile(self.register.get() as *mut u16, value) } + } -/// `&mut nvic::Registers` -pub fn nvic() -> &'static mut nvic::Registers { - unsafe { ::deref_mut(NVIC) } + /// Writes an `u32` payload into the stimulus port + pub fn write_u32(&self, value: u32) { + unsafe { ptr::write_volatile(self.register.get(), value) } + } + + /// Returns `true` if the stimulus port is ready to accept more data + pub fn is_fifo_ready(&self) -> bool { + unsafe { ptr::read_volatile(self.register.get()) == 1 } + } } -/// `&scb::Registers` -pub fn scb() -> &'static scb::Registers { - unsafe { ::deref(SCB) } +/// MPU register block +#[repr(C)] +pub struct Mpu { + /// Type + pub _type: RO<u32>, + /// Control + pub ctrl: RW<u32>, + /// Region Number + pub rnr: RW<u32>, + /// Region Base Address + pub rbar: RW<u32>, + /// Region Attribute and Size + pub rasr: RW<u32>, + /// Alias 1 of RBAR + pub rbar_a1: RW<u32>, + /// Alias 1 of RSAR + pub rsar_a1: RW<u32>, + /// Alias 2 of RBAR + pub rbar_a2: RW<u32>, + /// Alias 2 of RSAR + pub rsar_a2: RW<u32>, + /// Alias 3 of RBAR + pub rbar_a3: RW<u32>, + /// Alias 3 of RSAR + pub rsar_a3: RW<u32>, } -/// `&mut scb::Registers` -pub unsafe fn scb_mut() -> &'static mut scb::Registers { - ::deref_mut(SCB) +/// NVIC register block +#[repr(C)] +pub struct Nvic { + /// Interrupt Set-Enable + pub iser: [RW<u32>; 8], + reserved0: [u32; 24], + /// Interrupt Clear-Enable + pub icer: [RW<u32>; 8], + reserved1: [u32; 24], + /// Interrupt Set-Pending + pub ispr: [RW<u32>; 8], + reserved2: [u32; 24], + /// Interrupt Clear-Pending + pub icpr: [RW<u32>; 8], + reserved3: [u32; 24], + /// Interrupt Active Bit + pub iabr: [RO<u32>; 8], + reserved4: [u32; 56], + /// Interrupt Priority + pub ipr: [RW<u8>; 240], } -/// `&syst::Registers` -pub fn syst() -> &'static syst::Registers { - unsafe { ::deref(SYST) } +impl Nvic { + /// Clears `interrupt`'s pending state + pub fn clear_pending<I>(&self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + unsafe { self.icpr[usize::from(nr / 32)].write(1 << (nr % 32)) } + } + + /// Disables `interrupt` + pub fn disable<I>(&self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + unsafe { self.icer[usize::from(nr / 32)].write(1 << (nr % 32)) } + } + + /// Enables `interrupt` + pub fn enable<I>(&self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + unsafe { self.iser[usize::from(nr / 32)].write(1 << (nr % 32)) } + } + + /// Gets the "priority" of `interrupt` + /// + /// NOTE NVIC encodes priority in the highest bits of a byte so values like + /// `1` and `2` have the same priority. Also for NVIC priorities, a lower + /// value (e.g. `16`) has higher priority than a larger value (e.g. `32`). + pub fn get_priority<I>(&self, interrupt: I) -> u8 + where I: Nr + { + let nr = interrupt.nr(); + + self.ipr[usize::from(nr)].read() + } + + /// Is `interrupt` active or pre-empted and stacked + pub fn is_active<I>(&self, interrupt: I) -> bool + where I: Nr + { + let nr = interrupt.nr(); + let mask = 1 << (nr % 32); + + (self.iabr[usize::from(nr / 32)].read() & mask) == mask + } + + /// Checks if `interrupt` is enabled + pub fn is_enabled<I>(&self, interrupt: I) -> bool + where I: Nr + { + let nr = interrupt.nr(); + let mask = 1 << (nr % 32); + + (self.iser[usize::from(nr / 32)].read() & mask) == mask + } + + /// Checks if `interrupt` is pending + pub fn is_pending<I>(&self, interrupt: I) -> bool + where I: Nr + { + let nr = interrupt.nr(); + let mask = 1 << (nr % 32); + + (self.ispr[usize::from(nr / 32)].read() & mask) == mask + } + + /// Forces `interrupt` into pending state + pub fn set_pending<I>(&self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + unsafe { self.ispr[usize::from(nr / 32)].write(1 << (nr % 32)) } + } + + /// Sets the "priority" of `interrupt` to `prio` + /// + /// NOTE See `get_priority` method for an explanation of how NVIC priorities + /// work. + pub fn set_priority<I>(&self, interrupt: I, prio: u8) + where I: Nr + { + let nr = interrupt.nr(); + + unsafe { self.ipr[usize::from(nr)].write(prio) } + } } -/// `&mut syst::Registers` -pub unsafe fn syst_mut() -> &'static mut syst::Registers { - ::deref_mut(SYST) +/// SCB register block +#[repr(C)] +pub struct Scb { + /// Interrupt Control and State + pub icsr: RW<u32>, + /// Vector Table Offset + pub vtor: RW<u32>, + /// Application Interrupt and Reset Control + pub aircr: RW<u32>, + /// System Control + pub scr: RW<u32>, + /// Configuration and Control + pub ccr: RW<u32>, + /// System Handler Priority + pub shpr: [RW<u8>; 12], + /// System Handler Control and State + pub shpcrs: RW<u32>, + /// Configurable Fault Status + pub cfsr: RW<u32>, + /// HardFault Status + pub hfsr: RW<u32>, + /// Debug Fault Status + pub dfsr: RW<u32>, + /// MemManage Fault Address + pub mmar: RW<u32>, + /// BusFault Address + pub bfar: RW<u32>, + /// Auxiliary Fault Status + pub afsr: RW<u32>, + reserved: [u32; 18], + /// Coprocessor Access Control + pub cpacr: RW<u32>, } -/// `&tpiu::Registers` -pub fn tpiu() -> &'static tpiu::Registers { - unsafe { ::deref(TPIU) } +/// SysTick register block +#[repr(C)] +pub struct Syst { + /// Control and Status + pub csr: RW<u32>, + /// Reload Value + pub rvr: RW<u32>, + /// Current Value + pub cvr: RW<u32>, + /// Calibration Value + pub calib: RO<u32>, } -/// `&mut tpiu::Registers` -pub unsafe fn tpiu_mut() -> &'static mut tpiu::Registers { - ::deref_mut(TPIU) +/// TPIU register block +#[repr(C)] +pub struct Tpiu { + /// Supported Parallel Port Sizes + pub sspsr: RO<u32>, + /// Current Parallel Port Size + pub cspsr: RW<u32>, + reserved0: [u32; 2], + /// Asynchronous Clock Prescaler + pub acpr: RW<u32>, + reserved1: [u32; 55], + /// Selected Pin Control + pub sppr: RW<u32>, + reserved2: [u32; 943], + /// Lock Access + pub lar: WO<u32>, + /// Lock Status + pub lsr: RO<u32>, + reserved3: [u32; 4], + /// TPIU Type + pub _type: RO<u32>, } |