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}