1 | //! Nested Vector Interrupt Controller |
2 | |
3 | use volatile_register::RW; |
4 | #[cfg (not(armv6m))] |
5 | use volatile_register::{RO, WO}; |
6 | |
7 | use crate::interrupt::InterruptNumber; |
8 | use crate::peripheral::NVIC; |
9 | |
10 | /// Register block |
11 | #[repr (C)] |
12 | pub 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 | |
77 | impl 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 | } |
266 | |