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