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