| 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 | |