Skip to main content

esp_hal/interrupt/
mod.rs

1//! # Interrupt support
2//!
3//! This module contains code to configure and handle peripheral interrupts.
4//!
5//! ## Overview
6//!
7//! Peripheral interrupt requests are not handled by the CPU directly. Instead, they are routed
8//! through the interrupt matrix, which maps peripheral interrupts to CPU interrupts. There are
9//! more peripheral interrupt signals than CPU interrupts, and multiple peripheral interrupts can
10//! be routed to the same CPU interrupt. A set of CPU interrupts are configured to run a default
11//! handler routine, which polls the interrupt controller and calls the appropriate handlers for the
12//! pending peripheral interrupts.
13//!
14//! This default handler implements interrupt nesting - meaning a higher [`Priority`] interrupt can
15//! preempt a lower priority interrupt handler. The number of priorities is a chip-specific detail.
16//!
17//! ## Usage
18//!
19//! Peripheral drivers manage interrupts for you. Where appropriate, a `set_interrupt_handler`
20//! function is provided to register a function that handles interrupts at a chosen priority
21//! level. Interrupt handler functions need to be marked by the [`#[handler]`][crate::handler]
22//! attribute. These drivers also provide `listen` and `unlisten` functions that control whether an
23//! interrupt will be generated for the matching event or not. For more information and examples,
24//! consult the documentation of the specific peripheral drivers.
25//!
26//! If you are writing your own peripheral driver, you will need to first register interrupt
27//! handlers using the [peripheral singletons'][crate::peripherals::I2C0] `bind_X_interrupt`
28//! functions. You can use the matching `enable` and `disable` functions to control the peripheral
29//! interrupt in the interrupt matrix, or you can, depending on the peripheral, set or clear the
30//! appropriate enable bits in the `int_ena` register.
31//!
32//! ## Software Interrupts
33//!
34//! The [`software`] module implements software interrupts using peripheral interrupt signals.
35#![cfg_attr(
36    multi_core,
37    doc = "Those signals can also carry cross-core communication."
38)]
39#![cfg_attr(
40    all(feature = "rt", multi_core),
41    doc = "
42## Inter-Processor Call (IPC)
43
44The [`ipc`] module posts a function to a CPU, and raises the IPC interrupt of that CPU.
45"
46)]
47#![cfg_attr(
48    all(feature = "rt", multi_core, xtensa),
49    doc = "The HAL reserves `FROM_CPU_INTR0` and `FROM_CPU_INTR1` for this path."
50)]
51#![cfg_attr(
52    all(feature = "rt", single_core, context_switch_source = "from_cpu"),
53    doc = "The HAL reserves `FROM_CPU_INTR0` to switch tasks."
54)]
55#![cfg_attr(
56    all(feature = "rt", context_switch_source = "software0"),
57    doc = "
58## Context Switching
59
60The HAL defines the `Software0` interrupt handler, and switches tasks in that interrupt.
61"
62)]
63
64use crate::{peripherals::Interrupt, system::Cpu};
65
66cfg_select! {
67    esp32 => {
68        use crate::peripherals::{DPORT as INTERRUPT_CORE0, DPORT as INTERRUPT_CORE1};
69    }
70    _ => {
71        use crate::peripherals::INTERRUPT_CORE0;
72        #[cfg(multi_core)]
73        use crate::peripherals::INTERRUPT_CORE1;
74    }
75}
76
77#[cfg_attr(riscv, path = "riscv.rs")]
78#[cfg_attr(xtensa, path = "xtensa.rs")]
79mod arch;
80pub use arch::*;
81
82use crate::pac;
83
84unstable_module! {
85    pub mod software;
86
87    #[cfg(all(feature = "rt", multi_core))]
88    pub mod ipc;
89
90    #[cfg(feature = "rt")]
91    #[doc(hidden)]
92    pub mod __rtos_implementation;
93}
94
95#[cfg(feature = "rt")]
96#[unsafe(no_mangle)]
97extern "C" fn EspDefaultHandler() {
98    panic!("Unhandled interrupt on {:?}", crate::system::Cpu::current());
99}
100
101/// Default (unhandled) interrupt handler
102#[instability::unstable]
103pub const DEFAULT_INTERRUPT_HANDLER: InterruptHandler = InterruptHandler::new(
104    {
105        unsafe extern "C" {
106            fn EspDefaultHandler();
107        }
108
109        unsafe {
110            core::mem::transmute::<unsafe extern "C" fn(), extern "C" fn()>(EspDefaultHandler)
111        }
112    },
113    Priority::min(),
114);
115
116/// Trait implemented by drivers that support registering an
117/// [`InterruptHandler`].
118#[instability::unstable]
119pub trait InterruptConfigurable: crate::private::Sealed {
120    #[doc = cfg_select! {
121        multi_core => "Registers an interrupt handler for the peripheral on the current core.",
122        _ => "Registers an interrupt handler for the peripheral.",
123    }]
124    #[doc = ""]
125    /// Replaces any previously registered interrupt handlers. Some peripherals
126    /// offer a shared interrupt handler for multiple purposes. The caller must
127    /// honor this constraint.
128    ///
129    /// The default/unhandled interrupt handler can be restored with
130    /// [`DEFAULT_INTERRUPT_HANDLER`].
131    fn set_interrupt_handler(&mut self, handler: InterruptHandler);
132}
133
134/// Represents an ISR callback function.
135#[derive(Copy, Clone, Debug)]
136#[cfg_attr(feature = "defmt", derive(defmt::Format))]
137#[instability::unstable]
138pub struct IsrCallback {
139    f: extern "C" fn(),
140}
141
142impl IsrCallback {
143    /// Creates a new callback from the callback function.
144    pub fn new(f: extern "C" fn()) -> Self {
145        // a valid fn pointer is non zero
146        Self { f }
147    }
148
149    /// Returns the address of the callback.
150    pub fn address(self) -> usize {
151        self.f as usize
152    }
153
154    /// The callback function.
155    ///
156    /// This is aligned and can be called.
157    pub fn callback(self) -> extern "C" fn() {
158        self.f
159    }
160}
161
162impl PartialEq for IsrCallback {
163    fn eq(&self, other: &Self) -> bool {
164        core::ptr::fn_addr_eq(self.callback(), other.callback())
165    }
166}
167
168/// An interrupt handler.
169#[cfg_attr(
170    multi_core,
171    doc = r"
172
173**Note**: Interrupts are handled on the core they were setup on. If a driver is initialized
174on core 0, and moved to core 1, core 0 will still handle the interrupt."
175)]
176#[derive(Copy, Clone, Debug)]
177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
178#[instability::unstable]
179pub struct InterruptHandler {
180    f: extern "C" fn(),
181    prio: Priority,
182}
183
184impl InterruptHandler {
185    /// Creates a new [`InterruptHandler`] which will call the given function at
186    /// the given priority.
187    pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
188        Self { f, prio }
189    }
190
191    /// The Isr callback.
192    #[inline]
193    pub fn handler(&self) -> IsrCallback {
194        IsrCallback::new(self.f)
195    }
196
197    /// Returns the priority used when registering the interrupt.
198    #[inline]
199    pub fn priority(&self) -> Priority {
200        self.prio
201    }
202}
203
204const STATUS_WORDS: usize = property!("interrupts.status_registers");
205
206/// Representation of peripheral-interrupt status bits.
207#[derive(Clone, Copy, Default, Debug)]
208#[instability::unstable]
209pub struct InterruptStatus {
210    status: [u32; STATUS_WORDS],
211}
212
213impl InterruptStatus {
214    /// Returns the status of a particular peripheral interrupt.
215    #[instability::unstable]
216    #[inline]
217    pub fn is_pending(interrupt: Interrupt) -> bool {
218        let word = (interrupt as usize) / 32;
219        let bit = (interrupt as usize) % 32;
220        let mut status_word = 0;
221        for cpu in Cpu::all() {
222            status_word |= Self::interrupt_status_word(cpu, word);
223        }
224        (status_word & (1 << bit)) != 0
225    }
226
227    #[inline]
228    fn interrupt_status_word(cpu: Cpu, word: usize) -> u32 {
229        match cpu {
230            Cpu::ProCpu => {
231                cfg_select! {
232                    esp32p4 => {
233                        if word == 4 {
234                            // Discontiguous, cannot be part of the standard status array
235                            return INTERRUPT_CORE0::regs().core_0_intr_status4().read().bits();
236                        }
237                    }
238                    _ => {}
239                }
240
241                INTERRUPT_CORE0::regs()
242                    .core_0_intr_status(word)
243                    .read()
244                    .bits()
245            }
246            #[cfg(multi_core)]
247            Cpu::AppCpu => {
248                cfg_select! {
249                    esp32p4 => {
250                        if word == 4 {
251                            // Discontiguous, cannot be part of the standard status array
252                            return INTERRUPT_CORE1::regs().core_1_intr_status4().read().bits();
253                        }
254                    }
255                    _ => {}
256                }
257
258                INTERRUPT_CORE1::regs()
259                    .core_1_intr_status(word)
260                    .read()
261                    .bits()
262            }
263        }
264    }
265
266    /// Returns the status of peripheral interrupts.
267    #[instability::unstable]
268    pub fn current() -> InterruptStatus {
269        let cpu = Cpu::current();
270        InterruptStatus {
271            status: core::array::from_fn(|word| Self::interrupt_status_word(cpu, word)),
272        }
273    }
274
275    /// Is the given interrupt bit set.
276    #[instability::unstable]
277    pub fn is_set(&self, interrupt: u8) -> bool {
278        (self.status[interrupt as usize / 32] & (1 << (interrupt % 32))) != 0
279    }
280
281    /// Sets the given interrupt status bit.
282    #[instability::unstable]
283    pub fn set(&mut self, interrupt: u8) {
284        self.status[interrupt as usize / 32] |= 1 << (interrupt % 32);
285    }
286
287    /// Returns an iterator over the set interrupt status bits.
288    #[instability::unstable]
289    pub fn iterator(&self) -> InterruptStatusIterator {
290        InterruptStatusIterator {
291            status: *self,
292            idx: 0,
293        }
294    }
295}
296
297/// Iterator over set interrupt status bits
298#[derive(Debug, Clone)]
299#[instability::unstable]
300pub struct InterruptStatusIterator {
301    status: InterruptStatus,
302    idx: usize,
303}
304
305impl Iterator for InterruptStatusIterator {
306    type Item = u8;
307
308    fn next(&mut self) -> Option<Self::Item> {
309        for i in self.idx..STATUS_WORDS {
310            if self.status.status[i] != 0 {
311                let bit = self.status.status[i].trailing_zeros();
312                self.status.status[i] ^= 1 << bit;
313                self.idx = i;
314                return Some((bit + 32 * i as u32) as u8);
315            }
316        }
317        self.idx = usize::MAX;
318        None
319    }
320}
321
322// Peripheral interrupt API.
323
324fn vector_entry(interrupt: Interrupt) -> &'static pac::Vector {
325    unsafe extern "Rust" {
326        #[cfg(xtensa)]
327        static __INTERRUPTS: pac::Vector;
328        #[cfg(riscv)]
329        static __EXTERNAL_INTERRUPTS: pac::Vector;
330    }
331
332    // SAFETY: Interrupt numbers are guaranteed to be valid and in range because we use the
333    // Interrupt enum, which is generated from the list of valid peripheral interrupts in the PAC.
334    unsafe {
335        cfg_select! {
336            xtensa => (&__INTERRUPTS as *const pac::Vector)
337                .add(interrupt as usize)
338                .as_ref_unchecked(),
339            riscv => (&__EXTERNAL_INTERRUPTS as *const pac::Vector)
340                .add(interrupt as usize)
341                .as_ref_unchecked(),
342            _ => {
343                compile_error!("Unsupported architecture");
344            }
345        }
346    }
347}
348
349/// Returns the currently bound interrupt handler.
350#[instability::unstable]
351pub fn bound_handler(interrupt: Interrupt) -> Option<IsrCallback> {
352    unsafe {
353        let vector = vector_entry(interrupt);
354
355        let addr = vector._handler;
356        if addr as usize == 0 {
357            return None;
358        }
359
360        Some(IsrCallback::new(core::mem::transmute::<
361            unsafe extern "C" fn(),
362            extern "C" fn(),
363        >(addr)))
364    }
365}
366
367/// Binds the given handler to a peripheral interrupt.
368///
369/// The interrupt handler will be enabled at the specified priority level.
370///
371/// The interrupt handler will be called on the core where it is registered.
372/// Only one interrupt handler can be bound to a peripheral interrupt.
373#[instability::unstable]
374pub fn bind_handler(interrupt: Interrupt, handler: InterruptHandler) {
375    bind_vector(interrupt, handler);
376    enable(interrupt, handler.priority());
377}
378
379/// Binds `handler` to `interrupt` without enabling the peripheral interrupt.
380pub(crate) fn bind_vector(interrupt: Interrupt, handler: InterruptHandler) {
381    unsafe {
382        let vector = vector_entry(interrupt);
383        let ptr = (&raw const vector._handler).cast::<usize>().cast_mut();
384
385        #[cfg(all(riscv, write_vec_table_monitoring))]
386        if crate::soc::trap_section_protected() {
387            crate::debugger::DEBUGGER_LOCK.lock(|| {
388                let wp = crate::debugger::clear_watchpoint(1);
389                ptr.write_volatile(handler.handler().address());
390                crate::debugger::restore_watchpoint(1, wp);
391            });
392            return;
393        }
394
395        ptr.write_volatile(handler.handler().address());
396    }
397}
398
399/// Enables a peripheral interrupt at a given priority, using vectored CPU interrupts.
400///
401/// Interrupts must still be enabled globally for interrupts to be serviced.
402///
403/// Internally maps the interrupt to the appropriate CPU interrupt
404/// for the specified priority level.
405#[inline]
406#[instability::unstable]
407pub fn enable(interrupt: Interrupt, level: Priority) {
408    enable_on_cpu(Cpu::current(), interrupt, level);
409}
410
411pub(crate) fn enable_on_cpu(cpu: Cpu, interrupt: Interrupt, level: Priority) {
412    let cpu_interrupt = priority_to_cpu_interrupt(interrupt, level);
413    map_raw(cpu, interrupt, cpu_interrupt as u32);
414}
415
416/// Disables the given peripheral interrupt.
417///
418/// Internally maps the interrupt to a disabled CPU interrupt.
419#[inline]
420#[instability::unstable]
421pub fn disable(core: Cpu, interrupt: Interrupt) {
422    map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
423}
424
425pub(super) fn map_raw(core: Cpu, interrupt: Interrupt, cpu_interrupt: u32) {
426    match core {
427        Cpu::ProCpu => {
428            INTERRUPT_CORE0::regs()
429                .core_0_intr_map(interrupt as usize)
430                .modify(|_, w| unsafe { w.map().bits(cpu_interrupt as u8) });
431        }
432        #[cfg(multi_core)]
433        Cpu::AppCpu => {
434            INTERRUPT_CORE1::regs()
435                .core_1_intr_map(interrupt as usize)
436                .modify(|_, w| unsafe { w.map().bits(cpu_interrupt as u8) });
437        }
438    }
439}
440
441/// Returns the CPU interrupt assigned to peripheral interrupt.
442#[cfg(all(feature = "rt", gpio_driver_supported))]
443pub(crate) fn mapped_to(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
444    mapped_to_raw(cpu, interrupt as u32)
445}
446
447#[cfg(feature = "rt")]
448pub(crate) fn mapped_to_raw(cpu: Cpu, interrupt: u32) -> Option<CpuInterrupt> {
449    let cpu_intr = match cpu {
450        Cpu::ProCpu => INTERRUPT_CORE0::regs()
451            .core_0_intr_map(interrupt as usize)
452            .read()
453            .map()
454            .bits() as u32,
455        #[cfg(multi_core)]
456        Cpu::AppCpu => INTERRUPT_CORE1::regs()
457            .core_1_intr_map(interrupt as usize)
458            .read()
459            .map()
460            .bits() as u32,
461    };
462    CpuInterrupt::from_u32(cpu_intr)
463}
464
465/// Represents the priority level of running code.
466///
467/// Interrupts at or below this level are masked.
468#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
469#[cfg_attr(feature = "defmt", derive(defmt::Format))]
470#[non_exhaustive]
471pub enum RunLevel {
472    /// Thread mode.
473    ///
474    /// This is the lowest level. No interrupts are masked by the run level.
475    ThreadMode,
476
477    /// An elevated level, usually an interrupt handler's.
478    Interrupt(ElevatedRunLevel),
479}
480
481impl RunLevel {
482    #[procmacros::doc_replace]
483    /// Returns the current run level.
484    ///
485    /// # Examples
486    ///
487    /// ```rust, no_run
488    /// # {before_snippet}
489    /// use esp_hal::interrupt::RunLevel;
490    ///
491    /// let level = RunLevel::current();
492    /// println!("Current run level: {:?}", level);
493    /// # {after_snippet}
494    /// ```
495    #[inline]
496    pub fn current() -> Self {
497        let raw = current_raw_runlevel();
498        unwrap!(Self::try_from_u32(raw))
499    }
500
501    /// Changes the current run level to the specified level and returns the previous level.
502    ///
503    /// # Safety
504    ///
505    /// Must only be used to raise the run level and to restore it to a previous
506    /// value. Must not be used to arbitrarily lower the run level.
507    #[inline]
508    #[instability::unstable]
509    pub unsafe fn change(to: Self) -> Self {
510        unsafe { change_current_runlevel(to) }
511    }
512
513    #[procmacros::doc_replace]
514    /// Returns whether the run level indicates thread mode.
515    ///
516    /// Thread mode means the CPU is not executing an interrupt handler.
517    ///
518    /// # Examples
519    ///
520    /// ```rust, no_run
521    /// # {before_snippet}
522    /// use esp_hal::interrupt::RunLevel;
523    ///
524    /// let level = RunLevel::current();
525    ///
526    /// if level.is_thread() {
527    ///     println!("Running in thread mode");
528    /// } else {
529    ///     println!("Running in an interrupt handler");
530    /// }
531    /// # {after_snippet}
532    /// ```
533    #[inline]
534    pub fn is_thread(&self) -> bool {
535        matches!(self, RunLevel::ThreadMode)
536    }
537
538    pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
539        if priority == 0 {
540            Ok(RunLevel::ThreadMode)
541        } else {
542            ElevatedRunLevel::try_from_u32(priority).map(RunLevel::Interrupt)
543        }
544    }
545}
546
547impl PartialEq<ElevatedRunLevel> for RunLevel {
548    fn eq(&self, other: &ElevatedRunLevel) -> bool {
549        *self == RunLevel::Interrupt(*other)
550    }
551}
552
553impl PartialEq<Priority> for RunLevel {
554    fn eq(&self, other: &Priority) -> bool {
555        let runlevel = ElevatedRunLevel::from(*other);
556        *self == RunLevel::Interrupt(runlevel)
557    }
558}
559
560impl From<RunLevel> for u32 {
561    fn from(level: RunLevel) -> Self {
562        match level {
563            RunLevel::ThreadMode => 0,
564            RunLevel::Interrupt(priority) => priority as u32,
565        }
566    }
567}
568
569/// Priority Level Error.
570#[derive(Copy, Clone, Debug, PartialEq, Eq)]
571#[cfg_attr(feature = "defmt", derive(defmt::Format))]
572#[instability::unstable]
573pub enum PriorityError {
574    /// The priority is not valid.
575    InvalidInterruptPriority,
576}
577
578#[instability::unstable]
579impl TryFrom<u32> for RunLevel {
580    type Error = PriorityError;
581
582    fn try_from(priority: u32) -> Result<Self, Self::Error> {
583        Self::try_from_u32(priority)
584    }
585}
586
587#[cfg(feature = "rt")]
588pub(crate) fn setup_interrupts() {
589    // disable all known peripheral interrupts
590    for peripheral_interrupt in 0..255 {
591        crate::peripherals::Interrupt::try_from(peripheral_interrupt)
592            .map(|intr| {
593                #[cfg(multi_core)]
594                disable(Cpu::AppCpu, intr);
595                disable(Cpu::ProCpu, intr);
596            })
597            .ok();
598    }
599
600    unsafe { crate::interrupt::init_vectoring() };
601}
602
603#[inline(always)]
604#[cfg(feature = "rt")]
605fn should_handle(core: Cpu, interrupt_nr: u32, level: u32) -> bool {
606    if let Some(cpu_interrupt) = crate::interrupt::mapped_to_raw(core, interrupt_nr)
607        && cpu_interrupt.is_vectored()
608        && cpu_interrupt.level() == level
609    {
610        true
611    } else {
612        false
613    }
614}