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
//! 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<Cell<u16>> = 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 is also known as a "critical section".
#[inline(always)]
pub fn free<F, R>(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!()
}
}
}
#[cfg(feature = "critical-section-impl")]
mod cs {
use critical_section::RawRestoreState;
struct AvrCriticalSection;
critical_section::set_impl!(AvrCriticalSection);
unsafe impl critical_section::Impl for AvrCriticalSection {
unsafe fn acquire() -> RawRestoreState {
crate::interrupt::disable_save().sreg
}
unsafe fn release(restore_state: RawRestoreState) {
crate::interrupt::restore(crate::interrupt::IrqFlag::new(restore_state))
}
}
}