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 is 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    /// Enables the CPU interrupt.
111    #[inline]
112    #[instability::unstable]
113    pub fn enable(self) {
114        enable_cpu_interrupt_raw(self as u32);
115    }
116
117    /// Clears 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    /// Returns the 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/// Returns 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/// Must only be used to raise the runlevel and to restore it to a previous
301/// value. Must not be used to arbitrarily lower the runlevel.
302pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
303    let token: u32;
304    unsafe {
305        match level {
306            RunLevel::ThreadMode => core::arch::asm!("rsil {0}, 0", out(reg) token),
307            RunLevel::Interrupt(ElevatedRunLevel::Level1) => {
308                core::arch::asm!("rsil {0}, 1", out(reg) token)
309            }
310            RunLevel::Interrupt(ElevatedRunLevel::Level2) => {
311                core::arch::asm!("rsil {0}, 2", out(reg) token)
312            }
313            RunLevel::Interrupt(ElevatedRunLevel::Level3) => {
314                core::arch::asm!("rsil {0}, 3", out(reg) token)
315            }
316            RunLevel::Interrupt(ElevatedRunLevel::Level4) => {
317                core::arch::asm!("rsil {0}, 4", out(reg) token)
318            }
319            RunLevel::Interrupt(ElevatedRunLevel::Level5) => {
320                core::arch::asm!("rsil {0}, 5", out(reg) token)
321            }
322            RunLevel::Interrupt(ElevatedRunLevel::Level6) => {
323                core::arch::asm!("rsil {0}, 6", out(reg) token)
324            }
325            RunLevel::Interrupt(ElevatedRunLevel::Level7) => {
326                core::arch::asm!("rsil {0}, 7", out(reg) token)
327            }
328        };
329    }
330
331    unwrap!(RunLevel::try_from_u32(token & 0x0F))
332}
333
334/// Waits for an interrupt to occur.
335///
336/// Causes the current CPU core to execute its Wait For Interrupt (WFI or
337/// equivalent) instruction. After this call, the CPU core stops execution until
338/// an interrupt occurs.
339#[inline(always)]
340#[instability::unstable]
341pub fn wait_for_interrupt() {
342    unsafe { core::arch::asm!("waiti 0") };
343}
344
345pub(crate) fn priority_to_cpu_interrupt(interrupt: Interrupt, level: Priority) -> CpuInterrupt {
346    if EDGE_INTERRUPTS.contains(&interrupt) {
347        match level {
348            Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
349            Priority::Priority2 => {
350                warn!("Priority 2 edge interrupts are not supported, using Priority 1 instead");
351                CpuInterrupt::Interrupt10EdgePriority1
352            }
353            Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
354        }
355    } else {
356        match level {
357            Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
358            Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
359            Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
360        }
361    }
362}
363
364cfg_select! {
365    esp32 => {
366        pub(crate) const EDGE_INTERRUPTS: [Interrupt; 8] = [
367            Interrupt::TG0_T0_EDGE,
368            Interrupt::TG0_T1_EDGE,
369            Interrupt::TG0_WDT_EDGE,
370            Interrupt::TG0_LACT_EDGE,
371            Interrupt::TG1_T0_EDGE,
372            Interrupt::TG1_T1_EDGE,
373            Interrupt::TG1_WDT_EDGE,
374            Interrupt::TG1_LACT_EDGE,
375        ];
376    }
377    esp32s2 => {
378        pub(crate) const EDGE_INTERRUPTS: [Interrupt; 11] = [
379            Interrupt::TG0_T0_EDGE,
380            Interrupt::TG0_T1_EDGE,
381            Interrupt::TG0_WDT_EDGE,
382            Interrupt::TG0_LACT_EDGE,
383            Interrupt::TG1_T0_EDGE,
384            Interrupt::TG1_T1_EDGE,
385            Interrupt::TG1_WDT_EDGE,
386            Interrupt::TG1_LACT_EDGE,
387            Interrupt::SYSTIMER_TARGET0,
388            Interrupt::SYSTIMER_TARGET1,
389            Interrupt::SYSTIMER_TARGET2,
390        ];
391    }
392    esp32s3 => {
393        pub(crate) const EDGE_INTERRUPTS: [Interrupt; 0] = [];
394    }
395    _ => {
396        compile_error!("Unsupported chip");
397    }
398}
399
400/// Sets up interrupts ready for vectoring.
401///
402/// # Safety
403///
404/// Must be called only during core startup.
405#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
406pub(crate) unsafe fn init_vectoring() {
407    // Enable vectored interrupts. No configuration is needed because these interrupts have
408    // fixed priority and trigger mode.
409    for cpu_int in [
410        CpuInterrupt::Interrupt10EdgePriority1,
411        CpuInterrupt::Interrupt22EdgePriority3,
412        CpuInterrupt::Interrupt1LevelPriority1,
413        CpuInterrupt::Interrupt19LevelPriority2,
414        CpuInterrupt::Interrupt23LevelPriority3,
415    ] {
416        cpu_int.enable();
417    }
418}
419
420#[cfg(feature = "rt")]
421pub(crate) mod rt {
422    use procmacros::ram;
423    use xtensa_lx_rt::{exception::Context, interrupt::CpuInterruptLevel};
424
425    use super::*;
426    use crate::{interrupt::InterruptStatus, system::Cpu};
427
428    #[cfg_attr(place_switch_tables_in_ram, ram)]
429    pub(crate) static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
430    #[cfg_attr(place_switch_tables_in_ram, ram)]
431    pub(crate) static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
432
433    #[cfg_attr(place_switch_tables_in_ram, ram)]
434    pub(crate) static CPU_INTERRUPT_LEVELS: [u32; 8] = [
435        0, // Dummy level 0
436        CpuInterruptLevel::Level1.mask(),
437        CpuInterruptLevel::Level2.mask(),
438        CpuInterruptLevel::Level3.mask(),
439        CpuInterruptLevel::Level4.mask(),
440        CpuInterruptLevel::Level5.mask(),
441        CpuInterruptLevel::Level6.mask(),
442        CpuInterruptLevel::Level7.mask(),
443    ];
444
445    /// A bitmap of edge-triggered peripheral interrupts. See `handle_interrupts` why this is
446    /// necessary
447    #[cfg_attr(place_switch_tables_in_ram, ram)]
448    pub static INTERRUPT_EDGE: InterruptStatus = const {
449        let mut masks = [0; crate::interrupt::STATUS_WORDS];
450
451        let mut idx = 0;
452        while idx < EDGE_INTERRUPTS.len() {
453            let interrupt_idx = EDGE_INTERRUPTS[idx] as usize;
454            let word_idx = interrupt_idx / 32;
455            masks[word_idx] |= 1 << (interrupt_idx % 32);
456            idx += 1;
457        }
458
459        InterruptStatus { status: masks }
460    };
461
462    #[unsafe(no_mangle)]
463    #[ram]
464    unsafe fn __level_1_interrupt(save_frame: &mut Context) {
465        unsafe {
466            handle_interrupts::<1>(save_frame);
467        }
468    }
469
470    #[unsafe(no_mangle)]
471    #[ram]
472    unsafe fn __level_2_interrupt(save_frame: &mut Context) {
473        unsafe {
474            handle_interrupts::<2>(save_frame);
475        }
476    }
477
478    #[unsafe(no_mangle)]
479    #[ram]
480    unsafe fn __level_3_interrupt(save_frame: &mut Context) {
481        unsafe {
482            handle_interrupts::<3>(save_frame);
483        }
484    }
485
486    #[inline(always)]
487    unsafe fn handle_interrupts<const LEVEL: u32>(save_frame: &mut Context) {
488        let cpu_interrupt_mask = xtensa_lx::interrupt::get()
489            & xtensa_lx::interrupt::get_mask()
490            & CPU_INTERRUPT_LEVELS[LEVEL as usize];
491
492        if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
493            // Let's handle CPU-internal interrupts (NMI, Timer, Software, Profiling).
494            // These are rarely used by the HAL.
495
496            // Mask the relevant bits
497            let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
498
499            // Pick one
500            let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
501
502            // If the interrupt is edge triggered, we need to clear the request on the CPU's
503            // side.
504            if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
505                unsafe {
506                    xtensa_lx::interrupt::clear(1 << cpu_interrupt_nr);
507                }
508            }
509
510            if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
511                unsafe { handler(save_frame) };
512            }
513        } else {
514            let status = if !cfg!(esp32s3) && (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
515                // Next, handle edge triggered peripheral interrupts. Note that on the S3 all
516                // peripheral interrupts are level-triggered.
517
518                // If the interrupt is edge triggered, we need to clear the
519                // request on the CPU's side
520                unsafe { xtensa_lx::interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE) };
521
522                // For edge interrupts we cannot rely on the peripherals' interrupt status
523                // registers, therefore call all registered handlers for current level.
524                INTERRUPT_EDGE
525            } else {
526                // Finally, check level-triggered peripheral sources.
527                // These interrupts are cleared by the peripheral.
528                InterruptStatus::current()
529            };
530
531            let core = Cpu::current();
532            for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
533                crate::interrupt::should_handle(core, interrupt_nr as u32, LEVEL)
534            }) {
535                let handler = unsafe { crate::pac::__INTERRUPTS[interrupt_nr as usize]._handler };
536                let handler: fn(&mut Context) = unsafe {
537                    core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler)
538                };
539                handler(save_frame);
540            }
541        }
542    }
543
544    #[inline]
545    pub(crate) fn cpu_interrupt_nr_to_cpu_interrupt_handler(
546        number: u32,
547    ) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
548        use xtensa_lx_rt::*;
549        // we're fortunate that all esp variants use the same CPU interrupt layout
550        Some(match number {
551            6 => Timer0,
552            7 => Software0,
553            11 => Profiling,
554            14 => NMI,
555            15 => Timer1,
556            16 => Timer2,
557            29 => Software1,
558            _ => return None,
559        })
560    }
561
562    // Raw handlers for CPU interrupts, assembly only.
563    unsafe extern "C" {
564        fn level4_interrupt(save_frame: &mut Context);
565        fn level5_interrupt(save_frame: &mut Context);
566        #[cfg(not(all(feature = "rt", feature = "exception-handler", stack_guard_monitoring)))]
567        fn level6_interrupt(save_frame: &mut Context);
568        fn level7_interrupt(save_frame: &mut Context);
569    }
570
571    #[unsafe(no_mangle)]
572    #[ram]
573    unsafe fn __level_4_interrupt(save_frame: &mut Context) {
574        unsafe { level4_interrupt(save_frame) }
575    }
576
577    #[unsafe(no_mangle)]
578    #[ram]
579    unsafe fn __level_5_interrupt(save_frame: &mut Context) {
580        unsafe { level5_interrupt(save_frame) }
581    }
582
583    #[unsafe(no_mangle)]
584    #[ram]
585    unsafe fn __level_6_interrupt(save_frame: &mut Context) {
586        cfg_select! {
587            all(feature = "rt", feature = "exception-handler", stack_guard_monitoring) => {
588                crate::exception_handler::breakpoint_interrupt(save_frame);
589            }
590            _ => unsafe { level6_interrupt(save_frame) },
591        }
592    }
593
594    #[unsafe(no_mangle)]
595    #[ram]
596    unsafe fn __level_7_interrupt(save_frame: &mut Context) {
597        unsafe { level7_interrupt(save_frame) }
598    }
599}