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