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; 48],
40
41 /// Interrupt Priority
42 ///
43 /// On ARMv7-M, 124 word-sized registers are available. Each of those
44 /// contains of 4 interrupt priorities of 8 byte each.The architecture
45 /// specifically allows accessing those along byte boundaries, so they are
46 /// represented as 496 byte-sized registers, for convenience, and to allow
47 /// atomic priority updates.
48 ///
49 /// On ARMv6-M, the registers must only be accessed along word boundaries,
50 /// so convenient byte-sized representation wouldn't work on that
51 /// architecture.
52 #[cfg(not(armv6m))]
53 pub ipr: [RW<u8>; 496],
54
55 /// Interrupt Priority
56 ///
57 /// On ARMv7-M, 124 word-sized registers are available. Each of those
58 /// contains of 4 interrupt priorities of 8 byte each.The architecture
59 /// specifically allows accessing those along byte boundaries, so they are
60 /// represented as 496 byte-sized registers, for convenience, and to allow
61 /// atomic priority updates.
62 ///
63 /// On ARMv6-M, the registers must only be accessed along word boundaries,
64 /// so convenient byte-sized representation wouldn't work on that
65 /// architecture.
66 #[cfg(armv6m)]
67 pub ipr: [RW<u32>; 8],
68
69 #[cfg(not(armv6m))]
70 _reserved6: [u32; 580],
71
72 /// Software Trigger Interrupt
73 #[cfg(not(armv6m))]
74 pub stir: WO<u32>,
75}
76
77impl NVIC {
78 /// Request an IRQ in software
79 ///
80 /// Writing a value to the INTID field is the same as manually pending an interrupt by setting
81 /// the corresponding interrupt bit in an Interrupt Set Pending Register. This is similar to
82 /// [`NVIC::pend`].
83 ///
84 /// This method is not available on ARMv6-M chips.
85 ///
86 /// [`NVIC::pend`]: #method.pend
87 #[cfg(not(armv6m))]
88 #[inline]
89 pub fn request<I>(&mut self, interrupt: I)
90 where
91 I: InterruptNumber,
92 {
93 let nr = interrupt.number();
94
95 unsafe {
96 self.stir.write(u32::from(nr));
97 }
98 }
99
100 /// Disables `interrupt`
101 #[inline]
102 pub fn mask<I>(interrupt: I)
103 where
104 I: InterruptNumber,
105 {
106 let nr = interrupt.number();
107 // NOTE(unsafe) this is a write to a stateless register
108 unsafe { (*Self::PTR).icer[usize::from(nr / 32)].write(1 << (nr % 32)) }
109 }
110
111 /// Enables `interrupt`
112 ///
113 /// This function is `unsafe` because it can break mask-based critical sections
114 #[inline]
115 pub unsafe fn unmask<I>(interrupt: I)
116 where
117 I: InterruptNumber,
118 {
119 let nr = interrupt.number();
120 // NOTE(ptr) this is a write to a stateless register
121 (*Self::PTR).iser[usize::from(nr / 32)].write(1 << (nr % 32))
122 }
123
124 /// Returns the NVIC priority of `interrupt`
125 ///
126 /// *NOTE* NVIC encodes priority in the highest bits of a byte so values like `1` and `2` map
127 /// to the same priority. Also for NVIC priorities, a lower value (e.g. `16`) has higher
128 /// priority (urgency) than a larger value (e.g. `32`).
129 #[inline]
130 pub fn get_priority<I>(interrupt: I) -> u8
131 where
132 I: InterruptNumber,
133 {
134 #[cfg(not(armv6m))]
135 {
136 let nr = interrupt.number();
137 // NOTE(unsafe) atomic read with no side effects
138 unsafe { (*Self::PTR).ipr[usize::from(nr)].read() }
139 }
140
141 #[cfg(armv6m)]
142 {
143 // NOTE(unsafe) atomic read with no side effects
144 let ipr_n = unsafe { (*Self::PTR).ipr[Self::ipr_index(interrupt)].read() };
145 let prio = (ipr_n >> Self::ipr_shift(interrupt)) & 0x0000_00ff;
146 prio as u8
147 }
148 }
149
150 /// Is `interrupt` active or pre-empted and stacked
151 #[cfg(not(armv6m))]
152 #[inline]
153 pub fn is_active<I>(interrupt: I) -> bool
154 where
155 I: InterruptNumber,
156 {
157 let nr = interrupt.number();
158 let mask = 1 << (nr % 32);
159
160 // NOTE(unsafe) atomic read with no side effects
161 unsafe { ((*Self::PTR).iabr[usize::from(nr / 32)].read() & mask) == mask }
162 }
163
164 /// Checks if `interrupt` is enabled
165 #[inline]
166 pub fn is_enabled<I>(interrupt: I) -> bool
167 where
168 I: InterruptNumber,
169 {
170 let nr = interrupt.number();
171 let mask = 1 << (nr % 32);
172
173 // NOTE(unsafe) atomic read with no side effects
174 unsafe { ((*Self::PTR).iser[usize::from(nr / 32)].read() & mask) == mask }
175 }
176
177 /// Checks if `interrupt` is pending
178 #[inline]
179 pub fn is_pending<I>(interrupt: I) -> bool
180 where
181 I: InterruptNumber,
182 {
183 let nr = interrupt.number();
184 let mask = 1 << (nr % 32);
185
186 // NOTE(unsafe) atomic read with no side effects
187 unsafe { ((*Self::PTR).ispr[usize::from(nr / 32)].read() & mask) == mask }
188 }
189
190 /// Forces `interrupt` into pending state
191 #[inline]
192 pub fn pend<I>(interrupt: I)
193 where
194 I: InterruptNumber,
195 {
196 let nr = interrupt.number();
197
198 // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
199 unsafe { (*Self::PTR).ispr[usize::from(nr / 32)].write(1 << (nr % 32)) }
200 }
201
202 /// Sets the "priority" of `interrupt` to `prio`
203 ///
204 /// *NOTE* See [`get_priority`](struct.NVIC.html#method.get_priority) method for an explanation
205 /// of how NVIC priorities work.
206 ///
207 /// On ARMv6-M, updating an interrupt priority requires a read-modify-write operation. On
208 /// ARMv7-M, the operation is performed in a single atomic write operation.
209 ///
210 /// # Unsafety
211 ///
212 /// Changing priority levels can break priority-based critical sections (see
213 /// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
214 #[inline]
215 pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8)
216 where
217 I: InterruptNumber,
218 {
219 #[cfg(not(armv6m))]
220 {
221 let nr = interrupt.number();
222 self.ipr[usize::from(nr)].write(prio)
223 }
224
225 #[cfg(armv6m)]
226 {
227 self.ipr[Self::ipr_index(interrupt)].modify(|value| {
228 let mask = 0x0000_00ff << Self::ipr_shift(interrupt);
229 let prio = u32::from(prio) << Self::ipr_shift(interrupt);
230
231 (value & !mask) | prio
232 })
233 }
234 }
235
236 /// Clears `interrupt`'s pending state
237 #[inline]
238 pub fn unpend<I>(interrupt: I)
239 where
240 I: InterruptNumber,
241 {
242 let nr = interrupt.number();
243
244 // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
245 unsafe { (*Self::PTR).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) }
246 }
247
248 #[cfg(armv6m)]
249 #[inline]
250 fn ipr_index<I>(interrupt: I) -> usize
251 where
252 I: InterruptNumber,
253 {
254 usize::from(interrupt.number()) / 4
255 }
256
257 #[cfg(armv6m)]
258 #[inline]
259 fn ipr_shift<I>(interrupt: I) -> usize
260 where
261 I: InterruptNumber,
262 {
263 (usize::from(interrupt.number()) % 4) * 8
264 }
265}