avr_device/
interrupt.rs

1//! Chip-Generic Interrupt Utilities
2//!
3//! For the most part, [crate::interrupt::free] is what you want:
4//!
5//! ```
6//! avr_device::interrupt::free(|cs| {
7//!     // Interrupts are disabled here
8//! });
9//! ```
10//!
11//! To access shared state, Mutex can be used:
12//!
13//! ```
14//! use avr_device::interrupt::Mutex;
15//! use core::cell::Cell;
16//!
17//! // Use Cell, if the wrapped type is Copy.
18//! // Use RefCell, if the wrapped type is not Copy or if you need a reference to it for other reasons.
19//! static MYGLOBAL: Mutex<Cell<u16>> = Mutex::new(Cell::new(0));
20//!
21//! fn my_fun() {
22//!     avr_device::interrupt::free(|cs| {
23//!         // Interrupts are disabled here
24//!
25//!         // Acquire mutex to global variable.
26//!         let myglobal_ref = MYGLOBAL.borrow(cs);
27//!         // Write to the global variable.
28//!         myglobal_ref.set(42);
29//!     });
30//! }
31//! ```
32
33pub use bare_metal::{CriticalSection, Mutex};
34
35#[cfg(target_arch = "avr")]
36use core::arch::asm;
37
38/// Opaque structure for storing the global interrupt flag status.
39///
40/// This structure does not implement `Copy` and `Clone`,
41/// because the user shall not duplicate and pass it twice to [crate::interrupt::restore].
42#[derive(Debug)]
43#[cfg_attr(feature = "ufmt", derive(ufmt::derive::uDebug))]
44pub struct IrqFlag {
45    // The saved SREG.
46    sreg: u8,
47}
48
49impl IrqFlag {
50    #[inline(always)]
51    fn new(sreg: u8) -> IrqFlag {
52        IrqFlag { sreg }
53    }
54
55    /// Check the status of the saved global interrupt flag.
56    ///
57    /// Returns true, if the saved global interrupt flag is set (IRQs enabled).
58    /// Otherwise returns false.
59    ///
60    /// This method can be used to check whether interrupts were enabled
61    /// before the [crate::interrupt::disable_save] call.
62    /// You probably shouldn't make your program behavior dependent on this state.
63    /// Consider using a different design.
64    #[inline(always)]
65    pub fn enabled(&self) -> bool {
66        self.sreg & 0x80 != 0
67    }
68}
69
70/// Disable the global interrupt flag.
71///
72/// *Hint*: Most of the time you probably don't want to use this function directly.
73///         Consider creating a critical section with [crate::interrupt::free] instead.
74///
75/// This function is an optimization fence.
76/// That means memory accesses will not be re-ordered by the compiler across this function call.
77#[inline(always)]
78pub fn disable() {
79    cfg_if::cfg_if! {
80        if #[cfg(target_arch = "avr")] {
81            // Disable interrupts
82            unsafe { asm!("cli") };
83        } else {
84            unimplemented!()
85        }
86    }
87}
88
89/// Disable the global interrupt flag and return an opaque representation of the previous flag status.
90///
91/// *Hint*: Most of the time you probably don't want to use this function directly.
92///         Consider creating a critical section with [crate::interrupt::free] instead.
93///
94/// This function is an optimization fence.
95/// That means memory accesses will not be re-ordered by the compiler across this function call.
96///
97/// Returns an object that contains the status of the global interrupt flag from *before* the `disable_save()` call.
98/// This object shall later be passed to the [crate::interrupt::restore] function.
99#[inline(always)]
100#[allow(unreachable_code)]
101pub fn disable_save() -> IrqFlag {
102    let sreg;
103    cfg_if::cfg_if! {
104        if #[cfg(target_arch = "avr")] {
105            // Store current state
106            unsafe {
107                asm!(
108                    "in {sreg}, 0x3F",
109                    sreg = out(reg) sreg,
110                )
111            };
112        } else {
113            let _ = sreg;
114            unimplemented!()
115        }
116    }
117    // Disable interrupts
118    disable();
119
120    IrqFlag::new(sreg)
121}
122
123/// Enable the global interrupt flag.
124///
125/// *Warning*: This function enables interrupts, no matter what the enable-state was before [crate::interrupt::disable].
126///            Especially in library code, where the previous interrupt state may be unknown,
127///            this function call shall be avoided.
128///            Most of the time you probably don't want to use this function directly.
129///            Consider creating a critical section with [crate::interrupt::free] instead.
130///
131/// This function is an optimization fence.
132/// That means memory accesses will not be re-ordered by the compiler across this function call.
133///
134/// # Safety
135///
136/// - Do not call this function inside an [crate::interrupt::free] critical section
137#[inline(always)]
138pub unsafe fn enable() {
139    cfg_if::cfg_if! {
140        if #[cfg(target_arch = "avr")] {
141            asm!("sei");
142        } else {
143            unimplemented!()
144        }
145    }
146}
147
148/// Restore the global interrupt flag to its previous state before [crate::interrupt::disable_save].
149///
150/// *Hint*: Most of the time you probably don't want to use this function directly.
151///         Consider creating a critical section with [crate::interrupt::free] instead.
152///
153/// This function is an optimization fence.
154/// That means memory accesses will not be re-ordered by the compiler across this function call.
155///
156/// # Safety
157///
158/// - If you call this function inside of a [crate::interrupt::free] critical section, the
159///   corresponding [crate::interrupt::disable_save] must also be in the same critical section.
160/// - If you nest multiple [crate::interrupt::disable_save] + [crate::interrupt::restore]
161///   sequences, the [crate::interrupt::restore] must be called in the reverse order of the
162///   [crate::interrupt::disable_save] call order.
163///   That means the first saved IrqFlag must be restored last.
164#[inline(always)]
165pub unsafe fn restore(irq_flag: IrqFlag) {
166    cfg_if::cfg_if! {
167        if #[cfg(target_arch = "avr")] {
168            // Restore global interrupt flag in SREG.
169            // This also clobbers all other bits in SREG.
170            asm!(
171                "out 0x3F, {sreg}",
172                sreg = in(reg) irq_flag.sreg,
173            );
174        } else {
175            let _ = irq_flag;
176            unimplemented!()
177        }
178    }
179}
180
181/// Check whether the global interrupt flag is currently enabled (in SREG).
182///
183/// *Warning*: You shouldn't use this to hand craft your own memory/interrupt safety mechanisms.
184///            This function may be used for things such as deciding whether to do
185///            expensive calculations in library code, or similar things.
186///
187/// This function is **not** an optimization fence.
188/// That means memory accesses *can* be re-ordered by the compiler across this function call.
189#[inline(always)]
190#[allow(unreachable_code)]
191pub fn is_enabled() -> bool {
192    let sreg;
193    cfg_if::cfg_if! {
194        if #[cfg(target_arch = "avr")] {
195            // Store current state
196            unsafe {
197                asm!(
198                    "in {sreg}, 0x3F",
199                    sreg = out(reg) sreg,
200                    options(readonly, preserves_flags, nostack),
201                )
202            };
203        } else {
204            let _ = sreg;
205            unimplemented!()
206        }
207    }
208
209    IrqFlag::new(sreg).enabled()
210}
211
212/// Execute closure `f` in an interrupt-free context.
213///
214/// This is also known as a "critical section".
215#[inline(always)]
216pub fn free<F, R>(f: F) -> R
217where
218    F: FnOnce(CriticalSection) -> R,
219{
220    cfg_if::cfg_if! {
221        if #[cfg(target_arch = "avr")] {
222            // Disable interrupts. This is an optimization fence.
223            let irq_flag = disable_save();
224
225            let r = f(unsafe { CriticalSection::new() });
226
227            // Restore interrupt state. This is an optimization fence.
228            unsafe { restore(irq_flag); }
229
230            r
231        } else {
232            let _ = f;
233            unimplemented!()
234        }
235    }
236}
237
238#[cfg(feature = "critical-section-impl")]
239mod cs {
240    use critical_section::RawRestoreState;
241
242    struct AvrCriticalSection;
243    critical_section::set_impl!(AvrCriticalSection);
244
245    unsafe impl critical_section::Impl for AvrCriticalSection {
246        unsafe fn acquire() -> RawRestoreState {
247            crate::interrupt::disable_save().sreg
248        }
249
250        unsafe fn release(restore_state: RawRestoreState) {
251            crate::interrupt::restore(crate::interrupt::IrqFlag::new(restore_state))
252        }
253    }
254}