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}