From 4979a7d3831cdfd50133edff04711f8190b16021 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 28 Feb 2017 09:59:03 -0500 Subject: [PATCH 01/31] changes to better integrate with svd2rust --- Cargo.toml | 7 +++ src/exception.rs | 73 +++++++++++++++++++++++++++ src/interrupt.rs | 6 +++ src/lib.rs | 29 ++++++++--- src/macros.rs | 29 +++++++++++ src/peripheral/nvic.rs | 109 ++++++++++++++++++++++++++++++++++++----- 6 files changed, 234 insertions(+), 19 deletions(-) create mode 100644 src/macros.rs diff --git a/Cargo.toml b/Cargo.toml index 25b8806d..e0705490 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,10 @@ version = "0.1.6" [dependencies] volatile-register = "0.1.0" + +[dependencies.cortex-m-semihosting] +optional = true +version = "0.1.3" + +[features] +semihosting = ["cortex-m-semihosting"] \ No newline at end of file diff --git a/src/exception.rs b/src/exception.rs index 70460808..fad3ea96 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,3 +1,7 @@ +//! Exceptions + +use {Handler, Reserved, StackFrame}; + /// Kind of exception #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Exception { @@ -41,7 +45,76 @@ impl Exception { 15 => Exception::Systick, n if n >= 16 => Exception::Interrupt(n - 16), _ => Exception::Reserved, + } + } +} +/// Exception handlers +#[repr(C)] +pub struct Handlers { + /// Non-maskable interrupt + pub nmi: Handler, + /// All class of fault + pub hard_fault: Handler, + /// Memory management + pub mem_manage: Handler, + /// Pre-fetch fault, memory access fault + pub bus_fault: Handler, + /// Undefined instruction or illegal state + pub usage_fault: Handler, + /// Reserved spots in the vector table + pub _reserved0: [Reserved; 4], + /// System service call via SWI instruction + pub svcall: Handler, + /// Reserved spots in the vector table + pub _reserved1: [Reserved; 2], + /// Pendable request for system service + pub pendsv: Handler, + /// System tick timer + pub sys_tick: Handler, +} + +/// Default exception handlers +pub const DEFAULT_HANDLERS: Handlers = Handlers { + _reserved0: [Reserved::Vector; 4], + _reserved1: [Reserved::Vector; 2], + bus_fault: default_handler, + hard_fault: default_handler, + mem_manage: default_handler, + nmi: default_handler, + pendsv: default_handler, + svcall: default_handler, + sys_tick: default_handler, + usage_fault: default_handler, +}; + +/// The default exception handler +/// +/// This handler triggers a breakpoint (`bkpt`) and gives you access, within a +/// GDB session, to the stack frame (`_sf`) where the exception occurred. +// This needs asm!, #[naked] and unreachable() to avoid modifying the stack +// pointer (MSP), that way it points to the previous stack frame +#[naked] +pub unsafe extern "C" fn default_handler() { + // This is the actual exception handler. `_sf` is a pointer to the previous + // stack frame + extern "C" fn handler(_sf: &StackFrame) -> ! { + #[cfg(feature = "semihosting")] + hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sf.pc); + + unsafe { + bkpt!(); } + + loop {} } + + // "trampoline" to get to the real exception handler. + asm!("mrs r0, MSP + ldr r1, [r0, #20] + b $0" + : + : "i"(handler as extern "C" fn(&StackFrame) -> !) :: "volatile"); + + ::core::intrinsics::unreachable() } diff --git a/src/interrupt.rs b/src/interrupt.rs index 43050717..035dcd40 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -26,6 +26,12 @@ impl Mutex { } } +/// Interrupt number +pub unsafe trait Nr { + /// Returns the number associated with this interrupt + fn nr(&self) -> u8; +} + // FIXME `T` should have some bound: `Send` or `Sync`? unsafe impl Sync for Mutex {} diff --git a/src/lib.rs b/src/lib.rs index 09eeceba..5e75d533 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,25 +7,30 @@ //! - Interrupt manipulation mechanisms //! - Data structures like the vector table //! - Miscellaneous assembly instructions like `bkpt` -//! +#![cfg_attr(feature = "semihosting", feature(macro_reexport))] #![deny(missing_docs)] #![deny(warnings)] #![feature(asm)] #![feature(const_fn)] +#![feature(core_intrinsics)] +#![feature(naked_functions)] #![no_std] +#[cfg(feature = "semihosting")] +pub extern crate cortex_m_semihosting as semihosting; extern crate volatile_register; +#[macro_use] +mod macros; + +#[macro_use] pub mod asm; +pub mod exception; pub mod interrupt; pub mod peripheral; pub mod register; -mod exception; - -pub use exception::Exception; - /// Stack frame #[repr(C)] pub struct StackFrame { @@ -82,14 +87,22 @@ pub struct VectorTable { pub interrupts: [Option; 0], } -/// Returns the vector table -pub fn vector_table() -> &'static VectorTable { - unsafe { deref(peripheral::scb().vtor.read() as usize) } +/// A reserved spot in the vector table +#[derive(Clone, Copy)] +#[repr(u32)] +pub enum Reserved { + /// Reserved + Vector = 0, } /// Exception/Interrupt Handler pub type Handler = unsafe extern "C" fn(); +/// Returns the vector table +pub fn vector_table() -> &'static VectorTable { + unsafe { deref(peripheral::scb().vtor.read() as usize) } +} + #[cfg(test)] fn address(r: &T) -> usize { r as *const T as usize diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 00000000..bd311676 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,29 @@ +/// Macro for printing to the **host's** standard stderr +#[macro_export] +macro_rules! ehprint { + ($s:expr) => ($crate::semihosting:::io:ewrite_str($s)); + ($($arg:tt)*) => ($crate::semihosting::io::ewrite_fmt(format_args!($($arg)*))); +} + +/// Macro for printing to the **host's** standard error, with a newline. +#[macro_export] +macro_rules! ehprintln { + () => (ehprint!("\n")); + ($fmt:expr) => (ehprint!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (ehprint!(concat!($fmt, "\n"), $($arg)*)); +} + +/// Macro for printing to the **host's** standard output +#[macro_export] +macro_rules! hprint { + ($s:expr) => ($crate::semihosting::io::write_str($s)); + ($($arg:tt)*) => ($crate::semihosting::io::write_fmt(format_args!($($arg)*))); +} + +/// Macro for printing to the **host's** standard output, with a newline. +#[macro_export] +macro_rules! hprintln { + () => (hprint!("\n")); + ($fmt:expr) => (hprint!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (hprint!(concat!($fmt, "\n"), $($arg)*)); +} diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 4570c0f3..64adc904 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -1,25 +1,112 @@ //! Nested Vector Interrupt Controller +use interrupt::Nr; use volatile_register::{RO, RW}; /// Registers #[repr(C)] pub struct Registers { /// Interrupt Set-Enable - pub iser: [RW; 16], - reserved0: [u32; 16], + pub iser: [RW; 8], + reserved0: [u32; 24], /// Interrupt Clear-Enable - pub icer: [RW; 16], - reserved1: [u32; 16], + pub icer: [RW; 8], + reserved1: [u32; 24], /// Interrupt Set-Pending - pub ispr: [RW; 16], - reserved2: [u32; 16], + pub ispr: [RW; 8], + reserved2: [u32; 24], /// Interrupt Clear-Pending - pub icpr: [RW; 16], - reserved3: [u32; 16], + pub icpr: [RW; 8], + reserved3: [u32; 24], /// Interrupt Active Bit - pub iabr: [RO; 16], - reserved4: [u32; 48], + pub iabr: [RO; 8], + reserved4: [u32; 56], /// Interrupt Priority - pub ipr: [RW; 124], + pub ipr: [RW; 240], +} + +impl Registers { + /// Clears `interrupt` pending state + pub fn clear_pending(&mut self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + self.icpr[usize::from(nr / 32)].write(1 << (nr % 32)); + } + + /// Disables `interrupt` + pub fn disable(&mut self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + self.icer[usize::from(nr / 32)].write(1 << (nr % 32)); + } + + /// Enables `interrupt` + pub fn enable(&mut self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + self.iser[usize::from(nr / 32)].write(1 << (nr % 32)); + } + + /// Gets the priority of `interrupt` + pub fn get_priority(&mut self, interrupt: I) -> u8 + where I: Nr + { + let nr = interrupt.nr(); + + self.ipr[usize::from(nr / 4)].read() + } + + /// Is `interrupt` active or pre-empted and stacked + pub fn is_active(&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(&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(&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(&mut self, interrupt: I) + where I: Nr + { + let nr = interrupt.nr(); + + self.ispr[usize::from(nr / 32)].write(1 << (nr % 32)); + } + + /// Sets the priority of `interrupt` to `prio` + pub fn set_priority(&mut self, interrupt: I, prio: u8) + where I: Nr + { + let nr = interrupt.nr(); + + self.ipr[usize::from(nr / 4)].write(prio); + } } From 5959572dcd7eaca0f8d364372637c2629f628c12 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 28 Feb 2017 11:21:05 -0500 Subject: [PATCH 02/31] hide the registers of the NVIC register block thus only allowing access through the high level API With this change `peripheral::nvic() -> &mut nvic::Registers` becomes safe since the high level API is atomic. --- src/peripheral/mod.rs | 9 ++------- src/peripheral/nvic.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index 9f02aa39..c0cb2998 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -108,14 +108,9 @@ pub unsafe fn mpu_mut() -> &'static mut mpu::Registers { ::deref_mut(MPU) } -/// `&nvic::Registers` -pub fn nvic() -> &'static nvic::Registers { - unsafe { ::deref(NVIC) } -} - /// `&mut nvic::Registers` -pub unsafe fn nvic_mut() -> &'static mut nvic::Registers { - ::deref_mut(NVIC) +pub fn nvic() -> &'static mut nvic::Registers { + unsafe { ::deref_mut(NVIC) } } /// `&scb::Registers` diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 64adc904..2cc5a8e8 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -7,22 +7,22 @@ use volatile_register::{RO, RW}; #[repr(C)] pub struct Registers { /// Interrupt Set-Enable - pub iser: [RW; 8], + iser: [RW; 8], reserved0: [u32; 24], /// Interrupt Clear-Enable - pub icer: [RW; 8], + icer: [RW; 8], reserved1: [u32; 24], /// Interrupt Set-Pending - pub ispr: [RW; 8], + ispr: [RW; 8], reserved2: [u32; 24], /// Interrupt Clear-Pending - pub icpr: [RW; 8], + icpr: [RW; 8], reserved3: [u32; 24], /// Interrupt Active Bit - pub iabr: [RO; 8], + iabr: [RO; 8], reserved4: [u32; 56], /// Interrupt Priority - pub ipr: [RW; 240], + ipr: [RW; 240], } impl Registers { From 4a40edc9281e8d504f32630f2f260a1b69c8365f Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 28 Feb 2017 14:38:25 -0500 Subject: [PATCH 03/31] NVIC: fix set/get_priority methods --- src/peripheral/nvic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 2cc5a8e8..91e158c5 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -59,7 +59,7 @@ impl Registers { { let nr = interrupt.nr(); - self.ipr[usize::from(nr / 4)].read() + self.ipr[usize::from(nr)].read() } /// Is `interrupt` active or pre-empted and stacked @@ -107,6 +107,6 @@ impl Registers { { let nr = interrupt.nr(); - self.ipr[usize::from(nr / 4)].write(prio); + self.ipr[usize::from(nr)].write(prio); } } From afa752b249d0d33ca9a46a6b4e390e13206990c3 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 28 Feb 2017 14:39:00 -0500 Subject: [PATCH 04/31] add shortcut for 'MSR BASEPRI_MAX' --- src/register.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/register.rs b/src/register.rs index 014ba11c..8ab9a8df 100644 --- a/src/register.rs +++ b/src/register.rs @@ -121,4 +121,5 @@ sro!(#[doc = "Exception Program Status Register"] epsr); srw!(#[doc = "Priority Mask Register"] primask); srw!(#[doc = "Fault Mask Register"] faultmask); srw!(#[doc = "Base Priority Mask Register"] basepri); +srw!(#[doc = "Base Priority Mask Register"] basepri_max); srw!(#[doc = "Control Register"] control); From fede2074f7fa74e0ab0211bab7af4240954f4e0a Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 28 Feb 2017 21:07:03 -0500 Subject: [PATCH 05/31] fix get/set_priority these methods must write to the highest bits of the IPR registers --- build.rs | 9 +++++++++ src/peripheral/nvic.rs | 11 +++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..f8ad1634 --- /dev/null +++ b/build.rs @@ -0,0 +1,9 @@ +use std::env; + +fn main() { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + println!("cargo:rustc-cfg=armv6m") + } +} diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 91e158c5..054a74e1 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -3,6 +3,12 @@ use interrupt::Nr; use volatile_register::{RO, RW}; +#[cfg(thumbv6m)] +const PRIORITY_BITS: u8 = 2; + +#[cfg(not(thumbv6m))] +const PRIORITY_BITS: u8 = 4; + /// Registers #[repr(C)] pub struct Registers { @@ -59,7 +65,7 @@ impl Registers { { let nr = interrupt.nr(); - self.ipr[usize::from(nr)].read() + self.ipr[usize::from(nr)].read() >> (8 - PRIORITY_BITS) } /// Is `interrupt` active or pre-empted and stacked @@ -107,6 +113,7 @@ impl Registers { { let nr = interrupt.nr(); - self.ipr[usize::from(nr)].write(prio); + self.ipr[usize::from(nr)].write((prio & ((1 << PRIORITY_BITS) - 1)) << + (8 - PRIORITY_BITS)); } } From b4f105cde28d89f3c8e42e4fe341390a7dc2dccf Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 2 Mar 2017 10:50:27 -0500 Subject: [PATCH 06/31] add a critical section token to `interrupt::free` --- src/interrupt.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/interrupt.rs b/src/interrupt.rs index 035dcd40..edc3dfb0 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -22,7 +22,7 @@ impl Mutex { pub fn lock(&self, f: F) -> R where F: FnOnce(&mut T) -> R { - unsafe { ::interrupt::free(|| f(&mut *self.inner.get())) } + unsafe { ::interrupt::free(|_| f(&mut *self.inner.get())) } } } @@ -61,16 +61,23 @@ pub unsafe fn enable() { } } +/// Critical section token +/// +/// Indicates that you are executing code within a critical section +pub struct CsToken { + _private: (), +} + /// Execute closure `f` in an interrupt-free context. /// This as also known as a "critical section". pub unsafe fn free(f: F) -> R - where F: FnOnce() -> R + where F: FnOnce(&CsToken) -> R { let primask = ::register::primask::read(); disable(); - let r = f(); + let r = f(&CsToken { _private: () }); // If the interrupts were enabled before our `disable` call, then re-enable // them. Otherwise, keep them disabled From 251d1aa11244d5356659ccf969e29b0e7da82c7a Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 4 Mar 2017 20:46:19 -0500 Subject: [PATCH 07/31] review safety of the existing API, make the register API type safe --- src/asm.rs | 64 ++++++++++++------ src/exception.rs | 28 +++++--- src/interrupt.rs | 33 ++++++---- src/lib.rs | 2 +- src/register.rs | 125 ------------------------------------ src/register/apsr.rs | 52 +++++++++++++++ src/register/basepri.rs | 27 ++++++++ src/register/basepri_max.rs | 16 +++++ src/register/control.rs | 117 +++++++++++++++++++++++++++++++++ src/register/faultmask.rs | 40 ++++++++++++ src/register/lr.rs | 25 ++++++++ src/register/mod.rs | 41 ++++++++++++ src/register/msp.rs | 25 ++++++++ src/register/pc.rs | 25 ++++++++ src/register/primask.rs | 40 ++++++++++++ src/register/psp.rs | 25 ++++++++ 16 files changed, 520 insertions(+), 165 deletions(-) delete mode 100644 src/register.rs create mode 100644 src/register/apsr.rs create mode 100644 src/register/basepri.rs create mode 100644 src/register/basepri_max.rs create mode 100644 src/register/control.rs create mode 100644 src/register/faultmask.rs create mode 100644 src/register/lr.rs create mode 100644 src/register/mod.rs create mode 100644 src/register/msp.rs create mode 100644 src/register/pc.rs create mode 100644 src/register/primask.rs create mode 100644 src/register/psp.rs diff --git a/src/asm.rs b/src/asm.rs index 2e3368ac..b94d4efc 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -1,58 +1,86 @@ //! Miscellaneous assembly instructions -/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint". +/// Puts the processor in Debug state. Debuggers can pick this up as a +/// "breakpoint". /// -/// Optionally, an "immediate" value (in the 0-255 range) can be passed to `bkpt!`. The debugger can -/// then read this value using the Program Counter (PC). +/// Optionally, an "immediate" value (in the 0-255 range) can be passed to +/// `bkpt!`. The debugger can then read this value using the Program Counter +/// (PC). #[cfg(target_arch = "arm")] #[macro_export] macro_rules! bkpt { () => { - asm!("bkpt" :::: "volatile"); + asm!("bkpt" + : + : + : + : "volatile"); }; ($imm:expr) => { - asm!(concat!("bkpt #", stringify!($imm)) :::: "volatile"); + asm!(concat!("bkpt #", stringify!($imm)) + : + : + : + : "volatile"); }; } -/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint". +/// Puts the processor in Debug state. Debuggers can pick this up as a +/// "breakpoint". /// -/// Optionally, an "immediate" value (in the 0-255 range) can be passed to `bkpt!`. The debugger can -/// then read this value using the Program Counter (PC). +/// Optionally, an "immediate" value (in the 0-255 range) can be passed to +/// `bkpt!`. The debugger can then read this value using the Program Counter +/// (PC). #[cfg(not(target_arch = "arm"))] #[macro_export] macro_rules! bkpt { () => { - asm!("nop" :::: "volatile"); + asm!(""); }; ($e:expr) => { - asm!("nop" :::: "volatile"); + asm!(""); }; } -/// Wait for event -pub unsafe fn wfe() { +/// Wait For Event +pub fn wfe() { match () { #[cfg(target_arch = "arm")] - () => asm!("wfe" :::: "volatile"), + () => unsafe { + asm!("wfe" + : + : + : + : "volatile") + }, #[cfg(not(target_arch = "arm"))] () => {} } } -/// Wait for interupt -pub unsafe fn wfi() { +/// Wait For Interrupt +pub fn wfi() { match () { #[cfg(target_arch = "arm")] - () => asm!("wfi" :::: "volatile"), + () => unsafe{ + asm!("wfi" + : + : + : + : "volatile") + }, #[cfg(not(target_arch = "arm"))] () => {} } } -/// A no-operation. Useful to stop delay loops being elided. +/// A no-operation. Useful to prevent delay loops from being optimized away. pub fn nop() { unsafe { - asm!("nop" :::: "volatile"); + asm!("nop" + : + : + : + : "volatile"); } } diff --git a/src/exception.rs b/src/exception.rs index fad3ea96..cd383663 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,6 +1,8 @@ //! Exceptions -use {Handler, Reserved, StackFrame}; +use {Handler, Reserved}; +#[cfg(target_arch = "arm")] +use StackFrame; /// Kind of exception #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -98,6 +100,7 @@ pub const DEFAULT_HANDLERS: Handlers = Handlers { pub unsafe extern "C" fn default_handler() { // This is the actual exception handler. `_sf` is a pointer to the previous // stack frame + #[cfg(target_arch = "arm")] extern "C" fn handler(_sf: &StackFrame) -> ! { #[cfg(feature = "semihosting")] hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sf.pc); @@ -109,12 +112,21 @@ pub unsafe extern "C" fn default_handler() { loop {} } - // "trampoline" to get to the real exception handler. - asm!("mrs r0, MSP - ldr r1, [r0, #20] - b $0" - : - : "i"(handler as extern "C" fn(&StackFrame) -> !) :: "volatile"); + match () { + #[cfg(target_arch = "arm")] + () => { + // "trampoline" to get to the real exception handler. + asm!("mrs r0, MSP + ldr r1, [r0, #20] + b $0" + : + : "i"(handler as extern "C" fn(&StackFrame) -> !) + : + : "volatile"); - ::core::intrinsics::unreachable() + ::core::intrinsics::unreachable() + } + #[cfg(not(target_arch = "arm"))] + () => {} + } } diff --git a/src/interrupt.rs b/src/interrupt.rs index edc3dfb0..2552892f 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -37,12 +37,16 @@ unsafe impl Sync for Mutex {} /// Disable interrupts, globally #[inline(always)] -pub unsafe fn disable() { +pub fn disable() { match () { #[cfg(target_arch = "arm")] - () => { - asm!("cpsid i" :::: "volatile"); - } + () => unsafe { + asm!("cpsid i" + : + : + : + : "volatile"); + }, #[cfg(not(target_arch = "arm"))] () => {} } @@ -50,12 +54,16 @@ pub unsafe fn disable() { /// Enable interrupts, globally #[inline(always)] -pub unsafe fn enable() { +pub fn enable() { match () { #[cfg(target_arch = "arm")] - () => { - asm!("cpsie i" :::: "volatile"); - } + () => unsafe { + asm!("cpsie i" + : + : + : + : "volatile"); + }, #[cfg(not(target_arch = "arm"))] () => {} } @@ -70,20 +78,19 @@ pub struct CsToken { /// Execute closure `f` in an interrupt-free context. /// This as also known as a "critical section". -pub unsafe fn free(f: F) -> R +pub fn free(f: F) -> R where F: FnOnce(&CsToken) -> R { let primask = ::register::primask::read(); + // disable interrupts disable(); let r = f(&CsToken { _private: () }); - // If the interrupts were enabled before our `disable` call, then re-enable + // If the interrupts were active before our `disable` call, then re-enable // them. Otherwise, keep them disabled - // PRIMASK & 1 = 1 indicates that the interrupts were disabled - // PRIMASK & 1 = 0 indicates that they were enabled - if primask & 1 == 0 { + if primask.is_active() { enable(); } diff --git a/src/lib.rs b/src/lib.rs index 5e75d533..322d20c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,11 +9,11 @@ //! - Miscellaneous assembly instructions like `bkpt` #![cfg_attr(feature = "semihosting", feature(macro_reexport))] +#![cfg_attr(target_arch = "arm", feature(core_intrinsics))] #![deny(missing_docs)] #![deny(warnings)] #![feature(asm)] #![feature(const_fn)] -#![feature(core_intrinsics)] #![feature(naked_functions)] #![no_std] diff --git a/src/register.rs b/src/register.rs deleted file mode 100644 index 8ab9a8df..00000000 --- a/src/register.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! Processor core registers -//! -//! The following registers can only be accessed in PRIVILEGED mode: -//! -//! - MSP -//! - IPSR -//! - EPSR -//! - PRIMASK -//! - FAULTMASK -//! - BASEPRI -//! - CONTROL -//! -//! The rest of registers (see list below) can be accessed in either, PRIVILEGED or UNPRIVILEGED, -//! mode. -//! -//! - PSP -//! - LR -//! - PC -//! - APSR -//! -//! # 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 -//! -//! - Cortex-M* Devices Generic User Guide - Section 2.1.3 Core registers - -// NOTE all the functions here are `always(inline)` to prevent a function call which may change the -// contents of the core registers. - -macro_rules! sr { - ($name:ident) => { - /// Reads the special register - #[inline(always)] - pub unsafe fn read() -> u32 { - let r: u32; - match () { - #[cfg(target_arch = "arm")] - () => asm!(concat!("mrs ", "$0,", stringify!($name)) : "=r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - () => r = 0, - } - r - } - }; -} - -macro_rules! srw { - (#[$attr:meta] $name:ident) => { - #[$attr] - pub mod $name { - sr!($name); - - /// Writes to the special register - #[inline(always)] - pub unsafe fn write(r: u32) { - match r { - #[cfg(target_arch = "arm")] - _ => asm!(concat!("msr ", stringify!($name), ",$0") :: "r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - _ => {}, - } - } - } - }; -} - -macro_rules! sro { - (#[$attr:meta] $name:ident) => { - #[$attr] - pub mod $name { - sr!($name); - } - } -} - -macro_rules! rw { - (#[$attr:meta] $name:ident : $r:ident) => { - #[$attr] - pub mod $name { - /// Reads the special register - #[inline(always)] - pub unsafe fn read() -> u32 { - let r: u32; - match () { - #[cfg(target_arch = "arm")] - () => asm!(concat!("mov ", "$0,", stringify!($r)) : "=r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - () => r = 0, - } - r - } - - /// Writes to the special register - #[inline(always)] - pub unsafe fn write(r: u32) { - match r { - #[cfg(target_arch = "arm")] - _ => asm!(concat!("mov ", stringify!($r), ",$0") :: "r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - _ => {} - } - } - } - } -} - -srw!(#[doc = "Main Stack Pointer"] msp); -srw!(#[doc = "Process Stack Pointer"] psp); -rw!(#[doc = "Link Register"] lr: r14); -rw!(#[doc = "Program Counter"] pc: r15); -srw!(#[doc = "Application Program Status Register"] apsr); -sro!(#[doc = "Interrupt Program Status Register"] ipsr); -sro!(#[doc = "Exception Program Status Register"] epsr); -srw!(#[doc = "Priority Mask Register"] primask); -srw!(#[doc = "Fault Mask Register"] faultmask); -srw!(#[doc = "Base Priority Mask Register"] basepri); -srw!(#[doc = "Base Priority Mask Register"] basepri_max); -srw!(#[doc = "Control Register"] control); diff --git a/src/register/apsr.rs b/src/register/apsr.rs new file mode 100644 index 00000000..338c6847 --- /dev/null +++ b/src/register/apsr.rs @@ -0,0 +1,52 @@ +//! Application Program Status Register + +/// Application Program Status Register +pub struct Apsr { + bits: u32, +} + +impl Apsr { + /// Returns the contents of the register as raw bits + pub fn bits(&self) -> u32 { + self.bits + } + + /// DSP overflow and saturation flag + pub fn q(&self) -> bool { + self.bits & (1 << 27) == (1 << 27) + } + + /// Overflow flag + pub fn v(&self) -> bool { + self.bits & (1 << 28) == (1 << 28) + } + + /// Carry or borrow flag + pub fn c(&self) -> bool { + self.bits & (1 << 29) == (1 << 29) + } + + /// Zero flag + pub fn z(&self) -> bool { + self.bits & (1 << 30) == (1 << 30) + } + + /// Negative flag + pub fn n(&self) -> bool { + self.bits & (1 << 31) == (1 << 31) + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Apsr { + let r: u32; + unsafe { + asm!("mrs $0, APSR" + : "=r"(r) + : + : + : "volatile"); + } + Apsr { bits: r } +} diff --git a/src/register/basepri.rs b/src/register/basepri.rs new file mode 100644 index 00000000..e9164c1a --- /dev/null +++ b/src/register/basepri.rs @@ -0,0 +1,27 @@ +//! Base Priority Mask Register + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u8 { + let r: u32; + unsafe { + asm!("mrs $0, BASEPRI" + : "=r"(r) + : + : + : "volatile"); + } + r as u8 +} + +/// Writes to the CPU register +#[inline(always)] +pub fn write(basepri: u8) { + unsafe { + asm!("msr BASEPRI, $0" + : + : "r"(basepri) + : + : "volatile"); + } +} diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs new file mode 100644 index 00000000..d492015b --- /dev/null +++ b/src/register/basepri_max.rs @@ -0,0 +1,16 @@ +//! Base Priority Mask Register (conditional write) + +/// Writes to BASEPRI *if* +/// +/// - `basepri != 0` AND `basepri::read() == 0`, OR +/// - `basepri != 0` AND `basepri < basepri::read()` +#[inline(always)] +pub fn write(basepri: u8) { + unsafe { + asm!("msr BASEPRI_MAX, $0" + : + : "r"(basepri as u32) + : + : "volatile"); + } +} diff --git a/src/register/control.rs b/src/register/control.rs new file mode 100644 index 00000000..62ebff69 --- /dev/null +++ b/src/register/control.rs @@ -0,0 +1,117 @@ +//! Control register + +/// Control register +pub struct Control { + bits: u32, +} + +impl Control { + /// Returns the contents of the register as raw bits + pub fn bits(&self) -> u32 { + self.bits + } + + /// Thread mode privilege level + pub fn npriv(&self) -> Npriv { + if self.bits & (1 << 0) == (1 << 0) { + Npriv::Unprivileged + } else { + Npriv::Privileged + } + } + + /// Currently active stack pointer + pub fn spsel(&self) -> Spsel { + if self.bits & (1 << 1) == (1 << 1) { + Spsel::Psp + } else { + Spsel::Msp + } + } + + /// Whether context floating-point is currently active + pub fn fpca(&self) -> Fpca { + if self.bits & (1 << 2) == (1 << 2) { + Fpca::Active + } else { + Fpca::NotActive + } + } +} + +/// Thread mode privilege level +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Npriv { + /// Privileged + Privileged, + /// Unprivileged + Unprivileged, +} + +impl Npriv { + /// Is in privileged thread mode? + pub fn is_privileged(&self) -> bool { + *self == Npriv::Privileged + } + + /// Is in unprivileged thread mode? + pub fn is_unprivileged(&self) -> bool { + *self == Npriv::Unprivileged + } +} + +/// Currently active stack pointer +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Spsel { + /// MSP is the current stack pointer + Msp, + /// PSP is the current stack pointer + Psp, +} + +impl Spsel { + /// Is MSP the current stack pointer? + pub fn is_msp(&self) -> bool { + *self == Spsel::Msp + } + + /// Is PSP the current stack pointer? + pub fn is_psp(&self) -> bool { + *self == Spsel::Psp + } +} + +/// Whether context floating-point is currently active +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Fpca { + /// Floating-point context active. + Active, + /// No floating-point context active + NotActive, +} + +impl Fpca { + /// Is a floating-point context active? + pub fn is_active(&self) -> bool { + *self == Fpca::Active + } + + /// Is a floating-point context not active? + pub fn is_not_active(&self) -> bool { + *self == Fpca::NotActive + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Control { + let r: u32; + unsafe { + asm!("mrs $0, CONTROL" + : "=r"(r) + : + : + : "volatile"); + } + Control { bits: r } +} diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs new file mode 100644 index 00000000..5a06b37e --- /dev/null +++ b/src/register/faultmask.rs @@ -0,0 +1,40 @@ +//! Fault Mask Register + +/// All exceptions are ... +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Faultmask { + /// Active + Active, + /// Inactive, expect for NMI + Inactive, +} + +impl Faultmask { + /// All exceptions are active + pub fn is_active(&self) -> bool { + *self == Faultmask::Active + } + + /// All exceptions, except for NMI, are inactive + pub fn is_inactive(&self) -> bool { + *self == Faultmask::Inactive + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Faultmask { + let r: u32; + unsafe { + asm!("mrs $0, FAULTMASK" + : "=r"(r) + : + : + : "volatile"); + } + if r & (1 << 0) == (1 << 0) { + Faultmask::Inactive + } else { + Faultmask::Active + } +} diff --git a/src/register/lr.rs b/src/register/lr.rs new file mode 100644 index 00000000..fecfecbe --- /dev/null +++ b/src/register/lr.rs @@ -0,0 +1,25 @@ +//! Link register + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r: u32; + unsafe { + asm!("mov $0,R14" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("mov R14,$0" + : + : "r"(bits) + : + : "volatile"); +} diff --git a/src/register/mod.rs b/src/register/mod.rs new file mode 100644 index 00000000..0ec57204 --- /dev/null +++ b/src/register/mod.rs @@ -0,0 +1,41 @@ +//! Processor core registers +//! +//! The following registers can only be accessed in PRIVILEGED mode: +//! +//! - BASEPRI +//! - CONTROL +//! - FAULTMASK +//! - MSP +//! - PRIMASK +//! +//! The rest of registers (see list below) can be accessed in either, PRIVILEGED or UNPRIVILEGED, +//! mode. +//! +//! - APSR +//! - LR +//! - PC +//! - PSP +//! +//! The following registers are NOT available on ARMv6-M devices +//! (`thumbv6m-none-eabi`): +//! +//! - BASEPRI +//! - FAULTMASK +//! +//! # References +//! +//! - Cortex-M* Devices Generic User Guide - Section 2.1.3 Core registers + +pub mod apsr; +#[cfg(not(thumbv6m))] +pub mod basepri; +#[cfg(not(thumbv6m))] +pub mod basepri_max; +pub mod control; +#[cfg(not(thumbv6m))] +pub mod faultmask; +pub mod lr; +pub mod msp; +pub mod pc; +pub mod primask; +pub mod psp; diff --git a/src/register/msp.rs b/src/register/msp.rs new file mode 100644 index 00000000..ebea6ed5 --- /dev/null +++ b/src/register/msp.rs @@ -0,0 +1,25 @@ +//! Main Stack Pointer + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r; + unsafe { + asm!("mrs $0,MSP" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("msr MSP,$0" + : + : "r"(bits) + : + : "volatile"); +} diff --git a/src/register/pc.rs b/src/register/pc.rs new file mode 100644 index 00000000..3fec1aeb --- /dev/null +++ b/src/register/pc.rs @@ -0,0 +1,25 @@ +//! Program counter + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r; + unsafe { + asm!("mov $0,R15" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("mov R15,$0" + : + : "r"(bits) + : + : "volatile"); +} diff --git a/src/register/primask.rs b/src/register/primask.rs new file mode 100644 index 00000000..1e24b730 --- /dev/null +++ b/src/register/primask.rs @@ -0,0 +1,40 @@ +//! Priority mask register + +/// All exceptions with configurable priority are ... +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Primask { + /// Active + Active, + /// Inactive + Inactive, +} + +impl Primask { + /// All exceptions with configurable priority are active + pub fn is_active(&self) -> bool { + *self == Primask::Active + } + + /// All exceptions with configurable priority are inactive + pub fn is_inactive(&self) -> bool { + *self == Primask::Inactive + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Primask { + let r: u32; + unsafe { + asm!("mrs $0, PRIMASK" + : "=r"(r) + : + : + : "volatile"); + } + if r & (1 << 0) == (1 << 0) { + Primask::Inactive + } else { + Primask::Active + } +} diff --git a/src/register/psp.rs b/src/register/psp.rs new file mode 100644 index 00000000..ecd6f9cb --- /dev/null +++ b/src/register/psp.rs @@ -0,0 +1,25 @@ +//! Process Stack Pointer + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r; + unsafe { + asm!("mrs $0,PSP" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("msr PSP,$0" + : + : "r"(bits) + : + : "volatile"); +} From fe0461f356ea25e27ca59d2c59094113bb167604 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 4 Mar 2017 20:57:48 -0500 Subject: [PATCH 08/31] fix cfg: thumbv6m -> armv6m --- src/peripheral/nvic.rs | 4 ++-- src/register/mod.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 054a74e1..26a9524d 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -3,10 +3,10 @@ use interrupt::Nr; use volatile_register::{RO, RW}; -#[cfg(thumbv6m)] +#[cfg(armv6m)] const PRIORITY_BITS: u8 = 2; -#[cfg(not(thumbv6m))] +#[cfg(not(armv6m))] const PRIORITY_BITS: u8 = 4; /// Registers diff --git a/src/register/mod.rs b/src/register/mod.rs index 0ec57204..e3321a67 100644 --- a/src/register/mod.rs +++ b/src/register/mod.rs @@ -27,12 +27,12 @@ //! - Cortex-M* Devices Generic User Guide - Section 2.1.3 Core registers pub mod apsr; -#[cfg(not(thumbv6m))] +#[cfg(not(armv6m))] pub mod basepri; -#[cfg(not(thumbv6m))] +#[cfg(not(armv6m))] pub mod basepri_max; pub mod control; -#[cfg(not(thumbv6m))] +#[cfg(not(armv6m))] pub mod faultmask; pub mod lr; pub mod msp; From a8ae7c216c968a46cdab1c2ad2a5eaab794b6d25 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 4 Mar 2017 23:06:40 -0500 Subject: [PATCH 09/31] remove an unnecessary cast --- src/register/basepri_max.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs index d492015b..e7594e61 100644 --- a/src/register/basepri_max.rs +++ b/src/register/basepri_max.rs @@ -9,7 +9,7 @@ pub fn write(basepri: u8) { unsafe { asm!("msr BASEPRI_MAX, $0" : - : "r"(basepri as u32) + : "r"(basepri) : : "volatile"); } From 9d3f3f323f3b7543d0b49e773aea2c68e535ec83 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 4 Mar 2017 23:21:22 -0500 Subject: [PATCH 10/31] nvic: don't shift the priority note this in the documentation --- src/peripheral/nvic.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 26a9524d..8c12698c 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -3,12 +3,6 @@ use interrupt::Nr; use volatile_register::{RO, RW}; -#[cfg(armv6m)] -const PRIORITY_BITS: u8 = 2; - -#[cfg(not(armv6m))] -const PRIORITY_BITS: u8 = 4; - /// Registers #[repr(C)] pub struct Registers { @@ -59,13 +53,17 @@ impl Registers { self.iser[usize::from(nr / 32)].write(1 << (nr % 32)); } - /// Gets the priority of `interrupt` + /// 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(&mut self, interrupt: I) -> u8 where I: Nr { let nr = interrupt.nr(); - self.ipr[usize::from(nr)].read() >> (8 - PRIORITY_BITS) + self.ipr[usize::from(nr)].read() } /// Is `interrupt` active or pre-empted and stacked @@ -107,13 +105,15 @@ impl Registers { self.ispr[usize::from(nr / 32)].write(1 << (nr % 32)); } - /// Sets the priority of `interrupt` to `prio` + /// Sets the "priority" of `interrupt` to `prio` + /// + /// NOTE See `get_priority` method for an explanation of how NVIC priorities + /// work. pub fn set_priority(&mut self, interrupt: I, prio: u8) where I: Nr { let nr = interrupt.nr(); - self.ipr[usize::from(nr)].write((prio & ((1 << PRIORITY_BITS) - 1)) << - (8 - PRIORITY_BITS)); + self.ipr[usize::from(nr)].write(prio); } } From c3a35c1b6cea81aa71e8832bca79ccafa492be02 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 7 Mar 2017 22:56:06 -0500 Subject: [PATCH 11/31] revamp for memory safety --- CHANGELOG.md | 49 +++- Cargo.toml | 4 +- src/ctxt.rs | 34 +++ src/exception.rs | 93 ++++++-- src/interrupt.rs | 9 +- src/lib.rs | 86 +------ src/peripheral/cpuid.rs | 30 --- src/peripheral/dcb.rs | 16 -- src/peripheral/dwt.rs | 43 ---- src/peripheral/fpb.rs | 19 -- src/peripheral/fpu.rs | 17 -- src/peripheral/itm.rs | 54 ----- src/peripheral/mod.rs | 494 ++++++++++++++++++++++++++++++++-------- src/peripheral/mpu.rs | 30 --- src/peripheral/nvic.rs | 119 ---------- src/peripheral/scb.rs | 37 --- src/peripheral/syst.rs | 16 -- src/peripheral/test.rs | 243 ++++++++++---------- src/peripheral/tpiu.rs | 26 --- 19 files changed, 686 insertions(+), 733 deletions(-) create mode 100644 src/ctxt.rs delete mode 100644 src/peripheral/cpuid.rs delete mode 100644 src/peripheral/dcb.rs delete mode 100644 src/peripheral/dwt.rs delete mode 100644 src/peripheral/fpb.rs delete mode 100644 src/peripheral/fpu.rs delete mode 100644 src/peripheral/itm.rs delete mode 100644 src/peripheral/mpu.rs delete mode 100644 src/peripheral/nvic.rs delete mode 100644 src/peripheral/scb.rs delete mode 100644 src/peripheral/syst.rs delete mode 100644 src/peripheral/tpiu.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ea5af0c6..95f28c78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Semihosting functionality is now exposed via the "semihosting" Cargo feature. + +- `exception::Handlers` struct that represent the section of the vector table + that contains the exception handlers. + +- A default exception handler + +- A high level API for the NVIC peripheral. + +- Execution context primitives: execution context `Local` data and `Token`s to + identify execution contexts. + +### Changed + +- [breaking-change] `StackFrame` has been renamed to `StackedRegisters` and + moved into the `exceptions` module. + +- [breaking-change] Core peripherals can now be modified via a `&-` reference + and are no longer `Sync`. + +- [breaking-change] `interrupt::free`'s closure now includes a critical section + token, `CsCtxt`. + +- [breaking-change] the core register API has been revamped for type safety. + +- The safety of assembly wrappers like `wfi` and `interrupt::free` has been + reviewed. In many cases, the functions are no longer unsafe. + +### Removed + +- `vector_table` and its associated `struct`, `VectorTable`. It's not a good + idea to give people a simple way to call the exception handlers. + ## [v0.1.6] - 2017-01-22 ### Added @@ -60,10 +95,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Functions to get the vector table - Wrappers over miscellaneous instructions like `bkpt` -[Unreleased]: https://github.com/japaric/rustc-cfg/compare/v0.1.6...HEAD -[v0.1.6]: https://github.com/japaric/rustc-cfg/compare/v0.1.5...v0.1.6 -[v0.1.5]: https://github.com/japaric/rustc-cfg/compare/v0.1.4...v0.1.5 -[v0.1.4]: https://github.com/japaric/rustc-cfg/compare/v0.1.3...v0.1.4 -[v0.1.3]: https://github.com/japaric/rustc-cfg/compare/v0.1.2...v0.1.3 -[v0.1.2]: https://github.com/japaric/rustc-cfg/compare/v0.1.1...v0.1.2 -[v0.1.1]: https://github.com/japaric/rustc-cfg/compare/v0.1.0...v0.1.1 +[Unreleased]: https://github.com/japaric/cortex-m/compare/v0.1.6...HEAD +[v0.1.6]: https://github.com/japaric/cortex-m/compare/v0.1.5...v0.1.6 +[v0.1.5]: https://github.com/japaric/cortex-m/compare/v0.1.4...v0.1.5 +[v0.1.4]: https://github.com/japaric/cortex-m/compare/v0.1.3...v0.1.4 +[v0.1.3]: https://github.com/japaric/cortex-m/compare/v0.1.2...v0.1.3 +[v0.1.2]: https://github.com/japaric/cortex-m/compare/v0.1.1...v0.1.2 +[v0.1.1]: https://github.com/japaric/cortex-m/compare/v0.1.0...v0.1.1 diff --git a/Cargo.toml b/Cargo.toml index e0705490..4f298580 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,10 @@ keywords = ["arm", "cortex-m", "register", "peripheral"] license = "MIT OR Apache-2.0" name = "cortex-m" repository = "https://github.com/japaric/cortex-m" -version = "0.1.6" +version = "0.2.0" [dependencies] -volatile-register = "0.1.0" +volatile-register = "0.2.0" [dependencies.cortex-m-semihosting] optional = true diff --git a/src/ctxt.rs b/src/ctxt.rs new file mode 100644 index 00000000..7cd8a740 --- /dev/null +++ b/src/ctxt.rs @@ -0,0 +1,34 @@ +//! Execution context + +use core::marker::PhantomData; +use core::cell::UnsafeCell; + +/// Data local to an execution context +pub struct Local + where Ctxt: Token +{ + _ctxt: PhantomData, + data: UnsafeCell, +} + +impl Local + where Ctxt: Token +{ + /// Initializes the local data + pub const fn new(value: T) -> Self { + Local { + _ctxt: PhantomData, + data: UnsafeCell::new(value), + } + } + + /// Acquires a reference to the local data + pub fn borrow<'a>(&'static self, _ctxt: &'a Ctxt) -> &'a T { + unsafe { &*self.data.get() } + } +} + +unsafe impl Sync for Local where Ctxt: Token {} + +/// A unique token that identifies an execution context +pub unsafe trait Token {} diff --git a/src/exception.rs b/src/exception.rs index cd383663..b944d6cb 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,8 +1,7 @@ //! Exceptions -use {Handler, Reserved}; -#[cfg(target_arch = "arm")] -use StackFrame; +use ctxt::Token; +use Reserved; /// Kind of exception #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -35,7 +34,7 @@ pub enum Exception { impl Exception { /// Returns the kind of exception that's currently being serviced pub fn current() -> Exception { - match ::peripheral::scb().icsr.read() as u8 { + match unsafe { (*::peripheral::SCB.get()).icsr.read() } as u8 { 0 => Exception::ThreadMode, 2 => Exception::Nmi, 3 => Exception::HardFault, @@ -55,27 +54,65 @@ impl Exception { #[repr(C)] pub struct Handlers { /// Non-maskable interrupt - pub nmi: Handler, + pub nmi: unsafe extern "C" fn(&NmiCtxt), /// All class of fault - pub hard_fault: Handler, + pub hard_fault: unsafe extern "C" fn(&HardFaultCtxt), /// Memory management - pub mem_manage: Handler, + pub mem_manage: unsafe extern "C" fn(&MemManageCtxt), /// Pre-fetch fault, memory access fault - pub bus_fault: Handler, + pub bus_fault: unsafe extern "C" fn(&BusFaultCtxt), /// Undefined instruction or illegal state - pub usage_fault: Handler, + pub usage_fault: unsafe extern "C" fn(&UsageFaultCtxt), /// Reserved spots in the vector table pub _reserved0: [Reserved; 4], /// System service call via SWI instruction - pub svcall: Handler, + pub svcall: unsafe extern "C" fn(&SvcallCtxt), /// Reserved spots in the vector table pub _reserved1: [Reserved; 2], /// Pendable request for system service - pub pendsv: Handler, + pub pendsv: unsafe extern "C" fn(&PendsvCtxt), /// System tick timer - pub sys_tick: Handler, + pub sys_tick: unsafe extern "C" fn (&SysTickCtxt), } +/// Identifies the Nmi exception +pub struct NmiCtxt { _0: () } + +/// Identifies the HardFault exception +pub struct HardFaultCtxt { _0: () } + +/// Identifies the MemManage exception +pub struct MemManageCtxt { _0: () } + +/// Identifies the BusFault exception +pub struct BusFaultCtxt { _0: () } + +/// Identifies the UsageFault exception +pub struct UsageFaultCtxt { _0: () } + +/// Identifies the Svcall exception +pub struct SvcallCtxt { _0: () } + +/// Identifies the Pendsv exception +pub struct PendsvCtxt { _0: () } + +/// Identifies the Systick exception +pub struct SysTickCtxt { _0: () } + +unsafe impl Token for NmiCtxt {} + +unsafe impl Token for HardFaultCtxt {} + +unsafe impl Token for MemManageCtxt {} + +unsafe impl Token for BusFaultCtxt {} + +unsafe impl Token for SvcallCtxt {} + +unsafe impl Token for PendsvCtxt {} + +unsafe impl Token for SysTickCtxt {} + /// Default exception handlers pub const DEFAULT_HANDLERS: Handlers = Handlers { _reserved0: [Reserved::Vector; 4], @@ -95,15 +132,16 @@ pub const DEFAULT_HANDLERS: Handlers = Handlers { /// This handler triggers a breakpoint (`bkpt`) and gives you access, within a /// GDB session, to the stack frame (`_sf`) where the exception occurred. // This needs asm!, #[naked] and unreachable() to avoid modifying the stack -// pointer (MSP), that way it points to the previous stack frame +// pointer (MSP), that way it points to the stacked registers #[naked] -pub unsafe extern "C" fn default_handler() { +pub unsafe extern "C" fn default_handler(_token: &T) +{ // This is the actual exception handler. `_sf` is a pointer to the previous // stack frame #[cfg(target_arch = "arm")] - extern "C" fn handler(_sf: &StackFrame) -> ! { + extern "C" fn handler(_sr: &StackedRegisters) -> ! { #[cfg(feature = "semihosting")] - hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sf.pc); + hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sr.pc); unsafe { bkpt!(); @@ -120,7 +158,7 @@ pub unsafe extern "C" fn default_handler() { ldr r1, [r0, #20] b $0" : - : "i"(handler as extern "C" fn(&StackFrame) -> !) + : "i"(handler as extern "C" fn(&StackedRegisters) -> !) : : "volatile"); @@ -130,3 +168,24 @@ pub unsafe extern "C" fn default_handler() { () => {} } } + +/// Registers stacked during an exception +#[repr(C)] +pub struct StackedRegisters { + /// (General purpose) Register 0 + pub r0: u32, + /// (General purpose) Register 1 + pub r1: u32, + /// (General purpose) Register 2 + pub r2: u32, + /// (General purpose) Register 3 + pub r3: u32, + /// (General purpose) Register 12 + pub r12: u32, + /// Linker Register + pub lr: u32, + /// Program Counter + pub pc: u32, + /// Program Status Register + pub xpsr: u32, +} diff --git a/src/interrupt.rs b/src/interrupt.rs index 2552892f..95829ca8 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -72,21 +72,22 @@ pub fn enable() { /// Critical section token /// /// Indicates that you are executing code within a critical section -pub struct CsToken { - _private: (), +pub struct CsCtxt { + _0: (), } /// Execute closure `f` in an interrupt-free context. +/// /// This as also known as a "critical section". pub fn free(f: F) -> R - where F: FnOnce(&CsToken) -> R + where F: FnOnce(&CsCtxt) -> R { let primask = ::register::primask::read(); // disable interrupts disable(); - let r = f(&CsToken { _private: () }); + let r = f(&CsCtxt { _0: () }); // If the interrupts were active before our `disable` call, then re-enable // them. Otherwise, keep them disabled diff --git a/src/lib.rs b/src/lib.rs index 322d20c0..59428ba6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ //! Low level access to Cortex-M processors //! -//! This crate provides access to: +//! This crate provides: //! -//! - Core peripherals like NVIC, SCB and SysTick. -//! - Core registers like CONTROL, MSP and PSR. +//! - Access to core peripherals like NVIC, SCB and SysTick. +//! - Access to core registers like CONTROL, MSP and PSR. //! - Interrupt manipulation mechanisms //! - Data structures like the vector table -//! - Miscellaneous assembly instructions like `bkpt` +//! - Safe wrappers around assembly instructions like `bkpt` #![cfg_attr(feature = "semihosting", feature(macro_reexport))] #![cfg_attr(target_arch = "arm", feature(core_intrinsics))] @@ -26,67 +26,12 @@ mod macros; #[macro_use] pub mod asm; +pub mod ctxt; pub mod exception; pub mod interrupt; pub mod peripheral; pub mod register; -/// Stack frame -#[repr(C)] -pub struct StackFrame { - /// (General purpose) Register 0 - pub r0: u32, - /// (General purpose) Register 1 - pub r1: u32, - /// (General purpose) Register 2 - pub r2: u32, - /// (General purpose) Register 3 - pub r3: u32, - /// (General purpose) Register 12 - pub r12: u32, - /// Linker Register - pub lr: u32, - /// Program Counter - pub pc: u32, - /// Program Status Register - pub xpsr: u32, -} - -/// Vector Table -/// -/// # References -/// -/// - ARMv7-M Architecture Reference Manual (issue E.b) - Section B1.5 - ARMv7-M exception model -#[repr(C)] -pub struct VectorTable { - /// Reset value of the Main Stack Pointer (MSP) - pub sp_main: &'static (), - /// Reset - pub reset: extern "C" fn() -> !, - /// Non Maskable Interrupt - pub nmi: Option, - /// Hard Fault - pub hard_fault: Option, - /// Memory Management - pub mem_manage: Option, - /// Bus Fault - pub bus_fault: Option, - /// Usage Fault - pub usage_fault: Option, - reserved0: [u32; 4], - /// Supervisor Call - pub svcall: Option, - /// Debug Monitor - pub debug_monitor: Option, - reserved1: u32, - /// PendSV - pub pendsv: Option, - /// SysTick - pub sys_tick: Option, - /// Interrupts. An IMPLEMENTATION DEFINED number of them. - pub interrupts: [Option; 0], -} - /// A reserved spot in the vector table #[derive(Clone, Copy)] #[repr(u32)] @@ -94,24 +39,3 @@ pub enum Reserved { /// Reserved Vector = 0, } - -/// Exception/Interrupt Handler -pub type Handler = unsafe extern "C" fn(); - -/// Returns the vector table -pub fn vector_table() -> &'static VectorTable { - unsafe { deref(peripheral::scb().vtor.read() as usize) } -} - -#[cfg(test)] -fn address(r: &T) -> usize { - r as *const T as usize -} - -unsafe fn deref(a: usize) -> &'static T { - &*(a as *const T) -} - -unsafe fn deref_mut(a: usize) -> &'static mut T { - &mut *(a as *mut T) -} diff --git a/src/peripheral/cpuid.rs b/src/peripheral/cpuid.rs deleted file mode 100644 index 0dc140f7..00000000 --- a/src/peripheral/cpuid.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! CPUID - -use volatile_register::RO; - -/// Registers -#[repr(C)] -pub struct Registers { - /// CPUID base - pub base: RO, - reserved0: [u32; 15], - /// Processor Feature - pub pfr: [RO; 2], - /// Debug Feature - pub dfr: RO, - /// Auxiliary Feature - pub afr: RO, - /// Memory Model Feature - pub mmfr: [RO; 4], - /// Instruction Set Attribute - pub isar: [RO; 5], - reserved1: u32, - /// Cache Level ID - pub clidr: RO, - /// Cache Type - pub ctr: RO, - /// Cache Size ID - pub ccsidr: RO, - /// Cache Size Selection - pub csselr: RO, -} diff --git a/src/peripheral/dcb.rs b/src/peripheral/dcb.rs deleted file mode 100644 index 93a056b0..00000000 --- a/src/peripheral/dcb.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! Debug Control Block - -use volatile_register::{RW, WO}; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Debug Halting Control and Status - pub dhcsr: RW, - /// Debug Core Register Selector - pub dcrsr: WO, - /// Debug Core Register Data - pub dcrdr: RW, - /// Debug Exception and Monitor Control - pub demcr: RW, -} diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs deleted file mode 100644 index ecd214ea..00000000 --- a/src/peripheral/dwt.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Data Watchpoint and Trace unit - -use volatile_register::{RO, RW, WO}; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Control - pub ctrl: RW, - /// Cycle Count - pub cyccnt: RW, - /// CPI Count - pub cpicnt: RW, - /// Exception Overhead Count - pub exccnt: RW, - /// Sleep Count - pub sleepcnt: RW, - /// LSU Count - pub lsucnt: RW, - /// Folded-instruction Count - pub foldcnt: RW, - /// Program Counter Sample - pub pcsr: RO, - /// Comparators - pub c: [Comparator; 16], - reserved: [u32; 932], - /// Lock Access - pub lar: WO, - /// Lock Status - pub lsr: RO, -} - -/// Comparator -#[repr(C)] -pub struct Comparator { - /// Comparator - pub comp: RW, - /// Comparator Mask - pub mask: RW, - /// Comparator Function - pub function: RW, - reserved: u32, -} diff --git a/src/peripheral/fpb.rs b/src/peripheral/fpb.rs deleted file mode 100644 index 6aa6fbb7..00000000 --- a/src/peripheral/fpb.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Flash Patch and Breakpoint unit - -use volatile_register::{RO, RW, WO}; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Control - pub ctrl: RW, - /// Remap - pub remap: RW, - /// Comparator - pub comp: [RW; 127], - reserved: [u32; 875], - /// Lock Access - pub lar: WO, - /// Lock Status - pub lsr: RO, -} diff --git a/src/peripheral/fpu.rs b/src/peripheral/fpu.rs deleted file mode 100644 index 5bbf3521..00000000 --- a/src/peripheral/fpu.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Floating Point Unit - -use volatile_register::{RO, RW}; - -/// Registers -#[repr(C)] -pub struct Registers { - reserved: u32, - /// Floating Point Context Control - pub fpccr: RW, - /// Floating Point Context Address - pub fpcar: RW, - /// Floating Point Default Status Control - pub fpdscr: RW, - /// Media and FP Feature - pub mvfr: [RO; 3], -} diff --git a/src/peripheral/itm.rs b/src/peripheral/itm.rs deleted file mode 100644 index b56d1b5f..00000000 --- a/src/peripheral/itm.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Instrumentation Trace Macrocell - -use volatile_register::{RO, RW, WO}; - -use core::cell::UnsafeCell; -use core::ptr; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Stimulus Port - pub stim: [Stim; 256], - reserved0: [u32; 640], - /// Trace Enable - pub ter: [RW; 8], - reserved1: [u32; 8], - /// Trace Privilege - pub tpr: RW, - reserved2: [u32; 15], - /// Trace Control - pub tcr: RW, - reserved3: [u32; 75], - /// Lock Access - pub lar: WO, - /// Lock Status - pub lsr: RO, -} - -/// Stimulus Port -pub struct Stim { - register: UnsafeCell, -} - -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) } - } - - /// 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) } - } - - /// 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 } - } -} diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index c0cb2998..c6e8faa1 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 = unsafe { Peripheral::new(0xE000_ED00) }; + +/// Debug Control Block +pub const DCB: Peripheral = unsafe { Peripheral::new(0xE000_EDF0) }; + +/// Data Watchpoint and Trace unit +pub const DWT: Peripheral = unsafe { Peripheral::new(0xE000_1000) }; + +/// Flash Patch and Breakpoint unit +pub const FPB: Peripheral = unsafe { Peripheral::new(0xE000_2000) }; + +/// Floating Point Unit +pub const FPU: Peripheral = unsafe { Peripheral::new(0xE000_EF30) }; + +/// Instrumentation Trace Macrocell +pub const ITM: Peripheral = unsafe { Peripheral::new(0xE000_0000) }; + +/// Memory Protection Unit +pub const MPU: Peripheral = unsafe { Peripheral::new(0xE000_ED90) }; + +/// Nested Vector Interrupt Controller +pub const NVIC: Peripheral = unsafe { Peripheral::new(0xE000_E100) }; + +/// System Control Block +pub const SCB: Peripheral = unsafe { Peripheral::new(0xE000_ED04) }; + +/// SysTick: System Timer +pub const SYST: Peripheral = unsafe { Peripheral::new(0xE000_E010) }; + +/// Trace Port Interface Unit; +pub const TPIU: Peripheral = 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 + where T: 'static +{ + address: usize, + _marker: PhantomData<&'static mut T>, } -/// `&dcb::Registers` -pub fn dcb() -> &'static dcb::Registers { - unsafe { ::deref(DCB) } -} +impl Peripheral { + /// 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, + reserved0: [u32; 15], + /// Processor Feature + pub pfr: [RO; 2], + /// Debug Feature + pub dfr: RO, + /// Auxiliary Feature + pub afr: RO, + /// Memory Model Feature + pub mmfr: [RO; 4], + /// Instruction Set Attribute + pub isar: [RO; 5], + reserved1: u32, + /// Cache Level ID + pub clidr: RO, + /// Cache Type + pub ctr: RO, + /// Cache Size ID + pub ccsidr: RO, + /// Cache Size Selection + pub csselr: RO, } -/// `&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, + /// Debug Core Register Selector + pub dcrsr: WO, + /// Debug Core Register Data + pub dcrdr: RW, + /// Debug Exception and Monitor Control + pub demcr: RW, } -/// `&fpb::Registers` -pub fn fpb() -> &'static fpb::Registers { - unsafe { ::deref(FPB) } +/// DWT register block +#[repr(C)] +pub struct Dwt { + /// Control + pub ctrl: RW, + /// Cycle Count + pub cyccnt: RW, + /// CPI Count + pub cpicnt: RW, + /// Exception Overhead Count + pub exccnt: RW, + /// Sleep Count + pub sleepcnt: RW, + /// LSU Count + pub lsucnt: RW, + /// Folded-instruction Count + pub foldcnt: RW, + /// Program Counter Sample + pub pcsr: RO, + /// Comparators + pub c: [Comparator; 16], + reserved: [u32; 932], + /// Lock Access + pub lar: WO, + /// Lock Status + pub lsr: RO, } -/// `&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, + /// Comparator Mask + pub mask: RW, + /// Comparator Function + pub function: RW, + 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, + /// Remap + pub remap: RW, + /// Comparator + pub comp: [RW; 127], + reserved: [u32; 875], + /// Lock Access + pub lar: WO, + /// Lock Status + pub lsr: RO, } -/// `&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, + /// Floating Point Context Address + pub fpcar: RW, + /// Floating Point Default Status Control + pub fpdscr: RW, + /// Media and FP Feature + pub mvfr: [RO; 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; 8], + reserved1: [u32; 8], + /// Trace Privilege + pub tpr: RW, + reserved2: [u32; 15], + /// Trace Control + pub tcr: RW, + reserved3: [u32; 75], + /// Lock Access + pub lar: WO, + /// Lock Status + pub lsr: RO, } -/// `&mut itm::Registers` -pub unsafe fn itm_mut() -> &'static mut itm::Registers { - ::deref_mut(ITM) +/// Stimulus Port +pub struct Stim { + register: UnsafeCell, } -/// `&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, + /// Control + pub ctrl: RW, + /// Region Number + pub rnr: RW, + /// Region Base Address + pub rbar: RW, + /// Region Attribute and Size + pub rasr: RW, + /// Alias 1 of RBAR + pub rbar_a1: RW, + /// Alias 1 of RSAR + pub rsar_a1: RW, + /// Alias 2 of RBAR + pub rbar_a2: RW, + /// Alias 2 of RSAR + pub rsar_a2: RW, + /// Alias 3 of RBAR + pub rbar_a3: RW, + /// Alias 3 of RSAR + pub rsar_a3: RW, } -/// `&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; 8], + reserved0: [u32; 24], + /// Interrupt Clear-Enable + pub icer: [RW; 8], + reserved1: [u32; 24], + /// Interrupt Set-Pending + pub ispr: [RW; 8], + reserved2: [u32; 24], + /// Interrupt Clear-Pending + pub icpr: [RW; 8], + reserved3: [u32; 24], + /// Interrupt Active Bit + pub iabr: [RO; 8], + reserved4: [u32; 56], + /// Interrupt Priority + pub ipr: [RW; 240], } -/// `&syst::Registers` -pub fn syst() -> &'static syst::Registers { - unsafe { ::deref(SYST) } +impl Nvic { + /// Clears `interrupt`'s pending state + pub fn clear_pending(&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(&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(&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(&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(&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(&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(&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(&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(&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, + /// Vector Table Offset + pub vtor: RW, + /// Application Interrupt and Reset Control + pub aircr: RW, + /// System Control + pub scr: RW, + /// Configuration and Control + pub ccr: RW, + /// System Handler Priority + pub shpr: [RW; 12], + /// System Handler Control and State + pub shpcrs: RW, + /// Configurable Fault Status + pub cfsr: RW, + /// HardFault Status + pub hfsr: RW, + /// Debug Fault Status + pub dfsr: RW, + /// MemManage Fault Address + pub mmar: RW, + /// BusFault Address + pub bfar: RW, + /// Auxiliary Fault Status + pub afsr: RW, + reserved: [u32; 18], + /// Coprocessor Access Control + pub cpacr: RW, } -/// `&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, + /// Reload Value + pub rvr: RW, + /// Current Value + pub cvr: RW, + /// Calibration Value + pub calib: RO, } -/// `&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, + /// Current Parallel Port Size + pub cspsr: RW, + reserved0: [u32; 2], + /// Asynchronous Clock Prescaler + pub acpr: RW, + reserved1: [u32; 55], + /// Selected Pin Control + pub sppr: RW, + reserved2: [u32; 943], + /// Lock Access + pub lar: WO, + /// Lock Status + pub lsr: RO, + reserved3: [u32; 4], + /// TPIU Type + pub _type: RO, } diff --git a/src/peripheral/mpu.rs b/src/peripheral/mpu.rs deleted file mode 100644 index e024e625..00000000 --- a/src/peripheral/mpu.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! Memory Protection Unit - -use volatile_register::{RO, RW}; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Type - pub _type: RO, - /// Control - pub ctrl: RW, - /// Region Number - pub rnr: RW, - /// Region Base Address - pub rbar: RW, - /// Region Attribute and Size - pub rasr: RW, - /// Alias 1 of RBAR - pub rbar_a1: RW, - /// Alias 1 of RSAR - pub rsar_a1: RW, - /// Alias 2 of RBAR - pub rbar_a2: RW, - /// Alias 2 of RSAR - pub rsar_a2: RW, - /// Alias 3 of RBAR - pub rbar_a3: RW, - /// Alias 3 of RSAR - pub rsar_a3: RW, -} diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs deleted file mode 100644 index 8c12698c..00000000 --- a/src/peripheral/nvic.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! Nested Vector Interrupt Controller - -use interrupt::Nr; -use volatile_register::{RO, RW}; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Interrupt Set-Enable - iser: [RW; 8], - reserved0: [u32; 24], - /// Interrupt Clear-Enable - icer: [RW; 8], - reserved1: [u32; 24], - /// Interrupt Set-Pending - ispr: [RW; 8], - reserved2: [u32; 24], - /// Interrupt Clear-Pending - icpr: [RW; 8], - reserved3: [u32; 24], - /// Interrupt Active Bit - iabr: [RO; 8], - reserved4: [u32; 56], - /// Interrupt Priority - ipr: [RW; 240], -} - -impl Registers { - /// Clears `interrupt` pending state - pub fn clear_pending(&mut self, interrupt: I) - where I: Nr - { - let nr = interrupt.nr(); - - self.icpr[usize::from(nr / 32)].write(1 << (nr % 32)); - } - - /// Disables `interrupt` - pub fn disable(&mut self, interrupt: I) - where I: Nr - { - let nr = interrupt.nr(); - - self.icer[usize::from(nr / 32)].write(1 << (nr % 32)); - } - - /// Enables `interrupt` - pub fn enable(&mut self, interrupt: I) - where I: Nr - { - let nr = interrupt.nr(); - - 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(&mut 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(&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(&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(&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(&mut self, interrupt: I) - where I: Nr - { - let nr = interrupt.nr(); - - 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(&mut self, interrupt: I, prio: u8) - where I: Nr - { - let nr = interrupt.nr(); - - self.ipr[usize::from(nr)].write(prio); - } -} diff --git a/src/peripheral/scb.rs b/src/peripheral/scb.rs deleted file mode 100644 index d2b204ad..00000000 --- a/src/peripheral/scb.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! System Control Block - -use volatile_register::RW; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Interrupt Control and State - pub icsr: RW, - /// Vector Table Offset - pub vtor: RW, - /// Application Interrupt and Reset Control - pub aircr: RW, - /// System Control - pub scr: RW, - /// Configuration and Control - pub ccr: RW, - /// System Handler Priority - pub shpr: [RW; 12], - /// System Handler Control and State - pub shpcrs: RW, - /// Configurable Fault Status - pub cfsr: RW, - /// HardFault Status - pub hfsr: RW, - /// Debug Fault Status - pub dfsr: RW, - /// MemManage Fault Address - pub mmar: RW, - /// BusFault Address - pub bfar: RW, - /// Auxiliary Fault Status - pub afsr: RW, - reserved: [u32; 18], - /// Coprocessor Access Control - pub cpacr: RW, -} diff --git a/src/peripheral/syst.rs b/src/peripheral/syst.rs deleted file mode 100644 index 8ee70a14..00000000 --- a/src/peripheral/syst.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! SysTick: System Timer - -use volatile_register::{RO, RW}; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Control and Status - pub csr: RW, - /// Reload Value - pub rvr: RW, - /// Current Value - pub cvr: RW, - /// Calibration Value - pub calib: RO, -} diff --git a/src/peripheral/test.rs b/src/peripheral/test.rs index c291b161..1770e03b 100644 --- a/src/peripheral/test.rs +++ b/src/peripheral/test.rs @@ -1,162 +1,167 @@ #[test] fn cpuid() { - let cpuid = ::peripheral::cpuid(); - - assert_eq!(::address(&cpuid.base), 0xE000_ED00); - assert_eq!(::address(&cpuid.pfr), 0xE000_ED40); - assert_eq!(::address(&cpuid.dfr), 0xE000_ED48); - assert_eq!(::address(&cpuid.afr), 0xE000_ED4C); - assert_eq!(::address(&cpuid.mmfr), 0xE000_ED50); - assert_eq!(::address(&cpuid.isar), 0xE000_ED60); - assert_eq!(::address(&cpuid.clidr), 0xE000_ED78); - assert_eq!(::address(&cpuid.ctr), 0xE000_ED7C); - assert_eq!(::address(&cpuid.ccsidr), 0xE000_ED80); - assert_eq!(::address(&cpuid.csselr), 0xE000_ED84); + let cpuid = unsafe { &*::peripheral::CPUID.get() }; + + assert_eq!(address(&cpuid.base), 0xE000_ED00); + assert_eq!(address(&cpuid.pfr), 0xE000_ED40); + assert_eq!(address(&cpuid.dfr), 0xE000_ED48); + assert_eq!(address(&cpuid.afr), 0xE000_ED4C); + assert_eq!(address(&cpuid.mmfr), 0xE000_ED50); + assert_eq!(address(&cpuid.isar), 0xE000_ED60); + assert_eq!(address(&cpuid.clidr), 0xE000_ED78); + assert_eq!(address(&cpuid.ctr), 0xE000_ED7C); + assert_eq!(address(&cpuid.ccsidr), 0xE000_ED80); + assert_eq!(address(&cpuid.csselr), 0xE000_ED84); } #[test] fn dcb() { - for dcb in &[::peripheral::dcb(), unsafe { ::peripheral::dcb_mut() }] { - assert_eq!(::address(&dcb.dhcsr), 0xE000_EDF0); - assert_eq!(::address(&dcb.dcrsr), 0xE000_EDF4); - assert_eq!(::address(&dcb.dcrdr), 0xE000_EDF8); - assert_eq!(::address(&dcb.demcr), 0xE000_EDFC); - } + let dcb = unsafe { &*::peripheral::DCB.get() }; + + assert_eq!(address(&dcb.dhcsr), 0xE000_EDF0); + assert_eq!(address(&dcb.dcrsr), 0xE000_EDF4); + assert_eq!(address(&dcb.dcrdr), 0xE000_EDF8); + assert_eq!(address(&dcb.demcr), 0xE000_EDFC); } #[test] fn dwt() { - for dwt in &[::peripheral::dwt(), unsafe { ::peripheral::dwt_mut() }] { - assert_eq!(::address(&dwt.ctrl), 0xE000_1000); - assert_eq!(::address(&dwt.cyccnt), 0xE000_1004); - assert_eq!(::address(&dwt.cpicnt), 0xE000_1008); - assert_eq!(::address(&dwt.exccnt), 0xE000_100C); - assert_eq!(::address(&dwt.sleepcnt), 0xE000_1010); - assert_eq!(::address(&dwt.lsucnt), 0xE000_1014); - assert_eq!(::address(&dwt.foldcnt), 0xE000_1018); - assert_eq!(::address(&dwt.pcsr), 0xE000_101C); - assert_eq!(::address(&dwt.c[0].comp), 0xE000_1020); - assert_eq!(::address(&dwt.c[0].mask), 0xE000_1024); - assert_eq!(::address(&dwt.c[0].function), 0xE000_1028); - assert_eq!(::address(&dwt.c[1].comp), 0xE000_1030); - assert_eq!(::address(&dwt.c[1].mask), 0xE000_1034); - assert_eq!(::address(&dwt.c[1].function), 0xE000_1038); - assert_eq!(::address(&dwt.lar), 0xE000_1FB0); - assert_eq!(::address(&dwt.lsr), 0xE000_1FB4); - } + let dwt = unsafe { &*::peripheral::DWT.get() }; + + assert_eq!(address(&dwt.ctrl), 0xE000_1000); + assert_eq!(address(&dwt.cyccnt), 0xE000_1004); + assert_eq!(address(&dwt.cpicnt), 0xE000_1008); + assert_eq!(address(&dwt.exccnt), 0xE000_100C); + assert_eq!(address(&dwt.sleepcnt), 0xE000_1010); + assert_eq!(address(&dwt.lsucnt), 0xE000_1014); + assert_eq!(address(&dwt.foldcnt), 0xE000_1018); + assert_eq!(address(&dwt.pcsr), 0xE000_101C); + assert_eq!(address(&dwt.c[0].comp), 0xE000_1020); + assert_eq!(address(&dwt.c[0].mask), 0xE000_1024); + assert_eq!(address(&dwt.c[0].function), 0xE000_1028); + assert_eq!(address(&dwt.c[1].comp), 0xE000_1030); + assert_eq!(address(&dwt.c[1].mask), 0xE000_1034); + assert_eq!(address(&dwt.c[1].function), 0xE000_1038); + assert_eq!(address(&dwt.lar), 0xE000_1FB0); + assert_eq!(address(&dwt.lsr), 0xE000_1FB4); } #[test] fn fpb() { - for fpb in &[::peripheral::fpb(), unsafe { ::peripheral::fpb_mut() }] { - assert_eq!(::address(&fpb.ctrl), 0xE000_2000); - assert_eq!(::address(&fpb.remap), 0xE000_2004); - assert_eq!(::address(&fpb.comp), 0xE000_2008); - assert_eq!(::address(&fpb.comp[1]), 0xE000_200C); - assert_eq!(::address(&fpb.lar), 0xE000_2FB0); - assert_eq!(::address(&fpb.lsr), 0xE000_2FB4); - } + let fpb = unsafe { &*::peripheral::FPB.get() }; + + assert_eq!(address(&fpb.ctrl), 0xE000_2000); + assert_eq!(address(&fpb.remap), 0xE000_2004); + assert_eq!(address(&fpb.comp), 0xE000_2008); + assert_eq!(address(&fpb.comp[1]), 0xE000_200C); + assert_eq!(address(&fpb.lar), 0xE000_2FB0); + assert_eq!(address(&fpb.lsr), 0xE000_2FB4); } #[test] fn fpu() { - for fpu in &[::peripheral::fpu(), unsafe { ::peripheral::fpu_mut() }] { - assert_eq!(::address(&fpu.fpccr), 0xE000_EF34); - assert_eq!(::address(&fpu.fpcar), 0xE000_EF38); - assert_eq!(::address(&fpu.fpdscr), 0xE000_EF3C); - assert_eq!(::address(&fpu.mvfr), 0xE000_EF40); - assert_eq!(::address(&fpu.mvfr[1]), 0xE000_EF44); - assert_eq!(::address(&fpu.mvfr[2]), 0xE000_EF48); - } + let fpu = unsafe { &*::peripheral::FPU.get() }; + + assert_eq!(address(&fpu.fpccr), 0xE000_EF34); + assert_eq!(address(&fpu.fpcar), 0xE000_EF38); + assert_eq!(address(&fpu.fpdscr), 0xE000_EF3C); + assert_eq!(address(&fpu.mvfr), 0xE000_EF40); + assert_eq!(address(&fpu.mvfr[1]), 0xE000_EF44); + assert_eq!(address(&fpu.mvfr[2]), 0xE000_EF48); } #[test] fn itm() { - for itm in &[::peripheral::itm(), unsafe { ::peripheral::itm_mut() }] { - assert_eq!(::address(&itm.stim), 0xE000_0000); - assert_eq!(::address(&itm.ter), 0xE000_0E00); - assert_eq!(::address(&itm.tpr), 0xE000_0E40); - assert_eq!(::address(&itm.tcr), 0xE000_0E80); - assert_eq!(::address(&itm.lar), 0xE000_0FB0); - assert_eq!(::address(&itm.lsr), 0xE000_0FB4); - } + let itm = unsafe { &*::peripheral::ITM.get() }; + + assert_eq!(address(&itm.stim), 0xE000_0000); + assert_eq!(address(&itm.ter), 0xE000_0E00); + assert_eq!(address(&itm.tpr), 0xE000_0E40); + assert_eq!(address(&itm.tcr), 0xE000_0E80); + assert_eq!(address(&itm.lar), 0xE000_0FB0); + assert_eq!(address(&itm.lsr), 0xE000_0FB4); } #[test] fn mpu() { - for mpu in &[::peripheral::mpu(), unsafe { ::peripheral::mpu_mut() }] { - assert_eq!(::address(&mpu._type), 0xE000ED90); - assert_eq!(::address(&mpu.ctrl), 0xE000ED94); - assert_eq!(::address(&mpu.rnr), 0xE000ED98); - assert_eq!(::address(&mpu.rbar), 0xE000ED9C); - assert_eq!(::address(&mpu.rasr), 0xE000EDA0); - assert_eq!(::address(&mpu.rbar_a1), 0xE000EDA4); - assert_eq!(::address(&mpu.rsar_a1), 0xE000EDA8); - assert_eq!(::address(&mpu.rbar_a2), 0xE000EDAC); - assert_eq!(::address(&mpu.rsar_a2), 0xE000EDB0); - assert_eq!(::address(&mpu.rbar_a3), 0xE000EDB4); - assert_eq!(::address(&mpu.rsar_a3), 0xE000EDB8); - } + let mpu = unsafe { &*::peripheral::MPU.get() }; + + assert_eq!(address(&mpu._type), 0xE000ED90); + assert_eq!(address(&mpu.ctrl), 0xE000ED94); + assert_eq!(address(&mpu.rnr), 0xE000ED98); + assert_eq!(address(&mpu.rbar), 0xE000ED9C); + assert_eq!(address(&mpu.rasr), 0xE000EDA0); + assert_eq!(address(&mpu.rbar_a1), 0xE000EDA4); + assert_eq!(address(&mpu.rsar_a1), 0xE000EDA8); + assert_eq!(address(&mpu.rbar_a2), 0xE000EDAC); + assert_eq!(address(&mpu.rsar_a2), 0xE000EDB0); + assert_eq!(address(&mpu.rbar_a3), 0xE000EDB4); + assert_eq!(address(&mpu.rsar_a3), 0xE000EDB8); } #[test] fn nvic() { - for nvic in &[::peripheral::nvic(), unsafe { ::peripheral::nvic_mut() }] { - assert_eq!(::address(&nvic.iser), 0xE000E100); - assert_eq!(::address(&nvic.iser[15]), 0xE000E13C); - assert_eq!(::address(&nvic.icer), 0xE000E180); - assert_eq!(::address(&nvic.icer[7]), 0xE000E19C); - assert_eq!(::address(&nvic.icer[15]), 0xE000E1BC); - assert_eq!(::address(&nvic.ispr), 0xE000E200); - assert_eq!(::address(&nvic.ispr[15]), 0xE000E23C); - assert_eq!(::address(&nvic.icpr), 0xE000E280); - assert_eq!(::address(&nvic.icpr[15]), 0xE000E2BC); - assert_eq!(::address(&nvic.iabr), 0xE000E300); - assert_eq!(::address(&nvic.iabr[15]), 0xE000E33C); - assert_eq!(::address(&nvic.ipr), 0xE000E400); - assert_eq!(::address(&nvic.ipr[59]), 0xE000E4EC); - } + let nvic = unsafe { &*::peripheral::NVIC.get() }; + + assert_eq!(address(&nvic.iser), 0xE000E100); + assert_eq!(address(&nvic.iser[7]), 0xE000E11C); + assert_eq!(address(&nvic.icer), 0xE000E180); + assert_eq!(address(&nvic.icer[7]), 0xE000E19C); + assert_eq!(address(&nvic.ispr), 0xE000E200); + assert_eq!(address(&nvic.ispr[7]), 0xE000E21C); + assert_eq!(address(&nvic.icpr), 0xE000E280); + assert_eq!(address(&nvic.icpr[7]), 0xE000E29C); + assert_eq!(address(&nvic.iabr), 0xE000E300); + assert_eq!(address(&nvic.iabr[7]), 0xE000E31C); + assert_eq!(address(&nvic.ipr), 0xE000E400); + assert_eq!(address(&nvic.ipr[239]), 0xE000E4eF); } #[test] fn scb() { - for scb in &[::peripheral::scb(), unsafe { ::peripheral::scb_mut() }] { - assert_eq!(::address(&scb.icsr), 0xE000_ED04); - assert_eq!(::address(&scb.vtor), 0xE000_ED08); - assert_eq!(::address(&scb.aircr), 0xE000_ED0C); - assert_eq!(::address(&scb.scr), 0xE000_ED10); - assert_eq!(::address(&scb.ccr), 0xE000_ED14); - assert_eq!(::address(&scb.shpr), 0xE000_ED18); - assert_eq!(::address(&scb.shpcrs), 0xE000_ED24); - assert_eq!(::address(&scb.cfsr), 0xE000_ED28); - assert_eq!(::address(&scb.hfsr), 0xE000_ED2C); - assert_eq!(::address(&scb.dfsr), 0xE000_ED30); - assert_eq!(::address(&scb.mmar), 0xE000_ED34); - assert_eq!(::address(&scb.bfar), 0xE000_ED38); - assert_eq!(::address(&scb.afsr), 0xE000_ED3C); - assert_eq!(::address(&scb.cpacr), 0xE000_ED88); - } + let scb = unsafe { &*::peripheral::SCB.get() }; + + assert_eq!(address(&scb.icsr), 0xE000_ED04); + assert_eq!(address(&scb.vtor), 0xE000_ED08); + assert_eq!(address(&scb.aircr), 0xE000_ED0C); + assert_eq!(address(&scb.scr), 0xE000_ED10); + assert_eq!(address(&scb.ccr), 0xE000_ED14); + assert_eq!(address(&scb.shpr), 0xE000_ED18); + assert_eq!(address(&scb.shpcrs), 0xE000_ED24); + assert_eq!(address(&scb.cfsr), 0xE000_ED28); + assert_eq!(address(&scb.hfsr), 0xE000_ED2C); + assert_eq!(address(&scb.dfsr), 0xE000_ED30); + assert_eq!(address(&scb.mmar), 0xE000_ED34); + assert_eq!(address(&scb.bfar), 0xE000_ED38); + assert_eq!(address(&scb.afsr), 0xE000_ED3C); + assert_eq!(address(&scb.cpacr), 0xE000_ED88); + } #[test] fn syst() { - for syst in &[::peripheral::syst(), unsafe { ::peripheral::syst_mut() }] { - assert_eq!(::address(&syst.csr), 0xE000_E010); - assert_eq!(::address(&syst.rvr), 0xE000_E014); - assert_eq!(::address(&syst.cvr), 0xE000_E018); - assert_eq!(::address(&syst.calib), 0xE000_E01C); - } + let syst = unsafe { &*::peripheral::SYST.get() }; + + assert_eq!(address(&syst.csr), 0xE000_E010); + assert_eq!(address(&syst.rvr), 0xE000_E014); + assert_eq!(address(&syst.cvr), 0xE000_E018); + assert_eq!(address(&syst.calib), 0xE000_E01C); + } #[test] fn tpiu() { - for tpiu in &[::peripheral::tpiu(), unsafe { ::peripheral::tpiu_mut() }] { - assert_eq!(::address(&tpiu.sspsr), 0xE004_0000); - assert_eq!(::address(&tpiu.cspsr), 0xE004_0004); - assert_eq!(::address(&tpiu.acpr), 0xE004_0010); - assert_eq!(::address(&tpiu.sppr), 0xE004_00F0); - assert_eq!(::address(&tpiu.lar), 0xE004_0FB0); - assert_eq!(::address(&tpiu.lsr), 0xE004_0FB4); - assert_eq!(::address(&tpiu._type), 0xE004_0FC8); - } + let tpiu = unsafe { &*::peripheral::TPIU.get() }; + + assert_eq!(address(&tpiu.sspsr), 0xE004_0000); + assert_eq!(address(&tpiu.cspsr), 0xE004_0004); + assert_eq!(address(&tpiu.acpr), 0xE004_0010); + assert_eq!(address(&tpiu.sppr), 0xE004_00F0); + assert_eq!(address(&tpiu.lar), 0xE004_0FB0); + assert_eq!(address(&tpiu.lsr), 0xE004_0FB4); + assert_eq!(address(&tpiu._type), 0xE004_0FC8); +} + +fn address(r: *const T) -> usize { + r as usize } diff --git a/src/peripheral/tpiu.rs b/src/peripheral/tpiu.rs deleted file mode 100644 index 50473512..00000000 --- a/src/peripheral/tpiu.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Trace Port Interface Unit - -use volatile_register::{RO, RW, WO}; - -/// Registers -#[repr(C)] -pub struct Registers { - /// Supported Parallel Port Sizes - pub sspsr: RO, - /// Current Parallel Port Size - pub cspsr: RW, - reserved0: [u32; 2], - /// Asynchronous Clock Prescaler - pub acpr: RW, - reserved1: [u32; 55], - /// Selected Pin Control - pub sppr: RW, - reserved2: [u32; 943], - /// Lock Access - pub lar: WO, - /// Lock Status - pub lsr: RO, - reserved3: [u32; 4], - /// TPIU Type - pub _type: RO, -} From 0e628b32ac27d47aa897dfd7930ddad85903bd37 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 8 Mar 2017 10:02:51 -0500 Subject: [PATCH 12/31] turn bkpt! into a function --- CHANGELOG.md | 3 +++ src/asm.rs | 54 +++++++++++++----------------------------------- src/exception.rs | 41 +++++++++++++++++++++++------------- 3 files changed, 44 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f28c78..5289b6fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The safety of assembly wrappers like `wfi` and `interrupt::free` has been reviewed. In many cases, the functions are no longer unsafe. +- [breaking-change] `bkpt!` has been turned into a function. It no longer + accepts an immediate value. + ### Removed - `vector_table` and its associated `struct`, `VectorTable`. It's not a good diff --git a/src/asm.rs b/src/asm.rs index b94d4efc..c82d45d3 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -3,45 +3,30 @@ /// Puts the processor in Debug state. Debuggers can pick this up as a /// "breakpoint". /// -/// Optionally, an "immediate" value (in the 0-255 range) can be passed to -/// `bkpt!`. The debugger can then read this value using the Program Counter -/// (PC). -#[cfg(target_arch = "arm")] -#[macro_export] -macro_rules! bkpt { - () => { +/// NOTE calling `bkpt` when the processor is not connected to a debugger will +/// cause an exception +#[inline(always)] +pub fn bkpt() { + #[cfg(target_arch = "arm")] + unsafe { asm!("bkpt" : : : : "volatile"); - }; - ($imm:expr) => { - asm!(concat!("bkpt #", stringify!($imm)) + } +} + +/// A no-operation. Useful to prevent delay loops from being optimized away. +pub fn nop() { + unsafe { + asm!("nop" : : : : "volatile"); - }; -} - -/// Puts the processor in Debug state. Debuggers can pick this up as a -/// "breakpoint". -/// -/// Optionally, an "immediate" value (in the 0-255 range) can be passed to -/// `bkpt!`. The debugger can then read this value using the Program Counter -/// (PC). -#[cfg(not(target_arch = "arm"))] -#[macro_export] -macro_rules! bkpt { - () => { - asm!(""); - }; - ($e:expr) => { - asm!(""); - }; + } } - /// Wait For Event pub fn wfe() { match () { @@ -73,14 +58,3 @@ pub fn wfi() { () => {} } } - -/// A no-operation. Useful to prevent delay loops from being optimized away. -pub fn nop() { - unsafe { - asm!("nop" - : - : - : - : "volatile"); - } -} diff --git a/src/exception.rs b/src/exception.rs index b944d6cb..8343d99a 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -72,32 +72,48 @@ pub struct Handlers { /// Pendable request for system service pub pendsv: unsafe extern "C" fn(&PendsvCtxt), /// System tick timer - pub sys_tick: unsafe extern "C" fn (&SysTickCtxt), + pub sys_tick: unsafe extern "C" fn(&SysTickCtxt), } /// Identifies the Nmi exception -pub struct NmiCtxt { _0: () } +pub struct NmiCtxt { + _0: (), +} /// Identifies the HardFault exception -pub struct HardFaultCtxt { _0: () } +pub struct HardFaultCtxt { + _0: (), +} /// Identifies the MemManage exception -pub struct MemManageCtxt { _0: () } +pub struct MemManageCtxt { + _0: (), +} /// Identifies the BusFault exception -pub struct BusFaultCtxt { _0: () } +pub struct BusFaultCtxt { + _0: (), +} /// Identifies the UsageFault exception -pub struct UsageFaultCtxt { _0: () } +pub struct UsageFaultCtxt { + _0: (), +} /// Identifies the Svcall exception -pub struct SvcallCtxt { _0: () } +pub struct SvcallCtxt { + _0: (), +} /// Identifies the Pendsv exception -pub struct PendsvCtxt { _0: () } +pub struct PendsvCtxt { + _0: (), +} /// Identifies the Systick exception -pub struct SysTickCtxt { _0: () } +pub struct SysTickCtxt { + _0: (), +} unsafe impl Token for NmiCtxt {} @@ -134,8 +150,7 @@ pub const DEFAULT_HANDLERS: Handlers = Handlers { // This needs asm!, #[naked] and unreachable() to avoid modifying the stack // pointer (MSP), that way it points to the stacked registers #[naked] -pub unsafe extern "C" fn default_handler(_token: &T) -{ +pub unsafe extern "C" fn default_handler(_token: &T) { // This is the actual exception handler. `_sf` is a pointer to the previous // stack frame #[cfg(target_arch = "arm")] @@ -143,9 +158,7 @@ pub unsafe extern "C" fn default_handler(_token: &T) #[cfg(feature = "semihosting")] hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sr.pc); - unsafe { - bkpt!(); - } + ::asm::bkpt(); loop {} } From 559da5e265d4c1ac88c8bf07f5a4d317c922b408 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 8 Mar 2017 13:54:56 -0500 Subject: [PATCH 13/31] add macros for writing to an ITM port --- src/itm.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/macros.rs | 25 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 src/itm.rs diff --git a/src/itm.rs b/src/itm.rs new file mode 100644 index 00000000..6022bbe1 --- /dev/null +++ b/src/itm.rs @@ -0,0 +1,38 @@ +//! Instrumentation Trace Macrocell + +use core::fmt; + +struct Itm { + port: u8, +} + +impl Itm { + fn write_all(&self, buffer: &[u8]) { + let stim = + unsafe { &(*::peripheral::ITM.get()).stim[self.port as usize] }; + + for byte in buffer { + while !stim.is_fifo_ready() {} + stim.write_u8(*byte); + } + } +} + +impl fmt::Write for Itm { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_all(s.as_bytes()); + Ok(()) + } +} + +/// Writes `fmt::Arguments` to the ITM `port` +pub fn write_fmt(port: u8, args: fmt::Arguments) { + use core::fmt::Write; + + Itm { port }.write_fmt(args).ok(); +} + +/// Writes a string to the ITM `port` +pub fn write_str(port: u8, string: &str) { + Itm { port }.write_all(string.as_bytes()) +} diff --git a/src/lib.rs b/src/lib.rs index 59428ba6..9bdd74cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ pub mod asm; pub mod ctxt; pub mod exception; pub mod interrupt; +pub mod itm; pub mod peripheral; pub mod register; diff --git a/src/macros.rs b/src/macros.rs index bd311676..1c39f18d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,3 +27,28 @@ macro_rules! hprintln { ($fmt:expr) => (hprint!(concat!($fmt, "\n"))); ($fmt:expr, $($arg:tt)*) => (hprint!(concat!($fmt, "\n"), $($arg)*)); } + +/// Macro for sending a formatted string through an ITM channel +#[macro_export] +macro_rules! iprint { + ($channel:expr, $s:expr) => { + $crate::itm::write_str($channel, $s); + }; + ($channel:expr, $($arg:tt)*) => { + $crate::itm::write_fmt($channel, format_args!($($arg)*)); + }; +} + +/// Macro for sending a formatted string through an ITM channel, with a newline. +#[macro_export] +macro_rules! iprintln { + ($channel:expr) => { + iprint!($channel, "\n"); + }; + ($channel:expr, $fmt:expr) => { + iprint!($channel, concat!($fmt, "\n")); + }; + ($channel:expr, $fmt:expr, $($arg:tt)*) => { + iprint!($channel, concat!($fmt, "\n"), $($arg)*); + }; +} From 131339367b1a299834b975ce9caf79e049401a8b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 8 Mar 2017 14:07:50 -0500 Subject: [PATCH 14/31] remove the semihosting Cargo feature make the semihosting macros always available --- Cargo.toml | 6 +----- src/exception.rs | 3 --- src/lib.rs | 3 +-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4f298580..bdd3a4c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,4 @@ version = "0.2.0" volatile-register = "0.2.0" [dependencies.cortex-m-semihosting] -optional = true -version = "0.1.3" - -[features] -semihosting = ["cortex-m-semihosting"] \ No newline at end of file +version = "0.1.3" \ No newline at end of file diff --git a/src/exception.rs b/src/exception.rs index 8343d99a..ffe9f39d 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -155,9 +155,6 @@ pub unsafe extern "C" fn default_handler(_token: &T) { // stack frame #[cfg(target_arch = "arm")] extern "C" fn handler(_sr: &StackedRegisters) -> ! { - #[cfg(feature = "semihosting")] - hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sr.pc); - ::asm::bkpt(); loop {} diff --git a/src/lib.rs b/src/lib.rs index 9bdd74cc..05fcee5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! - Data structures like the vector table //! - Safe wrappers around assembly instructions like `bkpt` -#![cfg_attr(feature = "semihosting", feature(macro_reexport))] +#![feature(macro_reexport)] #![cfg_attr(target_arch = "arm", feature(core_intrinsics))] #![deny(missing_docs)] #![deny(warnings)] @@ -17,7 +17,6 @@ #![feature(naked_functions)] #![no_std] -#[cfg(feature = "semihosting")] pub extern crate cortex_m_semihosting as semihosting; extern crate volatile_register; From 50d19895633a8d4311549b214b8f5ae9f9d33236 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 8 Mar 2017 14:21:14 -0500 Subject: [PATCH 15/31] add a borrow method to Peripheral --- src/peripheral/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index c6e8faa1..f47baf5d 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -10,7 +10,7 @@ use core::ptr; use volatile_register::{RO, RW, WO}; -use interrupt::Nr; +use interrupt::{CsCtxt, Nr}; #[cfg(test)] mod test; @@ -69,6 +69,11 @@ impl Peripheral { } } + /// Borrows the peripheral for the duration of the critical section + pub fn borrow<'cs>(&self, _ctxt: &'cs CsCtxt) -> &'cs T { + unsafe { &*self.get() } + } + /// Returns a pointer to the register block pub fn get(&self) -> *mut T { self.address as *mut T From eb247936ddd3603038fc4668b062908e1df4f1fc Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 8 Mar 2017 21:42:28 -0500 Subject: [PATCH 16/31] improve throughput of ITM functions --- src/itm.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/src/itm.rs b/src/itm.rs index 6022bbe1..d4fae5d4 100644 --- a/src/itm.rs +++ b/src/itm.rs @@ -1,30 +1,73 @@ //! Instrumentation Trace Macrocell -use core::fmt; +use core::{fmt, ptr, slice}; +use peripheral::Stim; -struct Itm { - port: u8, +fn round_up_to_multiple_of(x: usize, k: usize) -> usize { + let rem = x % k; + + if rem == 0 { x } else { x + k - rem } +} + +fn round_down_to_multiple_of(x: usize, k: usize) -> usize { + x - (x % k) } -impl Itm { - fn write_all(&self, buffer: &[u8]) { - let stim = - unsafe { &(*::peripheral::ITM.get()).stim[self.port as usize] }; +unsafe fn split(buffer: &[u8]) -> (&[u8], &[u32], &[u8]) { + let start = buffer.as_ptr(); + let end = start.offset(buffer.len() as isize); + let sbody = round_up_to_multiple_of(start as usize, 4); + let ebody = round_down_to_multiple_of(end as usize, 4); - for byte in buffer { - while !stim.is_fifo_ready() {} - stim.write_u8(*byte); - } + let head = slice::from_raw_parts(start, sbody - start as usize); + let body = slice::from_raw_parts(sbody as *const _, (ebody - sbody) >> 2); + let tail = slice::from_raw_parts(ebody as *const _, end as usize - ebody); + + (head, body, tail) +} + +fn write_bytes(stim: &Stim, bytes: &[u8]) { + for byte in bytes { + while !stim.is_fifo_ready() {} + stim.write_u8(*byte); } } +// NOTE assumes that `bytes` is 32-bit aligned +unsafe fn write_words(stim: &Stim, bytes: &[u32]) { + let mut p = bytes.as_ptr(); + for _ in 0..bytes.len() { + while !stim.is_fifo_ready() {} + stim.write_u32(ptr::read(p)); + p = p.offset(1); + } +} + +struct Itm { + port: u8, +} + impl fmt::Write for Itm { fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_all(s.as_bytes()); + write_all(self.port, s.as_bytes()); Ok(()) } } +/// Writes a `buffer` to the ITM `port` +pub fn write_all(port: u8, buffer: &[u8]) { + let stim = unsafe { &(*::peripheral::ITM.get()).stim[port as usize] }; + + if buffer.len() < 7 { + write_bytes(stim, buffer); + } else { + let (head, body, tail) = unsafe { split(buffer) }; + write_bytes(stim, head); + unsafe { write_words(stim, body) } + write_bytes(stim, tail); + } +} + /// Writes `fmt::Arguments` to the ITM `port` pub fn write_fmt(port: u8, args: fmt::Arguments) { use core::fmt::Write; @@ -34,5 +77,5 @@ pub fn write_fmt(port: u8, args: fmt::Arguments) { /// Writes a string to the ITM `port` pub fn write_str(port: u8, string: &str) { - Itm { port }.write_all(string.as_bytes()) + write_all(port, string.as_bytes()) } From 8363022c7ecddcdb48e6b82c0a0fa78c6f45a879 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 10 Mar 2017 11:18:03 -0500 Subject: [PATCH 17/31] remove macro_reexport feature gate --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 05fcee5b..95a1b691 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,6 @@ //! - Data structures like the vector table //! - Safe wrappers around assembly instructions like `bkpt` -#![feature(macro_reexport)] #![cfg_attr(target_arch = "arm", feature(core_intrinsics))] #![deny(missing_docs)] #![deny(warnings)] From f2d4e38af794be833f21948a2a4837a2ff4cadd9 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 10 Mar 2017 20:10:33 -0500 Subject: [PATCH 18/31] rename Token to Context --- src/ctxt.rs | 18 +++++++------- src/exception.rs | 64 ++++++++++++++++++++++++------------------------ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/ctxt.rs b/src/ctxt.rs index 7cd8a740..2fdd1824 100644 --- a/src/ctxt.rs +++ b/src/ctxt.rs @@ -1,20 +1,20 @@ -//! Execution context +//! Interrupt / Exception context use core::marker::PhantomData; use core::cell::UnsafeCell; -/// Data local to an execution context +/// Data local to a context pub struct Local - where Ctxt: Token + where Ctxt: Context { _ctxt: PhantomData, data: UnsafeCell, } impl Local - where Ctxt: Token + where Ctxt: Context { - /// Initializes the local data + /// Initializes context local data pub const fn new(value: T) -> Self { Local { _ctxt: PhantomData, @@ -22,13 +22,13 @@ impl Local } } - /// Acquires a reference to the local data + /// Acquires a reference to the context local data pub fn borrow<'a>(&'static self, _ctxt: &'a Ctxt) -> &'a T { unsafe { &*self.data.get() } } } -unsafe impl Sync for Local where Ctxt: Token {} +unsafe impl Sync for Local where Ctxt: Context {} -/// A unique token that identifies an execution context -pub unsafe trait Token {} +/// A token unique to a context +pub unsafe trait Context {} diff --git a/src/exception.rs b/src/exception.rs index ffe9f39d..d32d61f3 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,6 +1,6 @@ //! Exceptions -use ctxt::Token; +use ctxt::Context; use Reserved; /// Kind of exception @@ -54,80 +54,80 @@ impl Exception { #[repr(C)] pub struct Handlers { /// Non-maskable interrupt - pub nmi: unsafe extern "C" fn(&NmiCtxt), + pub nmi: unsafe extern "C" fn(&Nmi), /// All class of fault - pub hard_fault: unsafe extern "C" fn(&HardFaultCtxt), + pub hard_fault: unsafe extern "C" fn(&HardFault), /// Memory management - pub mem_manage: unsafe extern "C" fn(&MemManageCtxt), + pub mem_manage: unsafe extern "C" fn(&MemManage), /// Pre-fetch fault, memory access fault - pub bus_fault: unsafe extern "C" fn(&BusFaultCtxt), + pub bus_fault: unsafe extern "C" fn(&BusFault), /// Undefined instruction or illegal state - pub usage_fault: unsafe extern "C" fn(&UsageFaultCtxt), + pub usage_fault: unsafe extern "C" fn(&UsageFault), /// Reserved spots in the vector table pub _reserved0: [Reserved; 4], /// System service call via SWI instruction - pub svcall: unsafe extern "C" fn(&SvcallCtxt), + pub svcall: unsafe extern "C" fn(&Svcall), /// Reserved spots in the vector table pub _reserved1: [Reserved; 2], /// Pendable request for system service - pub pendsv: unsafe extern "C" fn(&PendsvCtxt), + pub pendsv: unsafe extern "C" fn(&Pendsv), /// System tick timer - pub sys_tick: unsafe extern "C" fn(&SysTickCtxt), + pub sys_tick: unsafe extern "C" fn(&SysTick), } -/// Identifies the Nmi exception -pub struct NmiCtxt { +/// Non-maskable interrupt +pub struct Nmi { _0: (), } -/// Identifies the HardFault exception -pub struct HardFaultCtxt { +/// All class of fault +pub struct HardFault { _0: (), } -/// Identifies the MemManage exception -pub struct MemManageCtxt { +/// Memory management +pub struct MemManage { _0: (), } -/// Identifies the BusFault exception -pub struct BusFaultCtxt { +/// Pre-fetch fault, memory access fault +pub struct BusFault { _0: (), } -/// Identifies the UsageFault exception -pub struct UsageFaultCtxt { +/// Undefined instruction or illegal state +pub struct UsageFault { _0: (), } -/// Identifies the Svcall exception -pub struct SvcallCtxt { +/// System service call via SWI instruction +pub struct Svcall { _0: (), } -/// Identifies the Pendsv exception -pub struct PendsvCtxt { +/// Pendable request for system service +pub struct Pendsv { _0: (), } -/// Identifies the Systick exception -pub struct SysTickCtxt { +/// System tick timer +pub struct SysTick { _0: (), } -unsafe impl Token for NmiCtxt {} +unsafe impl Context for Nmi {} -unsafe impl Token for HardFaultCtxt {} +unsafe impl Context for HardFault {} -unsafe impl Token for MemManageCtxt {} +unsafe impl Context for MemManage {} -unsafe impl Token for BusFaultCtxt {} +unsafe impl Context for BusFault {} -unsafe impl Token for SvcallCtxt {} +unsafe impl Context for Svcall {} -unsafe impl Token for PendsvCtxt {} +unsafe impl Context for Pendsv {} -unsafe impl Token for SysTickCtxt {} +unsafe impl Context for SysTick {} /// Default exception handlers pub const DEFAULT_HANDLERS: Handlers = Handlers { From 81c9d39ebc8e73baa396a60eb8d9f717a74b96f0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 10 Mar 2017 20:35:52 -0500 Subject: [PATCH 19/31] CsCtxt renamed to CriticalSection. Mutex.lock removed in favor of Mutex.borrow --- CHANGELOG.md | 6 ++++++ src/interrupt.rs | 21 ++++++++------------- src/peripheral/mod.rs | 6 +++--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5289b6fe..cdf3795e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Execution context primitives: execution context `Local` data and `Token`s to identify execution contexts. +- A `borrow` method to `Mutex` that replaces `lock`. This method returns a `&-` + reference. + ### Changed - [breaking-change] `StackFrame` has been renamed to `StackedRegisters` and @@ -45,6 +48,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `vector_table` and its associated `struct`, `VectorTable`. It's not a good idea to give people a simple way to call the exception handlers. +- `Mutex`'s `lock` method as it's unsound. You can use it to get multiple `&mut + -` references to the data. + ## [v0.1.6] - 2017-01-22 ### Added diff --git a/src/interrupt.rs b/src/interrupt.rs index 95829ca8..6d84dec8 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -15,14 +15,9 @@ impl Mutex { } impl Mutex { - /// Gets access to the inner data - /// - /// NOTE this prevents interrupts handlers from running thus gaining - /// exclusive access to the processor - pub fn lock(&self, f: F) -> R - where F: FnOnce(&mut T) -> R - { - unsafe { ::interrupt::free(|_| f(&mut *self.inner.get())) } + /// Borrows the data for the duration of the critical section + pub fn borrow<'cs>(&self, _ctxt: &'cs CriticalSection) -> &'cs T { + unsafe { &*self.inner.get() } } } @@ -32,7 +27,6 @@ pub unsafe trait Nr { fn nr(&self) -> u8; } -// FIXME `T` should have some bound: `Send` or `Sync`? unsafe impl Sync for Mutex {} /// Disable interrupts, globally @@ -69,10 +63,10 @@ pub fn enable() { } } -/// Critical section token +/// Critical section context /// /// Indicates that you are executing code within a critical section -pub struct CsCtxt { +pub struct CriticalSection { _0: (), } @@ -80,14 +74,15 @@ pub struct CsCtxt { /// /// This as also known as a "critical section". pub fn free(f: F) -> R - where F: FnOnce(&CsCtxt) -> R +where + F: FnOnce(&CriticalSection) -> R, { let primask = ::register::primask::read(); // disable interrupts disable(); - let r = f(&CsCtxt { _0: () }); + let r = f(&CriticalSection { _0: () }); // If the interrupts were active before our `disable` call, then re-enable // them. Otherwise, keep them disabled diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index f47baf5d..f9ee2172 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -10,7 +10,7 @@ use core::ptr; use volatile_register::{RO, RW, WO}; -use interrupt::{CsCtxt, Nr}; +use interrupt::{CriticalSection, Nr}; #[cfg(test)] mod test; @@ -69,8 +69,8 @@ impl Peripheral { } } - /// Borrows the peripheral for the duration of the critical section - pub fn borrow<'cs>(&self, _ctxt: &'cs CsCtxt) -> &'cs T { + /// Borrows the peripheral for the duration of a critical section + pub fn borrow<'cs>(&self, _ctxt: &'cs CriticalSection) -> &'cs T { unsafe { &*self.get() } } From f6615b0fb8172e7b4d42f153d7a4f6afe6d8350c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 10 Mar 2017 23:34:59 -0500 Subject: [PATCH 20/31] reformat --- src/ctxt.rs | 12 +++++++++--- src/macros.rs | 36 +++++++++++++++++++++++++++--------- src/peripheral/mod.rs | 30 ++++++++++++++++++++---------- src/register/mod.rs | 4 ++-- 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/src/ctxt.rs b/src/ctxt.rs index 2fdd1824..0a05ecb4 100644 --- a/src/ctxt.rs +++ b/src/ctxt.rs @@ -5,14 +5,16 @@ use core::cell::UnsafeCell; /// Data local to a context pub struct Local - where Ctxt: Context +where + Ctxt: Context, { _ctxt: PhantomData, data: UnsafeCell, } impl Local - where Ctxt: Context +where + Ctxt: Context, { /// Initializes context local data pub const fn new(value: T) -> Self { @@ -28,7 +30,11 @@ impl Local } } -unsafe impl Sync for Local where Ctxt: Context {} +unsafe impl Sync for Local +where + Ctxt: Context, +{ +} /// A token unique to a context pub unsafe trait Context {} diff --git a/src/macros.rs b/src/macros.rs index 1c39f18d..1ee2cf2b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,31 +1,49 @@ /// Macro for printing to the **host's** standard stderr #[macro_export] macro_rules! ehprint { - ($s:expr) => ($crate::semihosting:::io:ewrite_str($s)); - ($($arg:tt)*) => ($crate::semihosting::io::ewrite_fmt(format_args!($($arg)*))); + ($s:expr) => { + $crate::semihosting:::io:ewrite_str($s); + }; + ($($arg:tt)*) => { + $crate::semihosting::io::ewrite_fmt(format_args!($($arg)*)); + }; } /// Macro for printing to the **host's** standard error, with a newline. #[macro_export] macro_rules! ehprintln { () => (ehprint!("\n")); - ($fmt:expr) => (ehprint!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (ehprint!(concat!($fmt, "\n"), $($arg)*)); + ($fmt:expr) => { + ehprint!(concat!($fmt, "\n")); + }; + ($fmt:expr, $($arg:tt)*) => { + ehprint!(concat!($fmt, "\n"), $($arg)*); + }; } /// Macro for printing to the **host's** standard output #[macro_export] macro_rules! hprint { - ($s:expr) => ($crate::semihosting::io::write_str($s)); - ($($arg:tt)*) => ($crate::semihosting::io::write_fmt(format_args!($($arg)*))); + ($s:expr) => { + $crate::semihosting::io::write_str($s); + }; + ($($arg:tt)*) => { + $crate::semihosting::io::write_fmt(format_args!($($arg)*)); + }; } /// Macro for printing to the **host's** standard output, with a newline. #[macro_export] macro_rules! hprintln { - () => (hprint!("\n")); - ($fmt:expr) => (hprint!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (hprint!(concat!($fmt, "\n"), $($arg)*)); + () => { + hprint!("\n"); + }; + ($fmt:expr) => { + hprint!(concat!($fmt, "\n")); + }; + ($fmt:expr, $($arg:tt)*) => { + hprint!(concat!($fmt, "\n"), $($arg)*); + }; } /// Macro for sending a formatted string through an ITM channel diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index f9ee2172..1f9e1476 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -52,7 +52,8 @@ pub const TPIU: Peripheral = unsafe { Peripheral::new(0xE004_0000) }; /// A peripheral pub struct Peripheral - where T: 'static +where + T: 'static, { address: usize, _marker: PhantomData<&'static mut T>, @@ -290,7 +291,8 @@ pub struct Nvic { impl Nvic { /// Clears `interrupt`'s pending state pub fn clear_pending(&self, interrupt: I) - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); @@ -299,7 +301,8 @@ impl Nvic { /// Disables `interrupt` pub fn disable(&self, interrupt: I) - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); @@ -308,7 +311,8 @@ impl Nvic { /// Enables `interrupt` pub fn enable(&self, interrupt: I) - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); @@ -321,7 +325,8 @@ impl Nvic { /// `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(&self, interrupt: I) -> u8 - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); @@ -330,7 +335,8 @@ impl Nvic { /// Is `interrupt` active or pre-empted and stacked pub fn is_active(&self, interrupt: I) -> bool - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); let mask = 1 << (nr % 32); @@ -340,7 +346,8 @@ impl Nvic { /// Checks if `interrupt` is enabled pub fn is_enabled(&self, interrupt: I) -> bool - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); let mask = 1 << (nr % 32); @@ -350,7 +357,8 @@ impl Nvic { /// Checks if `interrupt` is pending pub fn is_pending(&self, interrupt: I) -> bool - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); let mask = 1 << (nr % 32); @@ -360,7 +368,8 @@ impl Nvic { /// Forces `interrupt` into pending state pub fn set_pending(&self, interrupt: I) - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); @@ -372,7 +381,8 @@ impl Nvic { /// NOTE See `get_priority` method for an explanation of how NVIC priorities /// work. pub fn set_priority(&self, interrupt: I, prio: u8) - where I: Nr + where + I: Nr, { let nr = interrupt.nr(); diff --git a/src/register/mod.rs b/src/register/mod.rs index e3321a67..17f6fdad 100644 --- a/src/register/mod.rs +++ b/src/register/mod.rs @@ -8,8 +8,8 @@ //! - MSP //! - PRIMASK //! -//! The rest of registers (see list below) can be accessed in either, PRIVILEGED or UNPRIVILEGED, -//! mode. +//! The rest of registers (see list below) can be accessed in either, PRIVILEGED +//! or UNPRIVILEGED, mode. //! //! - APSR //! - LR From a9d701ecc888400ac269a55303388f0feec6f294 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 10 Mar 2017 23:37:22 -0500 Subject: [PATCH 21/31] add a Local.borrow_mut method, default_handler now takes the context token by value --- src/ctxt.rs | 10 +++++++++- src/exception.rs | 18 +++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/ctxt.rs b/src/ctxt.rs index 0a05ecb4..4650ff68 100644 --- a/src/ctxt.rs +++ b/src/ctxt.rs @@ -25,9 +25,17 @@ where } /// Acquires a reference to the context local data - pub fn borrow<'a>(&'static self, _ctxt: &'a Ctxt) -> &'a T { + pub fn borrow<'ctxt>(&'static self, _ctxt: &'ctxt Ctxt) -> &'ctxt T { unsafe { &*self.data.get() } } + + /// Acquires a mutable reference to the context local data + pub fn borrow_mut<'ctxt>( + &'static self, + _ctxt: &'ctxt mut Ctxt, + ) -> &'ctxt mut T { + unsafe { &mut *self.data.get() } + } } unsafe impl Sync for Local diff --git a/src/exception.rs b/src/exception.rs index d32d61f3..2c4b1bef 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -54,25 +54,25 @@ impl Exception { #[repr(C)] pub struct Handlers { /// Non-maskable interrupt - pub nmi: unsafe extern "C" fn(&Nmi), + pub nmi: unsafe extern "C" fn(Nmi), /// All class of fault - pub hard_fault: unsafe extern "C" fn(&HardFault), + pub hard_fault: unsafe extern "C" fn(HardFault), /// Memory management - pub mem_manage: unsafe extern "C" fn(&MemManage), + pub mem_manage: unsafe extern "C" fn(MemManage), /// Pre-fetch fault, memory access fault - pub bus_fault: unsafe extern "C" fn(&BusFault), + pub bus_fault: unsafe extern "C" fn(BusFault), /// Undefined instruction or illegal state - pub usage_fault: unsafe extern "C" fn(&UsageFault), + pub usage_fault: unsafe extern "C" fn(UsageFault), /// Reserved spots in the vector table pub _reserved0: [Reserved; 4], /// System service call via SWI instruction - pub svcall: unsafe extern "C" fn(&Svcall), + pub svcall: unsafe extern "C" fn(Svcall), /// Reserved spots in the vector table pub _reserved1: [Reserved; 2], /// Pendable request for system service - pub pendsv: unsafe extern "C" fn(&Pendsv), + pub pendsv: unsafe extern "C" fn(Pendsv), /// System tick timer - pub sys_tick: unsafe extern "C" fn(&SysTick), + pub sys_tick: unsafe extern "C" fn(SysTick), } /// Non-maskable interrupt @@ -150,7 +150,7 @@ pub const DEFAULT_HANDLERS: Handlers = Handlers { // This needs asm!, #[naked] and unreachable() to avoid modifying the stack // pointer (MSP), that way it points to the stacked registers #[naked] -pub unsafe extern "C" fn default_handler(_token: &T) { +pub unsafe extern "C" fn default_handler(_token: T) { // This is the actual exception handler. `_sf` is a pointer to the previous // stack frame #[cfg(target_arch = "arm")] From 6db72a50f302f6bd3974488a9d681dac26cd72c2 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 19:41:24 -0500 Subject: [PATCH 22/31] add Mutex.borrow_mut method --- src/interrupt.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/interrupt.rs b/src/interrupt.rs index 6d84dec8..2aff77eb 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -19,6 +19,11 @@ impl Mutex { pub fn borrow<'cs>(&self, _ctxt: &'cs CriticalSection) -> &'cs T { unsafe { &*self.inner.get() } } + + /// Mutably borrows the data for the duration of the critical section + pub fn borrow_mut<'cs>(&self, _ctxt: &'cs mut CriticalSection) -> &'cs mut T { + unsafe { &mut *self.inner.get() } + } } /// Interrupt number @@ -75,14 +80,14 @@ pub struct CriticalSection { /// This as also known as a "critical section". pub fn free(f: F) -> R where - F: FnOnce(&CriticalSection) -> R, + F: FnOnce(CriticalSection) -> R, { let primask = ::register::primask::read(); // disable interrupts disable(); - let r = f(&CriticalSection { _0: () }); + let r = f(CriticalSection { _0: () }); // If the interrupts were active before our `disable` call, then re-enable // them. Otherwise, keep them disabled From e666c0b79eb520e16c75bef4cfc085c1dfe72b06 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 19:47:57 -0500 Subject: [PATCH 23/31] make ITM functions operate on `Stim` push synchronization duties to the caller --- src/exception.rs | 2 +- src/interrupt.rs | 4 ++-- src/itm.rs | 26 +++++++++++--------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index 2c4b1bef..451332e1 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -3,7 +3,7 @@ use ctxt::Context; use Reserved; -/// Kind of exception +/// Enumeration of all exceptions #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Exception { /// i.e. currently not servicing an exception diff --git a/src/interrupt.rs b/src/interrupt.rs index 2aff77eb..6c843016 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -34,7 +34,7 @@ pub unsafe trait Nr { unsafe impl Sync for Mutex {} -/// Disable interrupts, globally +/// Disables all interrupts #[inline(always)] pub fn disable() { match () { @@ -51,7 +51,7 @@ pub fn disable() { } } -/// Enable interrupts, globally +/// Enables all the interrupts #[inline(always)] pub fn enable() { match () { diff --git a/src/itm.rs b/src/itm.rs index d4fae5d4..4c49d149 100644 --- a/src/itm.rs +++ b/src/itm.rs @@ -43,39 +43,35 @@ unsafe fn write_words(stim: &Stim, bytes: &[u32]) { } } -struct Itm { - port: u8, -} +struct Port<'p>(&'p Stim); -impl fmt::Write for Itm { +impl<'p> fmt::Write for Port<'p> { fn write_str(&mut self, s: &str) -> fmt::Result { - write_all(self.port, s.as_bytes()); + write_all(self.0, s.as_bytes()); Ok(()) } } /// Writes a `buffer` to the ITM `port` -pub fn write_all(port: u8, buffer: &[u8]) { - let stim = unsafe { &(*::peripheral::ITM.get()).stim[port as usize] }; - +pub fn write_all(port: &Stim, buffer: &[u8]) { if buffer.len() < 7 { - write_bytes(stim, buffer); + write_bytes(port, buffer); } else { let (head, body, tail) = unsafe { split(buffer) }; - write_bytes(stim, head); - unsafe { write_words(stim, body) } - write_bytes(stim, tail); + write_bytes(port, head); + unsafe { write_words(port, body) } + write_bytes(port, tail); } } /// Writes `fmt::Arguments` to the ITM `port` -pub fn write_fmt(port: u8, args: fmt::Arguments) { +pub fn write_fmt(port: &Stim, args: fmt::Arguments) { use core::fmt::Write; - Itm { port }.write_fmt(args).ok(); + Port(port).write_fmt(args).ok(); } /// Writes a string to the ITM `port` -pub fn write_str(port: u8, string: &str) { +pub fn write_str(port: &Stim, string: &str) { write_all(port, string.as_bytes()) } From 9f2374a01b5a9f5720fc86adb710f52cf51ffe4e Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 19:54:51 -0500 Subject: [PATCH 24/31] update the changelog --- CHANGELOG.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdf3795e..dd278f72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added -- Semihosting functionality is now exposed via the "semihosting" Cargo feature. +- Semihosting functionality in the `semihosting` module. - `exception::Handlers` struct that represent the section of the vector table that contains the exception handlers. @@ -18,11 +18,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - A high level API for the NVIC peripheral. -- Execution context primitives: execution context `Local` data and `Token`s to - identify execution contexts. +- Context local data. -- A `borrow` method to `Mutex` that replaces `lock`. This method returns a `&-` - reference. +- `borrow`/`borrow_mut` methods to `Mutex` that replace `lock`. + +- API and macros to send bytes / (formatted) strings through ITM ### Changed @@ -33,7 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). and are no longer `Sync`. - [breaking-change] `interrupt::free`'s closure now includes a critical section - token, `CsCtxt`. + token, `CriticalSection`. - [breaking-change] the core register API has been revamped for type safety. @@ -48,8 +48,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `vector_table` and its associated `struct`, `VectorTable`. It's not a good idea to give people a simple way to call the exception handlers. -- `Mutex`'s `lock` method as it's unsound. You can use it to get multiple `&mut - -` references to the data. +- `Mutex`'s `lock` method as it's unsound. You could use it to get multiple + `&mut -` references to the wrapped data. ## [v0.1.6] - 2017-01-22 From e1a67e7a238f5814028956af421636759888d740 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 19:57:33 -0500 Subject: [PATCH 25/31] update CI scripts --- ci/install.sh | 7 ++++++- ci/script.sh | 2 -- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ci/install.sh b/ci/install.sh index dd8cd261..550a548e 100644 --- a/ci/install.sh +++ b/ci/install.sh @@ -4,11 +4,16 @@ main() { curl https://sh.rustup.rs -sSf | \ sh -s -- -y --default-toolchain $TRAVIS_RUST_VERSION + local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ + | cut -d/ -f3 \ + | grep -E '^v[0-9.]+$' \ + | sort --version-sort \ + | tail -n1) curl -LSfs http://japaric.github.io/trust/install.sh | \ sh -s -- \ --force \ --git japaric/cross \ - --tag v0.1.2 \ + --tag $tag \ --target x86_64-unknown-linux-musl \ --to ~/.cargo/bin } diff --git a/ci/script.sh b/ci/script.sh index 82b3a1d1..5b836d23 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,8 +1,6 @@ set -ex main() { - cargo generate-lockfile - case $TARGET in thumbv*-none-eabi*) cross build --target $TARGET From 511d3c90107b4ae5c1b952bbd4597254f39b3474 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 20:54:10 -0500 Subject: [PATCH 26/31] add documentation to the ctxt module --- src/ctxt.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/src/ctxt.rs b/src/ctxt.rs index 4650ff68..05e578b6 100644 --- a/src/ctxt.rs +++ b/src/ctxt.rs @@ -1,4 +1,95 @@ -//! Interrupt / Exception context +//! Interrupt / Exception context local data +//! +//! The main use case is safely adding state to exception / interrupt handlers. +//! +//! This is done in two stages, first you define a token that will appear in the +//! interrupt handler signature; each handler will have its unique token. This +//! token must be zero sized type because interrupt handlers' real signature is +//! `fn()` and it must also implement the `Context` trait. You must also make +//! sure that the token can't be constructed outside of the crate where it's +//! defined. +//! +//! ``` +//! // This must be in a library crate +//! /// Token unique to the TIM7 interrupt handler +//! pub struct Tim7 { _0: () } +//! +//! unsafe impl Context for Tim7 {} +//! ``` +//! +//! Then in the application one can pin data to the interrupt handler using +//! `Local`. +//! +//! ``` +//! // omitted: how to put this handler in the vector table +//! extern "C" fn tim7(ctxt: Tim7) { +//! static STATE: Local, Tim7> = Local::new(Cell::new(false)); +//! +//! let state = STATE.borrow(&ctxt); +//! +//! // toggle state +//! state.set(!state.get()); +//! +//! if state.get() { +//! // something +//! } else { +//! // something else +//! } +//! } +//! ``` +//! +//! Note that due to the uniqueness of tokens, other handlers won't be able to +//! access context local data. (Given that you got the signatures right) +//! +//! ``` +//! static TIM3_DATA: Local, Tim3> +//! +//! extern "C" fn tim3(ctxt: Tim3) { +//! let data = TIM3_DATA.borrow(&ctxt); +//! } +//! +//! extern "C" fn tim4(ctxt: Tim4) { +//! //let data = TIM3_DATA.borrow(&ctxt); +//! // ^ wouldn't work +//! } +//! ``` +//! +//! To have the application use these tokenized function signatures, you can +//! define, in a library, a `Handlers` struct that represents the vector table: +//! +//! ``` +//! #[repr(C)] +//! pub struct Handlers { +//! tim1: extern "C" fn(Tim1), +//! tim2: extern "C" fn(Tim2), +//! tim3: extern "C" fn(Tim3), +//! tim4: extern "C" fn(Tim4), +//! .. +//! } +//! +//! pub const DEFAULT_HANDLERS: Handlers = Handlers { +//! tim1: default_handler, +//! tim2: default_handler, +//! tim3: default_handler, +//! tim4: default_handler, +//! .. +//! } +//! ``` +//! +//! Then have the user use that `struct` to register the interrupt handlers: +//! +//! ``` +//! extern "C" fn tim3(ctxt: Tim3) { .. } +//! +//! // override the TIM3 interrupt handler +//! #[no_mangle] +//! static _INTERRUPTS: Handlers = Handlers { +//! tim3: tim3, ..DEFAULT_HANDLERS +//! }; +//! ``` +//! +//! This pattern is implemented for exceptions in this crate. See +//! `exception::Handlers` and `exception::DEFAULT_HANDLERS`. use core::marker::PhantomData; use core::cell::UnsafeCell; From 23c2ee2561d54243a15484b1456b548b2408eadd Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 20:54:32 -0500 Subject: [PATCH 27/31] ci: manually generate lockfile --- ci/script.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/script.sh b/ci/script.sh index 5b836d23..82b3a1d1 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,6 +1,8 @@ set -ex main() { + cargo generate-lockfile + case $TARGET in thumbv*-none-eabi*) cross build --target $TARGET From 6d4478d448423d402aeabb1fefafb8dad619e889 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 20:58:50 -0500 Subject: [PATCH 28/31] remove unsafe from exception::default_handler --- src/exception.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index 451332e1..82401dfb 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -54,25 +54,25 @@ impl Exception { #[repr(C)] pub struct Handlers { /// Non-maskable interrupt - pub nmi: unsafe extern "C" fn(Nmi), + pub nmi: extern "C" fn(Nmi), /// All class of fault - pub hard_fault: unsafe extern "C" fn(HardFault), + pub hard_fault: extern "C" fn(HardFault), /// Memory management - pub mem_manage: unsafe extern "C" fn(MemManage), + pub mem_manage: extern "C" fn(MemManage), /// Pre-fetch fault, memory access fault - pub bus_fault: unsafe extern "C" fn(BusFault), + pub bus_fault: extern "C" fn(BusFault), /// Undefined instruction or illegal state - pub usage_fault: unsafe extern "C" fn(UsageFault), + pub usage_fault: extern "C" fn(UsageFault), /// Reserved spots in the vector table pub _reserved0: [Reserved; 4], /// System service call via SWI instruction - pub svcall: unsafe extern "C" fn(Svcall), + pub svcall: extern "C" fn(Svcall), /// Reserved spots in the vector table pub _reserved1: [Reserved; 2], /// Pendable request for system service - pub pendsv: unsafe extern "C" fn(Pendsv), + pub pendsv: extern "C" fn(Pendsv), /// System tick timer - pub sys_tick: unsafe extern "C" fn(SysTick), + pub sys_tick: extern "C" fn(SysTick), } /// Non-maskable interrupt @@ -123,6 +123,8 @@ unsafe impl Context for MemManage {} unsafe impl Context for BusFault {} +unsafe impl Context for UsageFault {} + unsafe impl Context for Svcall {} unsafe impl Context for Pendsv {} @@ -150,7 +152,9 @@ pub const DEFAULT_HANDLERS: Handlers = Handlers { // This needs asm!, #[naked] and unreachable() to avoid modifying the stack // pointer (MSP), that way it points to the stacked registers #[naked] -pub unsafe extern "C" fn default_handler(_token: T) { +pub extern "C" fn default_handler(_token: T) + where T: Context +{ // This is the actual exception handler. `_sf` is a pointer to the previous // stack frame #[cfg(target_arch = "arm")] From d6f953458d3a6c58c190a81dc370c528342df60c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 21:08:59 -0500 Subject: [PATCH 29/31] pin to an older nightly cf rust-lang/cargo#3819 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2e86e059..721b6c73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ dist: trusty language: rust -rust: nightly +rust: nightly-2017-03-04 services: docker sudo: required From 0856d2c1d5d14d17014949a1d5c0a7c25e1e5bb0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 21:38:50 -0500 Subject: [PATCH 30/31] fix doctests --- src/ctxt.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/ctxt.rs b/src/ctxt.rs index 05e578b6..727090b7 100644 --- a/src/ctxt.rs +++ b/src/ctxt.rs @@ -10,6 +10,7 @@ //! defined. //! //! ``` +//! # use cortex_m::ctxt::Context; //! // This must be in a library crate //! /// Token unique to the TIM7 interrupt handler //! pub struct Tim7 { _0: () } @@ -21,6 +22,11 @@ //! `Local`. //! //! ``` +//! # #![feature(const_fn)] +//! # use std::cell::Cell; +//! # use cortex_m::ctxt::{Context, Local}; +//! # struct Tim7; +//! # unsafe impl Context for Tim7 {} //! // omitted: how to put this handler in the vector table //! extern "C" fn tim7(ctxt: Tim7) { //! static STATE: Local, Tim7> = Local::new(Cell::new(false)); @@ -42,7 +48,12 @@ //! access context local data. (Given that you got the signatures right) //! //! ``` -//! static TIM3_DATA: Local, Tim3> +//! # #![feature(const_fn)] +//! # use std::cell::Cell; +//! # use cortex_m::ctxt::{Context, Local}; +//! # struct Tim3; +//! # struct Tim4; +//! static TIM3_DATA: Local, Tim3> = Local::new(Cell::new(false)); //! //! extern "C" fn tim3(ctxt: Tim3) { //! let data = TIM3_DATA.borrow(&ctxt); @@ -52,19 +63,26 @@ //! //let data = TIM3_DATA.borrow(&ctxt); //! // ^ wouldn't work //! } +//! # unsafe impl Context for Tim3 {} +//! # fn main() {} //! ``` //! //! To have the application use these tokenized function signatures, you can //! define, in a library, a `Handlers` struct that represents the vector table: //! //! ``` +//! # struct Tim1; +//! # struct Tim2; +//! # struct Tim3; +//! # struct Tim4; +//! # extern "C" fn default_handler(_: T) {} //! #[repr(C)] //! pub struct Handlers { //! tim1: extern "C" fn(Tim1), //! tim2: extern "C" fn(Tim2), //! tim3: extern "C" fn(Tim3), //! tim4: extern "C" fn(Tim4), -//! .. +//! /* .. */ //! } //! //! pub const DEFAULT_HANDLERS: Handlers = Handlers { @@ -72,14 +90,17 @@ //! tim2: default_handler, //! tim3: default_handler, //! tim4: default_handler, -//! .. -//! } +//! /* .. */ +//! }; //! ``` //! //! Then have the user use that `struct` to register the interrupt handlers: //! //! ``` -//! extern "C" fn tim3(ctxt: Tim3) { .. } +//! # struct Tim3; +//! # struct Handlers { tim3: extern "C" fn(Tim3), tim4: extern "C" fn(Tim3) } +//! # const DEFAULT_HANDLERS: Handlers = Handlers { tim3: tim3, tim4: tim3 }; +//! extern "C" fn tim3(ctxt: Tim3) { /* .. */ } //! //! // override the TIM3 interrupt handler //! #[no_mangle] From 173b5bc9750909408cfcd6140ddbf8a119dc7a18 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 11 Mar 2017 23:15:39 -0500 Subject: [PATCH 31/31] add unsafe block --- src/exception.rs | 19 +++++++++++-------- src/interrupt.rs | 5 ++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index 82401dfb..f5c8c288 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -153,7 +153,8 @@ pub const DEFAULT_HANDLERS: Handlers = Handlers { // pointer (MSP), that way it points to the stacked registers #[naked] pub extern "C" fn default_handler(_token: T) - where T: Context +where + T: Context, { // This is the actual exception handler. `_sf` is a pointer to the previous // stack frame @@ -167,16 +168,18 @@ pub extern "C" fn default_handler(_token: T) match () { #[cfg(target_arch = "arm")] () => { - // "trampoline" to get to the real exception handler. - asm!("mrs r0, MSP + unsafe { + // "trampoline" to get to the real exception handler. + asm!("mrs r0, MSP ldr r1, [r0, #20] b $0" - : - : "i"(handler as extern "C" fn(&StackedRegisters) -> !) - : - : "volatile"); + : + : "i"(handler as extern "C" fn(&StackedRegisters) -> !) + : + : "volatile"); - ::core::intrinsics::unreachable() + ::core::intrinsics::unreachable() + } } #[cfg(not(target_arch = "arm"))] () => {} diff --git a/src/interrupt.rs b/src/interrupt.rs index 6c843016..e11fdc3c 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -21,7 +21,10 @@ impl Mutex { } /// Mutably borrows the data for the duration of the critical section - pub fn borrow_mut<'cs>(&self, _ctxt: &'cs mut CriticalSection) -> &'cs mut T { + pub fn borrow_mut<'cs>( + &self, + _ctxt: &'cs mut CriticalSection, + ) -> &'cs mut T { unsafe { &mut *self.inner.get() } } }