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>; 16],
15
16    _reserved0: [u32; 16],
17
18    /// Interrupt Clear-Enable
19    pub icer: [RW<u32>; 16],
20
21    _reserved1: [u32; 16],
22
23    /// Interrupt Set-Pending
24    pub ispr: [RW<u32>; 16],
25
26    _reserved2: [u32; 16],
27
28    /// Interrupt Clear-Pending
29    pub icpr: [RW<u32>; 16],
30
31    _reserved3: [u32; 16],
32
33    /// Interrupt Active Bit (not present on Cortex-M0 variants)
34    #[cfg(not(armv6m))]
35    pub iabr: [RO<u32>; 16],
36    #[cfg(armv6m)]
37    _reserved4: [u32; 16],
38
39    _reserved5: [u32; 16],
40
41    #[cfg(armv8m)]
42    /// Interrupt Target Non-secure (only present on Arm v8-M)
43    pub itns: [RW<u32>; 16],
44    #[cfg(not(armv8m))]
45    _reserved6: [u32; 16],
46
47    _reserved7: [u32; 16],
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        // NOTE(unsafe) this is a write to a stateless register
116        unsafe { (*Self::PTR).icer[usize::from(nr / 32)].write(1 << (nr % 32)) }
117    }
118
119    /// Enables `interrupt`
120    ///
121    /// This function is `unsafe` because it can break mask-based critical sections
122    #[inline]
123    pub unsafe fn unmask<I>(interrupt: I)
124    where
125        I: InterruptNumber,
126    {
127        unsafe {
128            let nr = interrupt.number();
129            // NOTE(ptr) this is a write to a stateless register
130            (*Self::PTR).iser[usize::from(nr / 32)].write(1 << (nr % 32))
131        }
132    }
133
134    /// Returns the NVIC priority of `interrupt`
135    ///
136    /// *NOTE* NVIC encodes priority in the highest bits of a byte so values like `1` and `2` map
137    /// to the same priority. Also for NVIC priorities, a lower value (e.g. `16`) has higher
138    /// priority (urgency) than a larger value (e.g. `32`).
139    #[inline]
140    pub fn get_priority<I>(interrupt: I) -> u8
141    where
142        I: InterruptNumber,
143    {
144        #[cfg(not(armv6m))]
145        {
146            let nr = interrupt.number();
147            // NOTE(unsafe) atomic read with no side effects
148            unsafe { (*Self::PTR).ipr[usize::from(nr)].read() }
149        }
150
151        #[cfg(armv6m)]
152        {
153            // NOTE(unsafe) atomic read with no side effects
154            let ipr_n = unsafe { (*Self::PTR).ipr[Self::ipr_index(interrupt)].read() };
155            let prio = (ipr_n >> Self::ipr_shift(interrupt)) & 0x0000_00ff;
156            prio as u8
157        }
158    }
159
160    /// Is `interrupt` active or pre-empted and stacked
161    #[cfg(not(armv6m))]
162    #[inline]
163    pub fn is_active<I>(interrupt: I) -> bool
164    where
165        I: InterruptNumber,
166    {
167        let nr = interrupt.number();
168        let mask = 1 << (nr % 32);
169
170        // NOTE(unsafe) atomic read with no side effects
171        unsafe { ((*Self::PTR).iabr[usize::from(nr / 32)].read() & mask) == mask }
172    }
173
174    /// Checks if `interrupt` is enabled
175    #[inline]
176    pub fn is_enabled<I>(interrupt: I) -> bool
177    where
178        I: InterruptNumber,
179    {
180        let nr = interrupt.number();
181        let mask = 1 << (nr % 32);
182
183        // NOTE(unsafe) atomic read with no side effects
184        unsafe { ((*Self::PTR).iser[usize::from(nr / 32)].read() & mask) == mask }
185    }
186
187    /// Checks if `interrupt` is pending
188    #[inline]
189    pub fn is_pending<I>(interrupt: I) -> bool
190    where
191        I: InterruptNumber,
192    {
193        let nr = interrupt.number();
194        let mask = 1 << (nr % 32);
195
196        // NOTE(unsafe) atomic read with no side effects
197        unsafe { ((*Self::PTR).ispr[usize::from(nr / 32)].read() & mask) == mask }
198    }
199
200    /// Forces `interrupt` into pending state
201    #[inline]
202    pub fn pend<I>(interrupt: I)
203    where
204        I: InterruptNumber,
205    {
206        let nr = interrupt.number();
207
208        // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
209        unsafe { (*Self::PTR).ispr[usize::from(nr / 32)].write(1 << (nr % 32)) }
210    }
211
212    /// Sets the "priority" of `interrupt` to `prio`
213    ///
214    /// *NOTE* See [`get_priority`](struct.NVIC.html#method.get_priority) method for an explanation
215    /// of how NVIC priorities work.
216    ///
217    /// On ARMv6-M, updating an interrupt priority requires a read-modify-write operation. On
218    /// ARMv7-M, the operation is performed in a single atomic write operation.
219    ///
220    /// # Unsafety
221    ///
222    /// Changing priority levels can break priority-based critical sections (see
223    /// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
224    #[inline]
225    pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8)
226    where
227        I: InterruptNumber,
228    {
229        unsafe {
230            #[cfg(not(armv6m))]
231            {
232                let nr = interrupt.number();
233                self.ipr[usize::from(nr)].write(prio)
234            }
235
236            #[cfg(armv6m)]
237            {
238                self.ipr[Self::ipr_index(interrupt)].modify(|value| {
239                    let mask = 0x0000_00ff << Self::ipr_shift(interrupt);
240                    let prio = u32::from(prio) << Self::ipr_shift(interrupt);
241
242                    (value & !mask) | prio
243                })
244            }
245        }
246    }
247
248    /// Clears `interrupt`'s pending state
249    #[inline]
250    pub fn unpend<I>(interrupt: I)
251    where
252        I: InterruptNumber,
253    {
254        let nr = interrupt.number();
255
256        // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
257        unsafe { (*Self::PTR).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) }
258    }
259
260    /// Route `interrupt` to the Non-Secure world (ARMv8-M only).
261    ///
262    /// Sets the corresponding ITNS bit so the interrupt is taken as Non-Secure
263    /// and will not preempt Secure execution. Call this for every peripheral
264    /// interrupt handled by the Non-Secure application before jumping to it.
265    ///
266    /// # Safety
267    /// Must be called from the Secure world. Routing an interrupt to Non-Secure
268    /// while Secure handlers depend on it can violate security invariants.
269    #[cfg(armv8m)]
270    #[inline]
271    pub unsafe fn route_to_nonsecure<I>(&mut self, interrupt: I)
272    where
273        I: InterruptNumber,
274    {
275        let nr = interrupt.number();
276        let group_idx = usize::from(nr / 32);
277        let bit_mask = 1 << (nr % 32);
278        unsafe { self.itns[group_idx].modify(|v| v | bit_mask) }
279    }
280
281    /// Route `interrupt` back to the Secure world (ARMv8-M only).
282    ///
283    /// Clears the corresponding ITNS bit. After this call the interrupt
284    /// targets Secure state (the default after reset).
285    ///
286    /// # Safety
287    /// Must be called from the Secure world.
288    #[cfg(armv8m)]
289    #[inline]
290    pub unsafe fn route_to_secure<I>(&mut self, interrupt: I)
291    where
292        I: InterruptNumber,
293    {
294        let nr = interrupt.number();
295        let group_idx = usize::from(nr / 32);
296        let bit_mask = 1 << (nr % 32);
297        unsafe { self.itns[group_idx].modify(|v| v & !bit_mask) }
298    }
299
300    /// Returns `true` if `interrupt` is routed to the Non-Secure world (ARMv8-M only).
301    #[cfg(armv8m)]
302    #[inline]
303    pub fn is_routed_to_nonsecure<I>(interrupt: I) -> bool
304    where
305        I: InterruptNumber,
306    {
307        let nr = interrupt.number();
308        let group_idx = usize::from(nr / 32);
309        let bit_mask = 1 << (nr % 32);
310        // NOTE(unsafe) atomic read with no side effects
311        unsafe { ((*Self::PTR).itns[group_idx].read() & bit_mask) == bit_mask }
312    }
313
314    #[cfg(armv6m)]
315    #[inline]
316    fn ipr_index<I>(interrupt: I) -> usize
317    where
318        I: InterruptNumber,
319    {
320        usize::from(interrupt.number()) / 4
321    }
322
323    #[cfg(armv6m)]
324    #[inline]
325    fn ipr_shift<I>(interrupt: I) -> usize
326    where
327        I: InterruptNumber,
328    {
329        (usize::from(interrupt.number()) % 4) * 8
330    }
331}