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}