Skip to main content

esp_hal/interrupt/
xtensa.rs

1//! Interrupt handling
2
3#[cfg(esp32)]
4pub(crate) use xtensa_lx::interrupt::free;
5
6use crate::{
7    interrupt::{PriorityError, RunLevel},
8    peripherals::Interrupt,
9};
10
11/// Enumeration of available CPU interrupts
12///
13/// It's possible to create one handler per priority level. (e.g
14/// `level1_interrupt`)
15#[derive(Debug, Copy, Clone)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[repr(u32)]
18#[instability::unstable]
19pub enum CpuInterrupt {
20    /// Level-triggered interrupt with priority 1.
21    Interrupt0LevelPriority1      = 0,
22    /// Level-triggered interrupt with priority 1.
23    Interrupt1LevelPriority1      = 1,
24    /// Level-triggered interrupt with priority 1.
25    Interrupt2LevelPriority1      = 2,
26    /// Level-triggered interrupt with priority 1.
27    Interrupt3LevelPriority1      = 3,
28    /// Level-triggered interrupt with priority 1.
29    Interrupt4LevelPriority1      = 4,
30    /// Level-triggered interrupt with priority 1.
31    Interrupt5LevelPriority1      = 5,
32    /// Timer 0 interrupt with priority 1.
33    Interrupt6Timer0Priority1     = 6,
34    /// Software-triggered interrupt with priority 1.
35    Interrupt7SoftwarePriority1   = 7,
36    /// Level-triggered interrupt with priority 1.
37    Interrupt8LevelPriority1      = 8,
38    /// Level-triggered interrupt with priority 1.
39    Interrupt9LevelPriority1      = 9,
40    /// Edge-triggered interrupt with priority 1.
41    Interrupt10EdgePriority1      = 10,
42    /// Profiling-related interrupt with priority 3.
43    Interrupt11ProfilingPriority3 = 11,
44    /// Level-triggered interrupt with priority 1.
45    Interrupt12LevelPriority1     = 12,
46    /// Level-triggered interrupt with priority 1.
47    Interrupt13LevelPriority1     = 13,
48    /// Timer 1 interrupt with priority 3.
49    Interrupt15Timer1Priority3    = 15,
50    /// Level-triggered interrupt with priority 1.
51    Interrupt17LevelPriority1     = 17,
52    /// Level-triggered interrupt with priority 1.
53    Interrupt18LevelPriority1     = 18,
54    /// Level-triggered interrupt with priority 2.
55    Interrupt19LevelPriority2     = 19,
56    /// Level-triggered interrupt with priority 2.
57    Interrupt20LevelPriority2     = 20,
58    /// Level-triggered interrupt with priority 2.
59    Interrupt21LevelPriority2     = 21,
60    /// Edge-triggered interrupt with priority 3.
61    Interrupt22EdgePriority3      = 22,
62    /// Level-triggered interrupt with priority 3.
63    Interrupt23LevelPriority3     = 23,
64    /// Level-triggered interrupt with priority 3.
65    Interrupt27LevelPriority3     = 27,
66    /// Software-triggered interrupt with priority 3.
67    Interrupt29SoftwarePriority3  = 29,
68    // TODO: re-add higher level interrupts
69}
70
71impl CpuInterrupt {
72    #[cfg(feature = "rt")]
73    pub(super) fn from_u32(n: u32) -> Option<Self> {
74        match n {
75            0 => Some(Self::Interrupt0LevelPriority1),
76            1 => Some(Self::Interrupt1LevelPriority1),
77            2 => Some(Self::Interrupt2LevelPriority1),
78            3 => Some(Self::Interrupt3LevelPriority1),
79            4 => Some(Self::Interrupt4LevelPriority1),
80            5 => Some(Self::Interrupt5LevelPriority1),
81            6 => Some(Self::Interrupt6Timer0Priority1),
82            7 => Some(Self::Interrupt7SoftwarePriority1),
83            8 => Some(Self::Interrupt8LevelPriority1),
84            9 => Some(Self::Interrupt9LevelPriority1),
85            10 => Some(Self::Interrupt10EdgePriority1),
86            11 => Some(Self::Interrupt11ProfilingPriority3),
87            12 => Some(Self::Interrupt12LevelPriority1),
88            13 => Some(Self::Interrupt13LevelPriority1),
89            15 => Some(Self::Interrupt15Timer1Priority3),
90            17 => Some(Self::Interrupt17LevelPriority1),
91            18 => Some(Self::Interrupt18LevelPriority1),
92            19 => Some(Self::Interrupt19LevelPriority2),
93            20 => Some(Self::Interrupt20LevelPriority2),
94            21 => Some(Self::Interrupt21LevelPriority2),
95            22 => Some(Self::Interrupt22EdgePriority3),
96            23 => Some(Self::Interrupt23LevelPriority3),
97            27 => Some(Self::Interrupt27LevelPriority3),
98            29 => Some(Self::Interrupt29SoftwarePriority3),
99            _ => None,
100        }
101    }
102
103    #[inline]
104    #[cfg(feature = "rt")]
105    pub(crate) fn is_vectored(self) -> bool {
106        // Even "direct bound" interrupts go through the vectored interrupt handler
107        true
108    }
109
110    /// Enable the CPU interrupt
111    #[inline]
112    #[instability::unstable]
113    pub fn enable(self) {
114        enable_cpu_interrupt_raw(self as u32);
115    }
116
117    /// Clear the CPU interrupt status bit
118    #[inline]
119    #[instability::unstable]
120    pub fn clear(self) {
121        unsafe { xtensa_lx::interrupt::clear(1 << self as u32) };
122    }
123
124    /// Get interrupt priority for the CPU
125    #[inline]
126    #[instability::unstable]
127    pub fn priority(self) -> Priority {
128        match self {
129            CpuInterrupt::Interrupt0LevelPriority1
130            | CpuInterrupt::Interrupt1LevelPriority1
131            | CpuInterrupt::Interrupt2LevelPriority1
132            | CpuInterrupt::Interrupt3LevelPriority1
133            | CpuInterrupt::Interrupt4LevelPriority1
134            | CpuInterrupt::Interrupt5LevelPriority1
135            | CpuInterrupt::Interrupt6Timer0Priority1
136            | CpuInterrupt::Interrupt7SoftwarePriority1
137            | CpuInterrupt::Interrupt8LevelPriority1
138            | CpuInterrupt::Interrupt9LevelPriority1
139            | CpuInterrupt::Interrupt10EdgePriority1
140            | CpuInterrupt::Interrupt12LevelPriority1
141            | CpuInterrupt::Interrupt13LevelPriority1
142            | CpuInterrupt::Interrupt17LevelPriority1
143            | CpuInterrupt::Interrupt18LevelPriority1 => Priority::Priority1,
144
145            CpuInterrupt::Interrupt19LevelPriority2
146            | CpuInterrupt::Interrupt20LevelPriority2
147            | CpuInterrupt::Interrupt21LevelPriority2 => Priority::Priority2,
148
149            CpuInterrupt::Interrupt11ProfilingPriority3
150            | CpuInterrupt::Interrupt15Timer1Priority3
151            | CpuInterrupt::Interrupt22EdgePriority3
152            | CpuInterrupt::Interrupt27LevelPriority3
153            | CpuInterrupt::Interrupt29SoftwarePriority3
154            | CpuInterrupt::Interrupt23LevelPriority3 => Priority::Priority3,
155        }
156    }
157
158    #[inline]
159    #[cfg(feature = "rt")]
160    pub(crate) fn level(self) -> u32 {
161        self.priority() as u32
162    }
163}
164
165/// Interrupt priority levels.
166///
167/// A higher numeric value means higher priority. Interrupt requests at higher priority levels will
168/// be able to preempt code running at a lower [`RunLevel`][super::RunLevel].
169#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
170#[cfg_attr(feature = "defmt", derive(defmt::Format))]
171#[repr(u8)]
172#[non_exhaustive]
173pub enum Priority {
174    /// Priority level 1.
175    Priority1 = 1,
176    /// Priority level 2.
177    Priority2 = 2,
178    /// Priority level 3.
179    Priority3 = 3,
180    // TODO: Xtensa has 7 priority levels, the higher ones are only not recommended for use.
181    // We should add these levels, and a mechanism to bind assembly-written handlers for them.
182}
183
184impl Priority {
185    /// Maximum interrupt priority
186    #[instability::unstable]
187    pub const fn max() -> Priority {
188        Priority::Priority3
189    }
190
191    /// Minimum interrupt priority
192    pub const fn min() -> Priority {
193        Priority::Priority1
194    }
195}
196
197/// Interrupt run levels.
198#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
199#[cfg_attr(feature = "defmt", derive(defmt::Format))]
200#[repr(u8)]
201#[non_exhaustive]
202pub enum ElevatedRunLevel {
203    /// Run level 1.
204    Level1 = 1,
205    /// Run level 2.
206    Level2 = 2,
207    /// Run level 3.
208    Level3 = 3,
209    /// Run level 4.
210    Level4 = 4,
211    /// Run level 5.
212    Level5 = 5,
213    /// Run level 6.
214    Level6 = 6,
215    /// Run level 7.
216    Level7 = 7,
217}
218
219impl ElevatedRunLevel {
220    /// Maximum interrupt run level
221    #[instability::unstable]
222    pub const fn max() -> ElevatedRunLevel {
223        ElevatedRunLevel::Level7
224    }
225
226    /// Minimum interrupt run level
227    pub const fn min() -> ElevatedRunLevel {
228        ElevatedRunLevel::Level1
229    }
230
231    pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
232        match priority {
233            1 => Ok(ElevatedRunLevel::Level1),
234            2 => Ok(ElevatedRunLevel::Level2),
235            3 => Ok(ElevatedRunLevel::Level3),
236            4 => Ok(ElevatedRunLevel::Level4),
237            5 => Ok(ElevatedRunLevel::Level5),
238            6 => Ok(ElevatedRunLevel::Level6),
239            7 => Ok(ElevatedRunLevel::Level7),
240
241            _ => Err(PriorityError::InvalidInterruptPriority),
242        }
243    }
244
245    /// Converts a [`Priority`] into an [`ElevatedRunLevel`].
246    pub const fn from_priority(priority: Priority) -> Self {
247        match priority {
248            Priority::Priority1 => ElevatedRunLevel::Level1,
249            Priority::Priority2 => ElevatedRunLevel::Level2,
250            Priority::Priority3 => ElevatedRunLevel::Level3,
251        }
252    }
253}
254
255impl From<Priority> for ElevatedRunLevel {
256    fn from(priority: Priority) -> Self {
257        Self::from_priority(priority)
258    }
259}
260
261#[instability::unstable]
262impl TryFrom<u32> for ElevatedRunLevel {
263    type Error = PriorityError;
264
265    fn try_from(value: u32) -> Result<Self, Self::Error> {
266        Self::try_from_u32(value)
267    }
268}
269
270#[instability::unstable]
271impl TryFrom<u8> for ElevatedRunLevel {
272    type Error = PriorityError;
273
274    fn try_from(value: u8) -> Result<Self, Self::Error> {
275        Self::try_from(value as u32)
276    }
277}
278
279pub(super) const DISABLED_CPU_INTERRUPT: u32 = 16;
280
281// CPU interrupt API. These don't take a core, because the control mechanisms are generally
282// core-local.
283
284pub(crate) fn enable_cpu_interrupt_raw(cpu_interrupt: u32) {
285    unsafe { xtensa_lx::interrupt::enable_mask(1 << cpu_interrupt) };
286}
287
288// Runlevel APIs
289
290/// Get the current run level (the level below which interrupts are masked).
291pub(crate) fn current_raw_runlevel() -> u32 {
292    xtensa_lx::interrupt::get_level()
293}
294
295/// Changes the current run level (the level below which interrupts are
296/// masked), and returns the previous run level.
297///
298/// # Safety
299///
300/// This function must only be used to raise the runlevel and to restore it
301/// to a previous value. It must not be used to arbitrarily lower the
302/// runlevel.
303pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
304    let token: u32;
305    unsafe {
306        match level {
307            RunLevel::ThreadMode => core::arch::asm!("rsil {0}, 0", out(reg) token),
308            RunLevel::Interrupt(ElevatedRunLevel::Level1) => {
309                core::arch::asm!("rsil {0}, 1", out(reg) token)
310            }
311            RunLevel::Interrupt(ElevatedRunLevel::Level2) => {
312                core::arch::asm!("rsil {0}, 2", out(reg) token)
313            }
314            RunLevel::Interrupt(ElevatedRunLevel::Level3) => {
315                core::arch::asm!("rsil {0}, 3", out(reg) token)
316            }
317            RunLevel::Interrupt(ElevatedRunLevel::Level4) => {
318                core::arch::asm!("rsil {0}, 4", out(reg) token)
319            }
320            RunLevel::Interrupt(ElevatedRunLevel::Level5) => {
321                core::arch::asm!("rsil {0}, 5", out(reg) token)
322            }
323            RunLevel::Interrupt(ElevatedRunLevel::Level6) => {
324                core::arch::asm!("rsil {0}, 6", out(reg) token)
325            }
326            RunLevel::Interrupt(ElevatedRunLevel::Level7) => {
327                core::arch::asm!("rsil {0}, 7", out(reg) token)
328            }
329        };
330    }
331
332    unwrap!(RunLevel::try_from_u32(token & 0x0F))
333}
334
335/// Wait for an interrupt to occur.
336///
337/// This function causes the current CPU core to execute its Wait For Interrupt
338/// (WFI or equivalent) instruction. After executing this function, the CPU core
339/// will stop execution until an interrupt occurs.
340#[inline(always)]
341#[instability::unstable]
342pub fn wait_for_interrupt() {
343    unsafe { core::arch::asm!("waiti 0") };
344}
345
346pub(crate) fn priority_to_cpu_interrupt(interrupt: Interrupt, level: Priority) -> CpuInterrupt {
347    if EDGE_INTERRUPTS.contains(&interrupt) {
348        match level {
349            Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
350            Priority::Priority2 => {
351                warn!("Priority 2 edge interrupts are not supported, using Priority 1 instead");
352                CpuInterrupt::Interrupt10EdgePriority1
353            }
354            Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
355        }
356    } else {
357        match level {
358            Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
359            Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
360            Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
361        }
362    }
363}
364
365cfg_select! {
366    esp32 => {
367        pub(crate) const EDGE_INTERRUPTS: [Interrupt; 8] = [
368            Interrupt::TG0_T0_EDGE,
369            Interrupt::TG0_T1_EDGE,
370            Interrupt::TG0_WDT_EDGE,
371            Interrupt::TG0_LACT_EDGE,
372            Interrupt::TG1_T0_EDGE,
373            Interrupt::TG1_T1_EDGE,
374            Interrupt::TG1_WDT_EDGE,
375            Interrupt::TG1_LACT_EDGE,
376        ];
377    }
378    esp32s2 => {
379        pub(crate) const EDGE_INTERRUPTS: [Interrupt; 11] = [
380            Interrupt::TG0_T0_EDGE,
381            Interrupt::TG0_T1_EDGE,
382            Interrupt::TG0_WDT_EDGE,
383            Interrupt::TG0_LACT_EDGE,
384            Interrupt::TG1_T0_EDGE,
385            Interrupt::TG1_T1_EDGE,
386            Interrupt::TG1_WDT_EDGE,
387            Interrupt::TG1_LACT_EDGE,
388            Interrupt::SYSTIMER_TARGET0,
389            Interrupt::SYSTIMER_TARGET1,
390            Interrupt::SYSTIMER_TARGET2,
391        ];
392    }
393    esp32s3 => {
394        pub(crate) const EDGE_INTERRUPTS: [Interrupt; 0] = [];
395    }
396    _ => {
397        compile_error!("Unsupported chip");
398    }
399}
400
401/// Setup interrupts ready for vectoring
402///
403/// # Safety
404///
405/// This function must be called only during core startup.
406#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
407pub(crate) unsafe fn init_vectoring() {
408    // Enable vectored interrupts. No configuration is needed because these interrupts have
409    // fixed priority and trigger mode.
410    for cpu_int in [
411        CpuInterrupt::Interrupt10EdgePriority1,
412        CpuInterrupt::Interrupt22EdgePriority3,
413        CpuInterrupt::Interrupt1LevelPriority1,
414        CpuInterrupt::Interrupt19LevelPriority2,
415        CpuInterrupt::Interrupt23LevelPriority3,
416    ] {
417        cpu_int.enable();
418    }
419}
420
421#[cfg(feature = "rt")]
422pub(crate) mod rt {
423    use procmacros::ram;
424    use xtensa_lx_rt::{exception::Context, interrupt::CpuInterruptLevel};
425
426    use super::*;
427    use crate::{interrupt::InterruptStatus, system::Cpu};
428
429    #[cfg_attr(place_switch_tables_in_ram, ram)]
430    pub(crate) static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
431    #[cfg_attr(place_switch_tables_in_ram, ram)]
432    pub(crate) static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
433
434    #[cfg_attr(place_switch_tables_in_ram, ram)]
435    pub(crate) static CPU_INTERRUPT_LEVELS: [u32; 8] = [
436        0, // Dummy level 0
437        CpuInterruptLevel::Level1.mask(),
438        CpuInterruptLevel::Level2.mask(),
439        CpuInterruptLevel::Level3.mask(),
440        CpuInterruptLevel::Level4.mask(),
441        CpuInterruptLevel::Level5.mask(),
442        CpuInterruptLevel::Level6.mask(),
443        CpuInterruptLevel::Level7.mask(),
444    ];
445
446    /// A bitmap of edge-triggered peripheral interrupts. See `handle_interrupts` why this is
447    /// necessary
448    #[cfg_attr(place_switch_tables_in_ram, ram)]
449    pub static INTERRUPT_EDGE: InterruptStatus = const {
450        let mut masks = [0; crate::interrupt::STATUS_WORDS];
451
452        let mut idx = 0;
453        while idx < EDGE_INTERRUPTS.len() {
454            let interrupt_idx = EDGE_INTERRUPTS[idx] as usize;
455            let word_idx = interrupt_idx / 32;
456            masks[word_idx] |= 1 << (interrupt_idx % 32);
457            idx += 1;
458        }
459
460        InterruptStatus { status: masks }
461    };
462
463    #[unsafe(no_mangle)]
464    #[ram]
465    unsafe fn __level_1_interrupt(save_frame: &mut Context) {
466        unsafe {
467            handle_interrupts::<1>(save_frame);
468        }
469    }
470
471    #[unsafe(no_mangle)]
472    #[ram]
473    unsafe fn __level_2_interrupt(save_frame: &mut Context) {
474        unsafe {
475            handle_interrupts::<2>(save_frame);
476        }
477    }
478
479    #[unsafe(no_mangle)]
480    #[ram]
481    unsafe fn __level_3_interrupt(save_frame: &mut Context) {
482        unsafe {
483            handle_interrupts::<3>(save_frame);
484        }
485    }
486
487    #[inline(always)]
488    unsafe fn handle_interrupts<const LEVEL: u32>(save_frame: &mut Context) {
489        let cpu_interrupt_mask = xtensa_lx::interrupt::get()
490            & xtensa_lx::interrupt::get_mask()
491            & CPU_INTERRUPT_LEVELS[LEVEL as usize];
492
493        if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
494            // Let's handle CPU-internal interrupts (NMI, Timer, Software, Profiling).
495            // These are rarely used by the HAL.
496
497            // Mask the relevant bits
498            let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
499
500            // Pick one
501            let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
502
503            // If the interrupt is edge triggered, we need to clear the request on the CPU's
504            // side.
505            if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
506                unsafe {
507                    xtensa_lx::interrupt::clear(1 << cpu_interrupt_nr);
508                }
509            }
510
511            if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
512                unsafe { handler(save_frame) };
513            }
514        } else {
515            let status = if !cfg!(esp32s3) && (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
516                // Next, handle edge triggered peripheral interrupts. Note that on the S3 all
517                // peripheral interrupts are level-triggered.
518
519                // If the interrupt is edge triggered, we need to clear the
520                // request on the CPU's side
521                unsafe { xtensa_lx::interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE) };
522
523                // For edge interrupts we cannot rely on the peripherals' interrupt status
524                // registers, therefore call all registered handlers for current level.
525                INTERRUPT_EDGE
526            } else {
527                // Finally, check level-triggered peripheral sources.
528                // These interrupts are cleared by the peripheral.
529                InterruptStatus::current()
530            };
531
532            let core = Cpu::current();
533            for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
534                crate::interrupt::should_handle(core, interrupt_nr as u32, LEVEL)
535            }) {
536                let handler = unsafe { crate::pac::__INTERRUPTS[interrupt_nr as usize]._handler };
537                let handler: fn(&mut Context) = unsafe {
538                    core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler)
539                };
540                handler(save_frame);
541            }
542        }
543    }
544
545    #[inline]
546    pub(crate) fn cpu_interrupt_nr_to_cpu_interrupt_handler(
547        number: u32,
548    ) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
549        use xtensa_lx_rt::*;
550        // we're fortunate that all esp variants use the same CPU interrupt layout
551        Some(match number {
552            6 => Timer0,
553            7 => Software0,
554            11 => Profiling,
555            14 => NMI,
556            15 => Timer1,
557            16 => Timer2,
558            29 => Software1,
559            _ => return None,
560        })
561    }
562
563    // Raw handlers for CPU interrupts, assembly only.
564    unsafe extern "C" {
565        fn level4_interrupt(save_frame: &mut Context);
566        fn level5_interrupt(save_frame: &mut Context);
567        #[cfg(not(all(feature = "rt", feature = "exception-handler", stack_guard_monitoring)))]
568        fn level6_interrupt(save_frame: &mut Context);
569        fn level7_interrupt(save_frame: &mut Context);
570    }
571
572    #[unsafe(no_mangle)]
573    #[ram]
574    unsafe fn __level_4_interrupt(save_frame: &mut Context) {
575        unsafe { level4_interrupt(save_frame) }
576    }
577
578    #[unsafe(no_mangle)]
579    #[ram]
580    unsafe fn __level_5_interrupt(save_frame: &mut Context) {
581        unsafe { level5_interrupt(save_frame) }
582    }
583
584    #[unsafe(no_mangle)]
585    #[ram]
586    unsafe fn __level_6_interrupt(save_frame: &mut Context) {
587        cfg_select! {
588            all(feature = "rt", feature = "exception-handler", stack_guard_monitoring) => {
589                crate::exception_handler::breakpoint_interrupt(save_frame);
590            }
591            _ => unsafe { level6_interrupt(save_frame) },
592        }
593    }
594
595    #[unsafe(no_mangle)]
596    #[ram]
597    unsafe fn __level_7_interrupt(save_frame: &mut Context) {
598        unsafe { level7_interrupt(save_frame) }
599    }
600}