diff options
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | src/exception.rs | 73 | ||||
-rw-r--r-- | src/interrupt.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 29 | ||||
-rw-r--r-- | src/macros.rs | 29 | ||||
-rw-r--r-- | src/peripheral/nvic.rs | 109 |
6 files changed, 234 insertions, 19 deletions
@@ -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 7046080..fad3ea9 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 4305071..035dcd4 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -26,6 +26,12 @@ impl<T> Mutex<T> { } } +/// 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<T> Sync for Mutex<T> {} @@ -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<Handler>; 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<T>(r: &T) -> usize { r as *const T as usize diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..bd31167 --- /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 4570c0f..64adc90 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<u32>; 16], - reserved0: [u32; 16], + pub iser: [RW<u32>; 8], + reserved0: [u32; 24], /// Interrupt Clear-Enable - pub icer: [RW<u32>; 16], - reserved1: [u32; 16], + pub icer: [RW<u32>; 8], + reserved1: [u32; 24], /// Interrupt Set-Pending - pub ispr: [RW<u32>; 16], - reserved2: [u32; 16], + pub ispr: [RW<u32>; 8], + reserved2: [u32; 24], /// Interrupt Clear-Pending - pub icpr: [RW<u32>; 16], - reserved3: [u32; 16], + pub icpr: [RW<u32>; 8], + reserved3: [u32; 24], /// Interrupt Active Bit - pub iabr: [RO<u32>; 16], - reserved4: [u32; 48], + pub iabr: [RO<u32>; 8], + reserved4: [u32; 56], /// Interrupt Priority - pub ipr: [RW<u32>; 124], + pub ipr: [RW<u8>; 240], +} + +impl Registers { + /// Clears `interrupt` pending state + pub fn clear_pending<I>(&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<I>(&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<I>(&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<I>(&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<I>(&self, interrupt: I) -> bool + where I: Nr + { + let nr = interrupt.nr(); + let mask = 1 << (nr % 32); + + (self.iabr[usize::from(nr / 32)].read() & mask) == mask + } + + /// Checks if `interrupt` is enabled + pub fn is_enabled<I>(&self, interrupt: I) -> bool + where I: Nr + { + let nr = interrupt.nr(); + let mask = 1 << (nr % 32); + + (self.iser[usize::from(nr / 32)].read() & mask) == mask + } + + /// Checks if `interrupt` is pending + pub fn is_pending<I>(&self, interrupt: I) -> bool + where I: Nr + { + let nr = interrupt.nr(); + let mask = 1 << (nr % 32); + + (self.ispr[usize::from(nr / 32)].read() & mask) == mask + } + + /// Forces `interrupt` into pending state + pub fn set_pending<I>(&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<I>(&mut self, interrupt: I, prio: u8) + where I: Nr + { + let nr = interrupt.nr(); + + self.ipr[usize::from(nr / 4)].write(prio); + } } |