Skip to main content

esp_hal/interrupt/
riscv.rs

1//! Interrupt handling
2//!
3//! Peripheral interrupts go through the interrupt matrix, which routes them to the appropriate CPU
4//! interrupt line. The interrupt matrix is largely the same across devices, but CPU interrupts are
5//! device-specific before CLIC.
6//!
7//! Peripheral interrupts can be bound directly to CPU interrupts for better performance, but
8//! due to the limited number of CPU interrupts, the preferred mechanism is to use the vectored
9//! interrupts. The vectored interrupt handlers will call the appropriate interrupt handlers.
10//! The configuration of vectored interrupt handlers cannot be changed in runtime.
11
12#[cfg(feature = "rt")]
13#[instability::unstable]
14pub use esp_riscv_rt::TrapFrame;
15
16#[cfg_attr(interrupt_controller = "riscv_basic", path = "riscv/basic.rs")]
17#[cfg_attr(interrupt_controller = "plic", path = "riscv/plic.rs")]
18#[cfg_attr(interrupt_controller = "clic", path = "riscv/clic.rs")]
19mod cpu_int;
20
21// The software-interrupt driver is the only caller on this architecture, and that driver is
22// unstable.
23#[cfg(feature = "unstable")]
24pub(crate) use riscv::interrupt::free;
25
26use crate::{
27    interrupt::{PriorityError, RunLevel},
28    peripherals::Interrupt,
29    system::Cpu,
30};
31
32/// Interrupt kind
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34#[instability::unstable]
35pub enum InterruptKind {
36    /// Level interrupt
37    Level,
38    /// Edge interrupt
39    Edge,
40}
41
42for_each_interrupt!(
43    (all $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
44        paste::paste! {
45            /// Enumeration of available CPU interrupts.
46            #[repr(u32)]
47            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
49            #[instability::unstable]
50            pub enum CpuInterrupt {
51                $(
52                    #[doc = concat!(" Interrupt number ", stringify!($n), ".")]
53                    [<Interrupt $n>] = $n,
54                )*
55            }
56
57            impl CpuInterrupt {
58                #[inline]
59                pub(crate) fn from_u32(n: u32) -> Option<Self> {
60                    match n {
61                        $(n if n == $n && n != DISABLED_CPU_INTERRUPT => Some(Self:: [<Interrupt $n>]),)*
62                        _ => None
63                    }
64                }
65            }
66        }
67    };
68);
69
70for_each_classified_interrupt!(
71    (direct_bindable $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
72        paste::paste! {
73            /// Enumeration of CPU interrupts available for direct binding.
74            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
75            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
76            pub enum DirectBindableCpuInterrupt {
77                $(
78                    #[doc = concat!(" Direct bindable CPU interrupt number ", stringify!($idx_in_class), ".")]
79                    #[doc = " "]
80                    #[doc = concat!(" Corresponds to CPU interrupt ", stringify!($n), ".")]
81                    [<Interrupt $idx_in_class>] = $n,
82                )*
83            }
84
85            impl From<DirectBindableCpuInterrupt> for CpuInterrupt {
86                fn from(bindable: DirectBindableCpuInterrupt) -> CpuInterrupt {
87                    match bindable {
88                        $(
89                            DirectBindableCpuInterrupt::[<Interrupt $idx_in_class>] => CpuInterrupt::[<Interrupt $n>],
90                        )*
91                    }
92                }
93            }
94        }
95    };
96);
97
98impl CpuInterrupt {
99    #[inline]
100    #[cfg(feature = "rt")]
101    pub(crate) fn is_vectored(self) -> bool {
102        // Assumes contiguous interrupt allocation.
103        const VECTORED_CPU_INTERRUPT_RANGE: core::ops::RangeInclusive<u32> = PRIORITY_TO_INTERRUPT
104            [0] as u32
105            ..=PRIORITY_TO_INTERRUPT[PRIORITY_TO_INTERRUPT.len() - 1] as u32;
106        VECTORED_CPU_INTERRUPT_RANGE.contains(&(self as u32))
107    }
108
109    /// Enable the CPU interrupt
110    #[inline]
111    #[instability::unstable]
112    pub fn enable(self) {
113        cpu_int::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        cpu_int::clear_raw(self as u32);
121    }
122
123    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
124    ///
125    /// This is safe to call when the `vectored` feature is enabled. The
126    /// vectored interrupt handler will take care of clearing edge interrupt
127    /// bits.
128    #[inline]
129    #[instability::unstable]
130    pub fn set_kind(self, kind: InterruptKind) {
131        cpu_int::set_kind_raw(self as u32, kind);
132    }
133
134    /// Set the priority level of a CPU interrupt
135    #[inline]
136    #[instability::unstable]
137    pub fn set_priority(self, priority: Priority) {
138        cpu_int::set_priority_raw(self as u32, priority);
139    }
140
141    /// Get interrupt priority for the CPU
142    #[inline]
143    #[instability::unstable]
144    pub fn priority(self) -> Priority {
145        unwrap!(Priority::try_from_u32(self.level()))
146    }
147
148    #[inline]
149    pub(crate) fn level(self) -> u32 {
150        cpu_int::cpu_interrupt_priority_raw(self as u32) as u32
151    }
152}
153
154for_each_interrupt_priority!(
155    (all $( ($idx:literal, $n:literal, $ident:ident, $level:ident) ),*) => {
156        /// Interrupt priority levels.
157        ///
158        /// A higher numeric value means higher priority. Interrupt requests at higher priority
159        /// levels will be able to preempt code running at a lower [`RunLevel`][super::RunLevel].
160        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
161        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
162        #[repr(u8)]
163        pub enum Priority {
164            $(
165                #[doc = concat!(" Priority level ", stringify!($n), ".")]
166                $ident = $n,
167            )*
168        }
169
170        impl Priority {
171            fn iter() -> impl Iterator<Item = Priority> {
172                [$(Priority::$ident,)*].into_iter()
173            }
174        }
175
176        /// Interrupt run levels.
177        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
178        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
179        #[repr(u8)]
180        pub enum ElevatedRunLevel {
181            $(
182                #[doc = concat!("Run level ", stringify!($n), ".")]
183                $level = $n,
184            )*
185        }
186
187        impl ElevatedRunLevel {
188            /// Converts a [`Priority`] into an [`ElevatedRunLevel`].
189            pub const fn from_priority(priority: Priority) -> Self {
190                match priority {
191                    $(Priority::$ident => Self::$level,)*
192                }
193            }
194        }
195    };
196);
197
198impl Priority {
199    /// Maximum interrupt priority
200    #[allow(unused_assignments)]
201    #[instability::unstable]
202    pub const fn max() -> Priority {
203        const {
204            let mut last = Self::min();
205            for_each_interrupt_priority!(
206                ($_idx:literal, $_n:literal, $ident:ident, $_level:ident) => {
207                    last = Self::$ident;
208                };
209            );
210            last
211        }
212    }
213
214    /// Minimum interrupt priority
215    pub const fn min() -> Priority {
216        Priority::Priority1
217    }
218
219    pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
220        let result;
221        for_each_interrupt_priority!(
222            (all $( ($idx:literal, $n:literal, $ident:ident, $_level:ident) ),*) => {
223                result = match priority {
224                    $($n => Ok(Priority::$ident),)*
225                    _ => Err(PriorityError::InvalidInterruptPriority),
226                }
227            };
228        );
229        result
230    }
231}
232
233impl ElevatedRunLevel {
234    /// Returns the highest run level
235    #[instability::unstable]
236    pub const fn max() -> ElevatedRunLevel {
237        Self::from_priority(Priority::max())
238    }
239
240    /// Minimum elevated run level
241    pub const fn min() -> ElevatedRunLevel {
242        Self::from_priority(Priority::min())
243    }
244
245    pub(crate) fn try_from_u32(level: u32) -> Result<Self, PriorityError> {
246        Priority::try_from_u32(level).map(Self::from_priority)
247    }
248}
249
250#[instability::unstable]
251impl TryFrom<u32> for ElevatedRunLevel {
252    type Error = PriorityError;
253
254    fn try_from(value: u32) -> Result<Self, Self::Error> {
255        Self::try_from_u32(value)
256    }
257}
258
259impl From<Priority> for ElevatedRunLevel {
260    fn from(priority: Priority) -> Self {
261        Self::from_priority(priority)
262    }
263}
264
265#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
266pub(super) static DISABLED_CPU_INTERRUPT: u32 = property!("interrupts.disabled_interrupt");
267
268/// The number of vectored interrupts / The number of priority levels.
269const VECTOR_COUNT: usize = const {
270    let mut count = 0;
271    for_each_interrupt!(([vector $n:tt] $_:literal) => { count += 1; };);
272
273    core::assert!(count == Priority::max() as usize);
274
275    count
276};
277
278/// Maps priority levels to their corresponding interrupt vectors.
279#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
280pub(super) static PRIORITY_TO_INTERRUPT: [CpuInterrupt; VECTOR_COUNT] = const {
281    let mut counter = 0;
282    let mut vector = [CpuInterrupt::Interrupt0; VECTOR_COUNT];
283
284    for_each_interrupt!(
285        ([vector $_n:tt] $interrupt:literal) => {
286            vector[counter] = paste::paste! { CpuInterrupt::[<Interrupt $interrupt>] };
287            counter += 1;
288        };
289    );
290    vector
291};
292
293/// Enable an interrupt by directly binding it to an available CPU interrupt
294///
295/// ⚠️ This installs a *raw trap handler*, the `handler` user provides is written directly into the
296/// CPU interrupt vector table. That means:
297///
298/// - Provided handler will be used as an actual trap-handler
299/// - It is user's responsibility to:
300///   - Save and restore all registers they use.
301///   - Clear the interrupt source if necessary.
302///   - Return using the `mret` instruction.
303/// - The handler should be declared as naked function. The compiler will not insert a function
304///   prologue/epilogue for the user, normal Rust `fn` will result in an error.
305///
306/// Unless you are sure that you need such low-level control to achieve the lowest possible latency,
307/// you most likely want to use [`enable`][crate::interrupt::enable] instead.
308#[instability::unstable]
309pub fn enable_direct(
310    interrupt: Interrupt,
311    level: Priority,
312    cpu_interrupt: DirectBindableCpuInterrupt,
313    handler: unsafe extern "C" fn(),
314) {
315    cfg_select! {
316        interrupt_controller = "clic" => {
317            let clic = unsafe { crate::soc::pac::CLIC::steal() };
318
319            // Enable hardware vectoring
320            clic.int_attr(cpu_interrupt as usize).modify(|_, w| {
321                w.shv().hardware();
322                w.trig().positive_level()
323            });
324
325            let mtvt_table: *mut [u32; 48];
326            unsafe { core::arch::asm!("csrr {0}, 0x307", out(reg) mtvt_table) };
327
328            let int_slot = mtvt_table
329                .cast::<u32>()
330                .wrapping_add(cpu_interrupt as usize);
331
332            let instr = handler as usize as u32;
333        }
334        _ => {
335            use riscv::register::mtvec;
336            let mt = mtvec::read();
337
338            assert_eq!(
339                mt.trap_mode().into_usize(),
340                mtvec::TrapMode::Vectored.into_usize()
341            );
342
343            let base_addr = mt.address() as usize;
344
345            let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4) as *mut u32;
346
347            let instr = encode_jal_x0(handler as usize, int_slot as usize);
348        }
349    }
350
351    if crate::debugger::debugger_connected() {
352        unsafe { core::ptr::write_volatile(int_slot, instr) };
353    } else {
354        crate::debugger::DEBUGGER_LOCK.lock(|| unsafe {
355            let wp = crate::debugger::clear_watchpoint(1);
356            core::ptr::write_volatile(int_slot, instr);
357            crate::debugger::restore_watchpoint(1, wp);
358        });
359    }
360    unsafe {
361        core::arch::asm!("fence.i");
362    }
363
364    #[cfg(esp32p4)]
365    unsafe {
366        // Write back the cache to make sure the new interrupt handler is visible to the CPU.
367        crate::soc::cache_writeback_addr(mtvt_table as u32, 48 * 4);
368        // Invalidate the cache to make sure the CPU does not read from a stale instruction cache.
369        crate::soc::cache_invalidate_icache_addr(mtvt_table as u32, 48 * 4);
370    }
371
372    super::map_raw(Cpu::current(), interrupt, cpu_interrupt as u32);
373    cpu_int::set_priority_raw(cpu_interrupt as u32, level);
374    cpu_int::set_kind_raw(cpu_interrupt as u32, InterruptKind::Level);
375    cpu_int::enable_cpu_interrupt_raw(cpu_interrupt as u32);
376}
377
378// helper: returns correctly encoded RISC-V `jal` instruction
379#[cfg(not(interrupt_controller = "clic"))]
380fn encode_jal_x0(target: usize, pc: usize) -> u32 {
381    let offset = (target as isize) - (pc as isize);
382
383    const MIN: isize = -(1isize << 20);
384    const MAX: isize = (1isize << 20) - 1;
385
386    assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));
387
388    let imm = offset as u32;
389    let imm20 = (imm >> 20) & 0x1;
390    let imm10_1 = (imm >> 1) & 0x3ff;
391    let imm11 = (imm >> 11) & 0x1;
392    let imm19_12 = (imm >> 12) & 0xff;
393
394    (imm20 << 31)
395        | (imm19_12 << 12)
396        | (imm11 << 20)
397        | (imm10_1 << 21)
398        // https://lhtin.github.io/01world/app/riscv-isa/?xlen=32&insn_name=jal
399        | 0b1101111u32
400}
401
402// Runlevel APIs
403
404/// Get the current run level (the level below which interrupts are masked).
405pub(crate) fn current_raw_runlevel() -> u32 {
406    cpu_int::current_runlevel() as u32
407}
408
409/// Changes the current run level (the level below which interrupts are
410/// masked), and returns the previous run level.
411///
412/// # Safety
413///
414/// This function must only be used to raise the runlevel and to restore it
415/// to a previous value. It must not be used to arbitrarily lower the
416/// runlevel.
417pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
418    let previous = cpu_int::change_current_runlevel(level);
419    unwrap!(RunLevel::try_from_u32(previous as u32))
420}
421
422fn cpu_wait_mode_on() -> bool {
423    cfg_select! {
424        soc_has_pcr => crate::peripherals::PCR::regs()
425            .cpu_waiti_conf()
426            .read()
427            .cpu_wait_mode_force_on()
428            .bit_is_set(),
429        soc_has_hp_sys => crate::peripherals::HP_SYS::regs()
430            .cpu_waiti_conf()
431            .read()
432            .cpu_wait_mode_force_on()
433            .bit_is_set(),
434        _ => crate::peripherals::SYSTEM::regs()
435            .cpu_per_conf()
436            .read()
437            .cpu_wait_mode_force_on()
438            .bit_is_set(),
439    }
440}
441
442/// Wait for an interrupt to occur.
443///
444/// This function causes the current CPU core to execute its Wait For Interrupt
445/// (WFI or equivalent) instruction. After executing this function, the CPU core
446/// will stop execution until an interrupt occurs.
447///
448/// This function will return immediately when a debugger is attached, so it is intended to be
449/// called in a loop.
450#[inline(always)]
451#[instability::unstable]
452pub fn wait_for_interrupt() {
453    if crate::debugger::debugger_connected() && !cpu_wait_mode_on() {
454        // when SYSTEM_CPU_WAIT_MODE_FORCE_ON is disabled in WFI mode SBA access to memory does not
455        // work for debugger, so do not enter that mode when debugger is connected.
456        // https://github.com/espressif/esp-idf/blob/b9a308a47ca4128d018495662b009a7c461b6780/components/esp_hw_support/cpu.c#L57-L60
457        return;
458    }
459    unsafe { core::arch::asm!("wfi") };
460}
461
462pub(crate) fn priority_to_cpu_interrupt(_interrupt: Interrupt, level: Priority) -> CpuInterrupt {
463    PRIORITY_TO_INTERRUPT[(level as usize) - 1]
464}
465
466/// Setup interrupts ready for vectoring
467///
468/// # Safety
469///
470/// This function must be called only during core startup.
471#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
472pub(crate) unsafe fn init_vectoring() {
473    use riscv::register::mtvec;
474
475    unsafe extern "C" {
476        static _vector_table: u32;
477    }
478
479    unsafe {
480        let vec_table = (&raw const _vector_table).addr();
481
482        #[cfg(not(interrupt_controller = "clic"))]
483        {
484            mtvec::write({
485                let mut mtvec = mtvec::Mtvec::from_bits(0);
486                mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
487                mtvec.set_address(vec_table);
488                mtvec
489            });
490        }
491
492        #[cfg(interrupt_controller = "clic")]
493        {
494            mtvec::write({
495                let mut mtvec = mtvec::Mtvec::from_bits(0x03); // MODE = CLIC
496                mtvec.set_address(vec_table);
497                mtvec
498            });
499
500            // set mtvt (hardware vector base)
501            let mtvt_table = match Cpu::current() {
502                Cpu::ProCpu => {
503                    unsafe extern "C" {
504                        static _mtvt_table: u32;
505                    }
506                    (&raw const _mtvt_table).addr()
507                }
508                #[cfg(multi_core)]
509                Cpu::AppCpu => {
510                    unsafe extern "C" {
511                        static _mtvt_table2: u32;
512                    }
513                    (&raw const _mtvt_table2).addr()
514                }
515            };
516            core::arch::asm!("csrw 0x307, {0}", in(reg) mtvt_table);
517        }
518    };
519
520    // Configure CLIC for hardware-vectored mode (shv=1) and set nlbits.
521    // Must run on each core since CLIC control registers are per-core.
522    #[cfg(feature = "rt")]
523    cpu_int::init();
524
525    // Configure and enable vectored interrupts
526    for (int, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(Priority::iter()) {
527        let num = int as u32;
528        cpu_int::set_kind_raw(num, InterruptKind::Level);
529        cpu_int::set_priority_raw(num, prio);
530        cpu_int::enable_cpu_interrupt_raw(num);
531    }
532}
533
534#[cfg(feature = "rt")]
535pub(crate) mod rt {
536    use esp_riscv_rt::TrapFrame;
537    use riscv::register::mcause;
538
539    use super::*;
540    use crate::interrupt::InterruptStatus;
541
542    /// The total number of interrupts.
543    #[cfg(not(interrupt_controller = "clic"))]
544    const INTERRUPT_COUNT: usize = const {
545        let mut count = 0;
546        for_each_interrupt!(([$_class:tt $n:tt] $_:literal) => { count += 1; };);
547        count
548    };
549
550    /// Maps interrupt numbers to their vector priority levels.
551    #[cfg(not(interrupt_controller = "clic"))]
552    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
553    pub(super) static INTERRUPT_TO_PRIORITY: [Option<Priority>; INTERRUPT_COUNT] = const {
554        let mut priorities = [None; INTERRUPT_COUNT];
555
556        for_each_interrupt!(
557            ([vector $n:tt] $int:literal) => {
558                for_each_interrupt_priority!(($n, $__:tt, $ident:ident, $_level:ident) => { priorities[$int] = Some(Priority::$ident); };);
559            };
560        );
561
562        priorities
563    };
564
565    /// # Safety
566    ///
567    /// This function is called from an assembly trap handler.
568    #[doc(hidden)]
569    #[unsafe(link_section = ".trap.rust")]
570    #[unsafe(export_name = "_start_trap_rust_hal")]
571    unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
572        assert!(
573            mcause::read().is_exception(),
574            "Arrived into _start_trap_rust_hal but mcause is not an exception!"
575        );
576        unsafe extern "C" {
577            fn ExceptionHandler(tf: *mut TrapFrame);
578        }
579        unsafe {
580            ExceptionHandler(trap_frame);
581        }
582    }
583
584    #[doc(hidden)]
585    #[unsafe(no_mangle)]
586    #[unsafe(link_section = ".init")]
587    unsafe fn _setup_interrupts() {
588        crate::soc::riscv_preinit();
589        crate::interrupt::setup_interrupts();
590
591        #[cfg(interrupt_controller = "plic")]
592        unsafe {
593            core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
594        }
595    }
596
597    #[unsafe(no_mangle)]
598    #[crate::ram]
599    unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
600        let status = InterruptStatus::current();
601
602        // this has no effect on level interrupts, but the interrupt may be an edge one
603        // so we clear it anyway
604        cpu_intr.clear();
605
606        cfg_select! {
607            interrupt_controller = "clic" => {
608                let prio = cpu_int::current_runlevel();
609                let mcause = riscv::register::mcause::read();
610            }
611            _ => {
612                // Change the current runlevel so that interrupt handlers can access the correct
613                // runlevel.
614                let prio = unwrap!(INTERRUPT_TO_PRIORITY[cpu_intr as usize]);
615                let level = unsafe {
616                    change_current_runlevel(RunLevel::Interrupt(ElevatedRunLevel::from(prio)))
617                };
618                let prio = prio as u8;
619            }
620        }
621
622        let handle_interrupts = || unsafe {
623            for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
624                crate::interrupt::should_handle(Cpu::current(), interrupt_nr as u32, prio as u32)
625            }) {
626                let handler =
627                    crate::soc::pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler;
628
629                handler();
630            }
631        };
632
633        // Do not enable nesting on the highest priority level. Older interrupt controllers couldn't
634        // properly mask the highest priority interrupt, and for CLIC we don't want to waste
635        // the cycles it takes to enable nesting unnecessarily.
636        if prio != Priority::max() as u8 {
637            unsafe {
638                riscv::interrupt::nested(handle_interrupts);
639            }
640        } else {
641            handle_interrupts();
642        }
643
644        cfg_select! {
645            interrupt_controller = "clic" => {
646                // In case the target uses the CLIC, it is mandatory to restore `mcause` register
647                // since it contains the former CPU priority. When executing `mret`,
648                // the hardware will restore the former threshold, from `mcause` to
649                // `mintstatus` CSR
650                unsafe { core::arch::asm!("csrw 0x342, {}", in(reg) mcause.bits()) }
651            }
652            _ => {
653                unsafe { change_current_runlevel(level) };
654            }
655        }
656    }
657}