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

            
44
The [`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

            
60
The HAL defines the `Software0` interrupt handler, and switches tasks in that interrupt.
61
"
62
)]
63

            
64
use crate::{peripherals::Interrupt, system::Cpu};
65

            
66
cfg_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")]
79
mod arch;
80
pub use arch::*;
81

            
82
use crate::pac;
83

            
84
unstable_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)]
97
extern "C" fn EspDefaultHandler() {
98
    panic!("Unhandled interrupt on {:?}", crate::system::Cpu::current());
99
}
100

            
101
/// Default (unhandled) interrupt handler
102
#[instability::unstable]
103
pub 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]
119
pub 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]
138
pub struct IsrCallback {
139
    f: extern "C" fn(),
140
}
141

            
142
impl 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

            
162
impl 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
174
on 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]
179
pub struct InterruptHandler {
180
    f: extern "C" fn(),
181
    prio: Priority,
182
}
183

            
184
impl 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

            
204
const STATUS_WORDS: usize = property!("interrupts.status_registers");
205

            
206
/// Representation of peripheral-interrupt status bits.
207
#[derive(Clone, Copy, Default, Debug)]
208
#[instability::unstable]
209
pub struct InterruptStatus {
210
    status: [u32; STATUS_WORDS],
211
}
212

            
213
impl 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]
300
pub struct InterruptStatusIterator {
301
    status: InterruptStatus,
302
    idx: usize,
303
}
304

            
305
impl 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
6212
            if self.status.status[i] != 0 {
311
                let bit = self.status.status[i].trailing_zeros();
312
2081
                self.status.status[i] ^= 1 << bit;
313
2081
                self.idx = i;
314
2081
                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

            
324
fn 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]
351
pub fn bound_handler(interrupt: Interrupt) -> Option<IsrCallback> {
352
    unsafe {
353
        let vector = vector_entry(interrupt);
354

            
355
325
        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]
374
19
pub fn bind_handler(interrupt: Interrupt, handler: InterruptHandler) {
375
    bind_vector(interrupt, handler);
376
    enable(interrupt, handler.priority());
377
19
}
378

            
379
/// Binds `handler` to `interrupt` without enabling the peripheral interrupt.
380
pub(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]
407
pub fn enable(interrupt: Interrupt, level: Priority) {
408
    enable_on_cpu(Cpu::current(), interrupt, level);
409
}
410

            
411
pub(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]
421
pub fn disable(core: Cpu, interrupt: Interrupt) {
422
28
    map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
423
}
424

            
425
29
pub(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
29
}
440

            
441
/// Returns the CPU interrupt assigned to peripheral interrupt.
442
#[cfg(all(feature = "rt", gpio_driver_supported))]
443
pub(crate) fn mapped_to(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
444
    mapped_to_raw(cpu, interrupt as u32)
445
}
446

            
447
#[cfg(feature = "rt")]
448
pub(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
3
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
469
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
470
#[non_exhaustive]
471
pub 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

            
481
impl 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
71
        let raw = current_raw_runlevel();
498
73
        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
71
    pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
539
2070
        if priority == 0 {
540
            Ok(RunLevel::ThreadMode)
541
        } else {
542
            ElevatedRunLevel::try_from_u32(priority).map(RunLevel::Interrupt)
543
        }
544
71
    }
545
}
546

            
547
impl PartialEq<ElevatedRunLevel> for RunLevel {
548
    fn eq(&self, other: &ElevatedRunLevel) -> bool {
549
        *self == RunLevel::Interrupt(*other)
550
    }
551
}
552

            
553
impl 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

            
560
impl From<RunLevel> for u32 {
561
    fn from(level: RunLevel) -> Self {
562
2075
        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]
573
pub enum PriorityError {
574
    /// The priority is not valid.
575
    InvalidInterruptPriority,
576
}
577

            
578
#[instability::unstable]
579
impl 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")]
588
pub(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")]
605
fn 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
}