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}