1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
/* oserror
*
* Developed by Tim Walls <tim.walls@snowgoons.com>
* Copyright (c) All Rights Reserved, Tim Walls
*/
//! A way of communicating fatal OS errors that doesn't have all the
//! baggage that Rust loads Panic with, but still allows for useful debugging.
//!
//! You can denote a pin (typically an error LED or something similar that
//! you can hook up a meter to), which will be used to signal error
//! conditions as a series of pulses.
//!
//! When the OS triggers these conditions it is fatal; it will sit in a loop
//! flashing the given pin until the device is reset. If you need to
//! automatically reset after an error, use the Watchdog device to arrange
//! this.
//!
//! # Setting the debug pin to use
//! The debug pin to use can be set dynamically at runtime using the
//! `set_debug_pin` method:
//!
//! ```rust,no_run
//! # fn set_debug_pin() {
//! avr_oxide::oserror::set_debug_pin(avr_oxide::hal::atmega4809::port::porte::pin(2));
//! # }
//! ```
// Imports ===================================================================
use avr_oxide::hal::generic::port::{InterruptMode, Pin, PinMode};
use avr_oxide::util::debug;
use core::ops::{ControlFlow, Try};
// Declarations ==============================================================
static mut DEBUGPIN : Option<&'static dyn Pin> = None;
/// A type used for returning and propagating errors; this implementation
/// differs from `core::result::Result` by having an `unwrap()`
/// implementation that doesn't require Debug and importing a thousand
/// dependencies we can't afford on AVR.
#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
pub enum OxideResult<T,E> {
Ok(T),
Err(E)
}
#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
#[derive(Copy,Clone,PartialEq)]
pub enum OsError {
/// There is not enough memory to allocate a heap.
NotEnoughRam,
/// Ran out of memory for dynamic allocation.
OutOfMemory,
/// A thread has overflowed its allocated stack.
StackOverflow,
/// An interrupt or other kernel routine has overflowed its allocated stack.
KernelStackOverflow,
/// A kernel memory guard has been corrupted
KernelGuardCrashed,
/// It was impossible to schedule a thread for execution, because all
/// threads have died or are deadlocked.
NoSchedulableThreads,
/// Some kind of 'impossible' internal error condition has arisen...
InternalError,
/// An attempt to make an illegal thread state change has taken place
BadThreadState,
/// The maximum number of threads has been reached
OutOfThreads,
/// Call to free a block of memory twice
DoubleFree,
/// Bad parameters were passed to a system call
BadParams,
/// Calls to inhibit sleep/standby nested too deep
NestedInhibitTooDeep,
/// Arithmetic overflow/underflow
Arithmetic,
/// A call to the default panic!() handler was made
Panic,
/// A call to yield() was made that is not permitted (typically, yield
/// while interrupts are disabled.)
CannotYield,
/// The event queue for Oxide events overflowed
OxideEventOverflow,
/// You dropped a supposedly undroppable [`StaticWrap`]
///
/// [`StaticWrap`]: avr_oxide::StaticWrap
StaticDropped,
/// You violated the borrowing rules of a [`StaticWrap`]
///
/// [`StaticWrap`]: avr_oxide::StaticWrap
StaticBorrow,
/// The application called `panic_if_error!` on an Error
UnwrapError,
/// The application called `panic_if_none!` on a None
UnwrapNone
}
// Code ======================================================================
pub trait OxideTryFrom<T>
where
T: Sized
{
type Error;
fn oxide_try_from(_: T) -> OxideResult<Self, Self::Error> where Self: Sized;
}
impl<T,E> OxideResult<T,E> {
pub const fn is_ok(&self) -> bool {
match self {
OxideResult::Ok(_) => true,
OxideResult::Err(_) => false
}
}
pub const fn is_err(&self) -> bool {
match self {
OxideResult::Ok(_) => false,
OxideResult::Err(_) => true
}
}
pub fn unwrap(self) -> T {
match self {
OxideResult::Ok(t) => t,
OxideResult::Err(_) => {
halt(OsError::UnwrapError);
}
}
}
pub fn expect_err(self) -> E {
match self {
OxideResult::Ok(_) => {
halt(OsError::UnwrapError)
}
OxideResult::Err(e) => e
}
}
pub fn unwrap_or(self, default: T) -> T {
match self {
OxideResult::Ok(t) => t,
OxideResult::Err(_) => default
}
}
pub fn unwrap_or_else<F: FnOnce(E)->T>(self, op: F) -> T {
match self {
OxideResult::Ok(t) => t,
OxideResult::Err(e) => { (op)(e) }
}
}
}
impl<T,E> Try for OxideResult<T,E> {
type Output = T;
type Residual = OxideResult<core::convert::Infallible, E>;
#[inline]
fn from_output(output: Self::Output) -> Self {
OxideResult::Ok(output)
}
#[inline]
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
OxideResult::Ok(t) => ControlFlow::Continue(t),
OxideResult::Err(e) => ControlFlow::Break(OxideResult::Err(e))
}
}
}
impl<T, E, F: From<E>> const core::ops::FromResidual<OxideResult<core::convert::Infallible, E>>
for OxideResult<T, F>
{
#[inline]
fn from_residual(residual: OxideResult<core::convert::Infallible, E>) -> Self {
match residual {
OxideResult::Err(e) => return OxideResult::Err(From::from(e)),
OxideResult::Ok(_) => { panic!() }
}
}
}
impl<T, E> core::ops::Residual<T> for OxideResult<core::convert::Infallible, E> {
type TryType = OxideResult<T, E>;
}
impl OsError {
/// Turn this error into a bit representation of the pulse train we should
/// send to the debug pin to represent this error.
fn pulse_train(&self) -> u32 {
match self {
Self::NotEnoughRam => 0b_0000_0000_0000_0000_0000_0000_0000_0001,
Self::OutOfMemory => 0b_0000_0000_0000_0000_0000_0000_0000_0101,
Self::StackOverflow => 0b_0000_0000_0000_0000_0000_0000_0001_0101,
Self::KernelStackOverflow => 0b_0000_0000_0000_0000_0000_0001_0101_0111,
Self::KernelGuardCrashed => 0b_0000_0000_0000_0000_0000_0111_0101_0111,
Self::OxideEventOverflow => 0b_0000_0000_0000_0000_0001_0101_0111_0111,
Self::NoSchedulableThreads => 0b_0000_0000_0000_0000_0000_0000_0101_0101,
Self::CannotYield => 0b_0000_0000_0000_0000_0000_0101_0101_0111,
Self::InternalError => 0b_0000_0000_0000_0000_0000_0001_0101_0101,
Self::BadThreadState => 0b_0000_0000_0000_0000_0000_0101_0111_0101,
Self::OutOfThreads => 0b_0000_0000_0000_0000_0000_0101_0101_0101,
Self::DoubleFree => 0b_0000_0000_0000_0000_0001_0101_0101_0101,
Self::BadParams => 0b_0000_0000_0000_0000_0000_0000_0001_1101,
Self::NestedInhibitTooDeep => 0b_0000_0000_0000_0001_0101_0101_0101_0101,
Self::Arithmetic => 0b_0000_0000_0000_0000_0000_0001_1101_1101,
Self::Panic => 0b_0000_0000_0000_0000_0001_1101_1101_1101,
Self::StaticDropped => 0b_0000_0000_0000_0000_0000_0000_0111_0101,
Self::StaticBorrow => 0b_0000_0000_0000_0000_0000_0001_0111_0101,
Self::UnwrapError => 0b_0000_0000_0000_0000_0101_1101_1101_1101,
Self::UnwrapNone => 0b_0000_0000_0000_0001_0101_1101_1101_1101,
}
}
}
/**
* Set the pin which will be used when an OS error occurs to transmit the
* error code.
*
* ### Default pin for Arduino
* If avr_oxide is built with the `arduino` feature, the default pin
* corresponding to the Arduino's debug LED will already be set, and it is
* not necessary to call this function. (It is still permissible to do so
* to use an alternative pin if desired, however.)
*/
pub fn set_debug_pin(pin: &'static dyn Pin) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
unsafe {
core::ptr::replace(&mut DEBUGPIN, Some(pin));
}
})
}
/**
* Halt execution with the given OS error.
*
* The pulse code for the given error will be continuously sent on the debugging
* pin previously set using the `set_debug_pin` method.
*
* ### Hardware Debuggers
* A `break` instruction is included between pulse trains, meaning a device
* with a hardware debugger/ICE attached should drop to the debugger.
*/
#[cfg(target_arch="avr")]
pub fn halt(error: OsError) -> ! {
unsafe {
avr_oxide::concurrency::interrupt::disable_interrupts();
#[cfg(feature="panicout")]
{
debug::print("\r\n\nHALT: ");
debug::print_u32(error.pulse_train());
debug::print("\r\n");
debug::print_thread_state();
}
// We want the flash-rate to be *roughly* invariant to the clock-speed and
// powersaving mode. But only roughly :-).
const DELAYLOOP : u32 = (avr_oxide::deviceconsts::clock::MASTER_CLOCK_HZ / 100) / avr_oxide::deviceconsts::clock::MASTER_CLOCK_PRESCALER as u32;
match DEBUGPIN {
Some(pin) => {
let pulse_code = error.pulse_train();
// Set up the pin as an output
pin.set_mode(PinMode::Output);
pin.set_interrupt_mode(InterruptMode::Disabled);
pin.set_low();
loop {
let mut mask : u32 = 1u32;
for _i in 0..32 {
if pulse_code & mask > 0 {
pin.set_high();
} else {
pin.set_low();
}
mask <<= 1;
for _d in 0..DELAYLOOP {
core::arch::asm!("nop");
}
}
// If there is a debugger attached, it'd be nice to break
core::arch::asm!("break");
}
},
None => {
// No pin to toggle; just go into a busy loop (sleeping as much
// as possible for want of battery life. If there is a debugger
// attached, we'll fall into it on the Break.
core::arch::asm!(
"break",
"0: ", "sleep",
"jmp 0b",
options(noreturn)
);
}
}
}
}
#[cfg(not(target_arch="avr"))]
pub fn halt(error: OsError) -> ! {
panic!("OS Error: {:?}", error);
}
/**
* A macro you can use as a substitute for .unwrap() on Options that will simply
* panic if the result is None. You will want this to avoid hauling in all of
* the garbage associated with the Debug and Formatter types that comes
* along with the 'real' unwrap() implementation. With small EEPROMs/Flash
* we don't have the room for that luxury.
*/
#[macro_export]
macro_rules! panic_if_none {
($result:expr) => {
match $result {
Some(value) => value,
None => { avr_oxide::oserror::halt(avr_oxide::oserror::OsError::UnwrapNone); }
}
};
($result:expr,$paniccode:expr) => {
match $result {
Some(value) => value,
None => { avr_oxide::oserror::halt($paniccode); }
}
};
}
// Tests =====================================================================