Skip to main content

cortex_m/peripheral/
nvic.rs

1//! Nested Vector Interrupt Controller
2
3use volatile_register::RW;
4#[cfg(not(armv6m))]
5use volatile_register::{RO, WO};
6
7use crate::interrupt::InterruptNumber;
8use crate::peripheral::NVIC;
9
10/// Register block
11#[repr(C)]
12pub struct RegisterBlock {
13    /// Interrupt Set-Enable
14    pub iser: [RW<u32>; NVIC::BITMAP_WORDS],
15
16    _reserved0: [u32; NVIC::BITMAP_WORDS],
17
18    /// Interrupt Clear-Enable
19    pub icer: [RW<u32>; NVIC::BITMAP_WORDS],
20
21    _reserved1: [u32; NVIC::BITMAP_WORDS],
22
23    /// Interrupt Set-Pending
24    pub ispr: [RW<u32>; NVIC::BITMAP_WORDS],
25
26    _reserved2: [u32; NVIC::BITMAP_WORDS],
27
28    /// Interrupt Clear-Pending
29    pub icpr: [RW<u32>; NVIC::BITMAP_WORDS],
30
31    _reserved3: [u32; NVIC::BITMAP_WORDS],
32
33    /// Interrupt Active Bit (not present on Cortex-M0 variants)
34    #[cfg(not(armv6m))]
35    pub iabr: [RO<u32>; NVIC::BITMAP_WORDS],
36    #[cfg(armv6m)]
37    _reserved4: [u32; NVIC::BITMAP_WORDS],
38
39    _reserved5: [u32; NVIC::BITMAP_WORDS],
40
41    #[cfg(armv8m)]
42    /// Interrupt Target Non-secure (only present on Arm v8-M)
43    pub itns: [RW<u32>; NVIC::BITMAP_WORDS],
44    #[cfg(not(armv8m))]
45    _reserved6: [u32; NVIC::BITMAP_WORDS],
46
47    _reserved7: [u32; NVIC::BITMAP_WORDS],
48
49    /// Interrupt Priority
50    ///
51    /// On ARMv7-M, 124 word-sized registers are available. Each of those
52    /// contains of 4 interrupt priorities of 8 byte each.The architecture
53    /// specifically allows accessing those along byte boundaries, so they are
54    /// represented as 496 byte-sized registers, for convenience, and to allow
55    /// atomic priority updates.
56    ///
57    /// On ARMv6-M, the registers must only be accessed along word boundaries,
58    /// so convenient byte-sized representation wouldn't work on that
59    /// architecture.
60    #[cfg(not(armv6m))]
61    pub ipr: [RW<u8>; 496],
62
63    /// Interrupt Priority
64    ///
65    /// On ARMv7-M, 124 word-sized registers are available. Each of those
66    /// contains of 4 interrupt priorities of 8 byte each.The architecture
67    /// specifically allows accessing those along byte boundaries, so they are
68    /// represented as 496 byte-sized registers, for convenience, and to allow
69    /// atomic priority updates.
70    ///
71    /// On ARMv6-M, the registers must only be accessed along word boundaries,
72    /// so convenient byte-sized representation wouldn't work on that
73    /// architecture.
74    #[cfg(armv6m)]
75    pub ipr: [RW<u32>; 8],
76
77    #[cfg(not(armv6m))]
78    _reserved8: [u32; 580],
79
80    /// Software Trigger Interrupt
81    #[cfg(not(armv6m))]
82    pub stir: WO<u32>,
83}
84
85impl NVIC {
86    /// Request an IRQ in software
87    ///
88    /// Writing a value to the INTID field is the same as manually pending an interrupt by setting
89    /// the corresponding interrupt bit in an Interrupt Set Pending Register. This is similar to
90    /// [`NVIC::pend`].
91    ///
92    /// This method is not available on ARMv6-M chips.
93    ///
94    /// [`NVIC::pend`]: #method.pend
95    #[cfg(not(armv6m))]
96    #[inline]
97    pub fn request<I>(&mut self, interrupt: I)
98    where
99        I: InterruptNumber,
100    {
101        let nr = interrupt.number();
102
103        unsafe {
104            self.stir.write(u32::from(nr));
105        }
106    }
107
108    /// Disables `interrupt`
109    #[inline]
110    pub fn mask<I>(interrupt: I)
111    where
112        I: InterruptNumber,
113    {
114        let nr = interrupt.number();
115        let (idx, mask) = Self::reg_index_and_mask(nr);
116        // NOTE(unsafe) this is a write to a stateless register
117        unsafe { (*Self::PTR).icer[idx].write(mask) }
118    }
119
120    /// Enables `interrupt`
121    ///
122    /// This function is `unsafe` because it can break mask-based critical sections
123    #[inline]
124    pub unsafe fn unmask<I>(interrupt: I)
125    where
126        I: InterruptNumber,
127    {
128        unsafe {
129            let nr = interrupt.number();
130            let (idx, mask) = Self::reg_index_and_mask(nr);
131            // NOTE(ptr) this is a write to a stateless register
132            (*Self::PTR).iser[idx].write(mask)
133        }
134    }
135
136    /// Returns the NVIC priority of `interrupt`
137    ///
138    /// *NOTE* NVIC encodes priority in the highest bits of a byte so values like `1` and `2` map
139    /// to the same priority. Also for NVIC priorities, a lower value (e.g. `16`) has higher
140    /// priority (urgency) than a larger value (e.g. `32`).
141    #[inline]
142    pub fn get_priority<I>(interrupt: I) -> u8
143    where
144        I: InterruptNumber,
145    {
146        #[cfg(not(armv6m))]
147        {
148            let nr = interrupt.number();
149            // NOTE(unsafe) atomic read with no side effects
150            unsafe { (*Self::PTR).ipr[usize::from(nr)].read() }
151        }
152
153        #[cfg(armv6m)]
154        {
155            // NOTE(unsafe) atomic read with no side effects
156            let ipr_n = unsafe { (*Self::PTR).ipr[Self::ipr_index(interrupt)].read() };
157            let prio = (ipr_n >> Self::ipr_shift(interrupt)) & 0x0000_00ff;
158            prio as u8
159        }
160    }
161
162    /// Is `interrupt` active or pre-empted and stacked
163    #[cfg(not(armv6m))]
164    #[inline]
165    pub fn is_active<I>(interrupt: I) -> bool
166    where
167        I: InterruptNumber,
168    {
169        let nr = interrupt.number();
170        let (idx, mask) = Self::reg_index_and_mask(nr);
171
172        // NOTE(unsafe) atomic read with no side effects
173        unsafe { ((*Self::PTR).iabr[idx].read() & mask) == mask }
174    }
175
176    /// Checks if `interrupt` is enabled
177    #[inline]
178    pub fn is_enabled<I>(interrupt: I) -> bool
179    where
180        I: InterruptNumber,
181    {
182        let nr = interrupt.number();
183        let (idx, mask) = Self::reg_index_and_mask(nr);
184
185        // NOTE(unsafe) atomic read with no side effects
186        unsafe { ((*Self::PTR).iser[idx].read() & mask) == mask }
187    }
188
189    /// Checks if `interrupt` is pending
190    #[inline]
191    pub fn is_pending<I>(interrupt: I) -> bool
192    where
193        I: InterruptNumber,
194    {
195        let nr = interrupt.number();
196        let (idx, mask) = Self::reg_index_and_mask(nr);
197
198        // NOTE(unsafe) atomic read with no side effects
199        unsafe { ((*Self::PTR).ispr[idx].read() & mask) == mask }
200    }
201
202    /// Forces `interrupt` into pending state
203    #[inline]
204    pub fn pend<I>(interrupt: I)
205    where
206        I: InterruptNumber,
207    {
208        let nr = interrupt.number();
209        let (idx, mask) = Self::reg_index_and_mask(nr);
210
211        // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
212        unsafe { (*Self::PTR).ispr[idx].write(mask) }
213    }
214
215    /// Sets the "priority" of `interrupt` to `prio`
216    ///
217    /// *NOTE* See [`get_priority`](struct.NVIC.html#method.get_priority) method for an explanation
218    /// of how NVIC priorities work.
219    ///
220    /// On ARMv6-M, updating an interrupt priority requires a read-modify-write operation. On
221    /// ARMv7-M, the operation is performed in a single atomic write operation.
222    ///
223    /// # Unsafety
224    ///
225    /// Changing priority levels can break priority-based critical sections (see
226    /// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
227    #[inline]
228    pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8)
229    where
230        I: InterruptNumber,
231    {
232        unsafe {
233            #[cfg(not(armv6m))]
234            {
235                let nr = interrupt.number();
236                self.ipr[usize::from(nr)].write(prio)
237            }
238
239            #[cfg(armv6m)]
240            {
241                self.ipr[Self::ipr_index(interrupt)].modify(|value| {
242                    let mask = 0x0000_00ff << Self::ipr_shift(interrupt);
243                    let prio = u32::from(prio) << Self::ipr_shift(interrupt);
244
245                    (value & !mask) | prio
246                })
247            }
248        }
249    }
250
251    /// Clears `interrupt`'s pending state
252    #[inline]
253    pub fn unpend<I>(interrupt: I)
254    where
255        I: InterruptNumber,
256    {
257        let nr = interrupt.number();
258        let (idx, mask) = Self::reg_index_and_mask(nr);
259
260        // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
261        unsafe { (*Self::PTR).icpr[idx].write(mask) }
262    }
263
264    /// Route `interrupt` to the Non-Secure world (ARMv8-M only).
265    ///
266    /// Sets the corresponding ITNS bit so the interrupt is taken as Non-Secure
267    /// and will not preempt Secure execution. Call this for every peripheral
268    /// interrupt handled by the Non-Secure application before jumping to it.
269    ///
270    /// # Safety
271    /// Must be called from the Secure world. Routing an interrupt to Non-Secure
272    /// while Secure handlers depend on it can violate security invariants.
273    #[cfg(armv8m)]
274    #[inline]
275    pub unsafe fn route_to_nonsecure<I>(&mut self, interrupt: I)
276    where
277        I: InterruptNumber,
278    {
279        let nr = interrupt.number();
280        let (group_idx, bit_mask) = Self::reg_index_and_mask(nr);
281        unsafe { self.itns[group_idx].modify(|v| v | bit_mask) }
282    }
283
284    /// Route `interrupt` back to the Secure world (ARMv8-M only).
285    ///
286    /// Clears the corresponding ITNS bit. After this call the interrupt
287    /// targets Secure state (the default after reset).
288    ///
289    /// # Safety
290    /// Must be called from the Secure world.
291    #[cfg(armv8m)]
292    #[inline]
293    pub unsafe fn route_to_secure<I>(&mut self, interrupt: I)
294    where
295        I: InterruptNumber,
296    {
297        let nr = interrupt.number();
298        let (group_idx, bit_mask) = Self::reg_index_and_mask(nr);
299        unsafe { self.itns[group_idx].modify(|v| v & !bit_mask) }
300    }
301
302    /// Returns `true` if `interrupt` is routed to the Non-Secure world (ARMv8-M only).
303    #[cfg(armv8m)]
304    #[inline]
305    pub fn is_routed_to_nonsecure<I>(interrupt: I) -> bool
306    where
307        I: InterruptNumber,
308    {
309        let nr = interrupt.number();
310        let (group_idx, bit_mask) = Self::reg_index_and_mask(nr);
311        // NOTE(unsafe) atomic read with no side effects
312        unsafe { ((*Self::PTR).itns[group_idx].read() & bit_mask) == bit_mask }
313    }
314
315    /// Words in each of the NVIC's interrupt bitmaps: `ISER`, `ICER`, `ISPR`, `ICPR`, `IABR`
316    /// and `ITNS`.
317    const BITMAP_WORDS: usize = 16;
318
319    /// Which 32-bit word of an interrupt bitmap holds `nr`'s bit.
320    ///
321    /// The `&` helps remove an expensive bounds check - we know all valid interrupt numbers will be in range.
322    #[inline]
323    const fn reg_index_and_mask(irq: u16) -> (usize, u32) {
324        debug_assert!((irq as usize) < (Self::BITMAP_WORDS * 32));
325        let idx = (irq / 32) as usize & (Self::BITMAP_WORDS - 1);
326        let mask = 1 << (irq % 32);
327        (idx, mask)
328    }
329
330    #[cfg(armv6m)]
331    #[inline]
332    fn ipr_index<I>(interrupt: I) -> usize
333    where
334        I: InterruptNumber,
335    {
336        usize::from(interrupt.number()) / 4
337    }
338
339    #[cfg(armv6m)]
340    #[inline]
341    fn ipr_shift<I>(interrupt: I) -> usize
342    where
343        I: InterruptNumber,
344    {
345        (usize::from(interrupt.number()) % 4) * 8
346    }
347}