//! Chip-Generic Interrupt Utilities //! //! For the most part, [crate::interrupt::free] is what you want: //! //! ``` //! avr_device::interrupt::free(|cs| { //! // Interrupts are disabled here //! }); //! ``` //! //! To access shared state, Mutex can be used: //! //! ``` //! use avr_device::interrupt::Mutex; //! use core::cell::Cell; //! //! // Use Cell, if the wrapped type is Copy. //! // Use RefCell, if the wrapped type is not Copy or if you need a reference to it for other reasons. //! static MYGLOBAL: Mutex> = Mutex::new(Cell::new(0)); //! //! fn my_fun() { //! avr_device::interrupt::free(|cs| { //! // Interrupts are disabled here //! //! // Acquire mutex to global variable. //! let myglobal_ref = MYGLOBAL.borrow(cs); //! // Write to the global variable. //! myglobal_ref.set(42); //! }); //! } //! ``` pub use bare_metal::{CriticalSection, Mutex}; #[cfg(target_arch = "avr")] use core::arch::asm; /// Opaque structure for storing the global interrupt flag status. /// /// This structure does not implement `Copy` and `Clone`, /// because the user shall not duplicate and pass it twice to [crate::interrupt::restore]. #[derive(Debug)] #[cfg_attr(feature = "ufmt", derive(ufmt::derive::uDebug))] pub struct IrqFlag { // The saved SREG. sreg: u8, } impl IrqFlag { #[inline(always)] fn new(sreg: u8) -> IrqFlag { IrqFlag { sreg, } } /// Check the status of the saved global interrupt flag. /// /// Returns true, if the saved global interrupt flag is set (IRQs enabled). /// Otherwise returns false. /// /// This method can be used to check whether interrupts were enabled /// before the [crate::interrupt::disable_save] call. /// You probably shouldn't make your program behavior dependent on this state. /// Consider using a different design. #[inline(always)] pub fn enabled(&self) -> bool { self.sreg & 0x80 != 0 } } /// Disable the global interrupt flag. /// /// *Hint*: Most of the time you probably don't want to use this function directly. /// Consider creating a critical section with [crate::interrupt::free] instead. /// /// This function is an optimization fence. /// That means memory accesses will not be re-ordered by the compiler across this function call. #[inline(always)] pub fn disable() { cfg_if::cfg_if! { if #[cfg(target_arch = "avr")] { // Disable interrupts unsafe { asm!("cli") }; } else { unimplemented!() } } } /// Disable the global interrupt flag and return an opaque representation of the previous flag status. /// /// *Hint*: Most of the time you probably don't want to use this function directly. /// Consider creating a critical section with [crate::interrupt::free] instead. /// /// This function is an optimization fence. /// That means memory accesses will not be re-ordered by the compiler across this function call. /// /// Returns an object that contains the status of the global interrupt flag from *before* the `disable_save()` call. /// This object shall later be passed to the [crate::interrupt::restore] function. #[inline(always)] #[allow(unreachable_code)] pub fn disable_save() -> IrqFlag { let sreg; cfg_if::cfg_if! { if #[cfg(target_arch = "avr")] { // Store current state unsafe { asm!( "in {sreg}, 0x3F", sreg = out(reg) sreg, ) }; } else { let _ = sreg; unimplemented!() } } // Disable interrupts disable(); IrqFlag::new(sreg) } /// Enable the global interrupt flag. /// /// *Warning*: This function enables interrupts, no matter what the enable-state was before [crate::interrupt::disable]. /// Especially in library code, where the previous interrupt state may be unknown, /// this function call shall be avoided. /// Most of the time you probably don't want to use this function directly. /// Consider creating a critical section with [crate::interrupt::free] instead. /// /// This function is an optimization fence. /// That means memory accesses will not be re-ordered by the compiler across this function call. /// /// # Safety /// /// - Do not call this function inside an [crate::interrupt::free] critical section #[inline(always)] pub unsafe fn enable() { cfg_if::cfg_if! { if #[cfg(target_arch = "avr")] { asm!("sei"); } else { unimplemented!() } } } /// Restore the global interrupt flag to its previous state before [crate::interrupt::disable_save]. /// /// *Hint*: Most of the time you probably don't want to use this function directly. /// Consider creating a critical section with [crate::interrupt::free] instead. /// /// This function is an optimization fence. /// That means memory accesses will not be re-ordered by the compiler across this function call. /// /// # Safety /// /// - If you call this function inside of a [crate::interrupt::free] critical section, the /// corresponding [crate::interrupt::disable_save] must also be in the same critical section. /// - If you nest multiple [crate::interrupt::disable_save] + [crate::interrupt::restore] /// sequences, the [crate::interrupt::restore] must be called in the reverse order of the /// [crate::interrupt::disable_save] call order. /// That means the first saved IrqFlag must be restored last. #[inline(always)] pub unsafe fn restore(irq_flag: IrqFlag) { cfg_if::cfg_if! { if #[cfg(target_arch = "avr")] { // Restore global interrupt flag in SREG. // This also clobbers all other bits in SREG. asm!( "out 0x3F, {sreg}", sreg = in(reg) irq_flag.sreg, ); } else { let _ = irq_flag; unimplemented!() } } } /// Check whether the global interrupt flag is currently enabled (in SREG). /// /// *Warning*: You shouldn't use this to hand craft your own memory/interrupt safety mechanisms. /// This function may be used for things such as deciding whether to do /// expensive calculations in library code, or similar things. /// /// This function is **not** an optimization fence. /// That means memory accesses *can* be re-ordered by the compiler across this function call. #[inline(always)] #[allow(unreachable_code)] pub fn is_enabled() -> bool { let sreg; cfg_if::cfg_if! { if #[cfg(target_arch = "avr")] { // Store current state unsafe { asm!( "in {sreg}, 0x3F", sreg = out(reg) sreg, options(readonly, preserves_flags, nostack), ) }; } else { let _ = sreg; unimplemented!() } } IrqFlag::new(sreg).enabled() } /// Execute closure `f` in an interrupt-free context. /// /// This as also known as a "critical section". #[inline(always)] pub fn free(f: F) -> R where F: FnOnce(CriticalSection) -> R, { cfg_if::cfg_if! { if #[cfg(target_arch = "avr")] { // Disable interrupts. This is an optimization fence. let irq_flag = disable_save(); let r = f(unsafe { CriticalSection::new() }); // Restore interrupt state. This is an optimization fence. unsafe { restore(irq_flag); } r } else { let _ = f; unimplemented!() } } }