esp_hal/interrupt/
riscv.rs

1//! Interrupt handling
2//!
3//! CPU interrupts 1 through 15 are reserved for each of the possible interrupt
4//! priorities.
5//!
6//! On chips with a PLIC CPU interrupts 1,2,5,6,9 .. 19 are used.
7//!
8//! ```rust, ignore
9//! interrupt1() => Priority::Priority1
10//! interrupt2() => Priority::Priority2
11//! ...
12//! interrupt15() => Priority::Priority15
13//! ```
14
15#[cfg(feature = "rt")]
16pub use esp_riscv_rt::TrapFrame;
17use procmacros::ram;
18use riscv::register::{mcause, mtvec};
19
20#[cfg(not(plic))]
21pub use self::classic::*;
22#[cfg(plic)]
23pub use self::plic::*;
24pub use self::vectored::*;
25use super::InterruptStatus;
26use crate::{
27    pac,
28    peripherals::{INTERRUPT_CORE0, Interrupt},
29    system::Cpu,
30};
31
32/// Interrupt Error
33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub enum Error {
36    /// The priority is not valid
37    InvalidInterruptPriority,
38    /// The CPU interrupt is a reserved interrupt
39    CpuInterruptReserved,
40}
41
42/// Interrupt kind
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44pub enum InterruptKind {
45    /// Level interrupt
46    Level,
47    /// Edge interrupt
48    Edge,
49}
50
51/// Enumeration of available CPU interrupts.
52/// It is possible to create a handler for each of the interrupts. (e.g.
53/// `interrupt3`)
54#[repr(u32)]
55#[derive(Debug, Copy, Clone)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57pub enum CpuInterrupt {
58    /// Interrupt number 1.
59    Interrupt1 = 1,
60    /// Interrupt number 2.
61    Interrupt2,
62    /// Interrupt number 3.
63    Interrupt3,
64    /// Interrupt number 4.
65    Interrupt4,
66    /// Interrupt number 5.
67    Interrupt5,
68    /// Interrupt number 6.
69    Interrupt6,
70    /// Interrupt number 7.
71    Interrupt7,
72    /// Interrupt number 8.
73    Interrupt8,
74    /// Interrupt number 9.
75    Interrupt9,
76    /// Interrupt number 10.
77    Interrupt10,
78    /// Interrupt number 11.
79    Interrupt11,
80    /// Interrupt number 12.
81    Interrupt12,
82    /// Interrupt number 13.
83    Interrupt13,
84    /// Interrupt number 14.
85    Interrupt14,
86    /// Interrupt number 15.
87    Interrupt15,
88    /// Interrupt number 16.
89    Interrupt16,
90    /// Interrupt number 17.
91    Interrupt17,
92    /// Interrupt number 18.
93    Interrupt18,
94    /// Interrupt number 19.
95    Interrupt19,
96    /// Interrupt number 20.
97    Interrupt20,
98    /// Interrupt number 21.
99    Interrupt21,
100    /// Interrupt number 22.
101    Interrupt22,
102    /// Interrupt number 23.
103    Interrupt23,
104    /// Interrupt number 24.
105    Interrupt24,
106    /// Interrupt number 25.
107    Interrupt25,
108    /// Interrupt number 26.
109    Interrupt26,
110    /// Interrupt number 27.
111    Interrupt27,
112    /// Interrupt number 28.
113    Interrupt28,
114    /// Interrupt number 29.
115    Interrupt29,
116    /// Interrupt number 30.
117    Interrupt30,
118    /// Interrupt number 31.
119    Interrupt31,
120}
121
122/// Interrupt priority levels.
123#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
124#[cfg_attr(feature = "defmt", derive(defmt::Format))]
125#[repr(u8)]
126pub enum Priority {
127    /// No priority.
128    None = 0,
129    /// Priority level 1.
130    Priority1,
131    /// Priority level 2.
132    Priority2,
133    /// Priority level 3.
134    Priority3,
135    /// Priority level 4.
136    Priority4,
137    /// Priority level 5.
138    Priority5,
139    /// Priority level 6.
140    Priority6,
141    /// Priority level 7.
142    Priority7,
143    /// Priority level 8.
144    Priority8,
145    /// Priority level 9.
146    Priority9,
147    /// Priority level 10.
148    Priority10,
149    /// Priority level 11.
150    Priority11,
151    /// Priority level 12.
152    Priority12,
153    /// Priority level 13.
154    Priority13,
155    /// Priority level 14.
156    Priority14,
157    /// Priority level 15.
158    Priority15,
159}
160
161impl Priority {
162    /// Maximum interrupt priority
163    pub const fn max() -> Priority {
164        Priority::Priority15
165    }
166
167    /// Minimum interrupt priority
168    pub const fn min() -> Priority {
169        Priority::Priority1
170    }
171}
172
173impl TryFrom<u32> for Priority {
174    type Error = Error;
175
176    fn try_from(value: u32) -> Result<Self, Self::Error> {
177        match value {
178            0 => Ok(Priority::None),
179            1 => Ok(Priority::Priority1),
180            2 => Ok(Priority::Priority2),
181            3 => Ok(Priority::Priority3),
182            4 => Ok(Priority::Priority4),
183            5 => Ok(Priority::Priority5),
184            6 => Ok(Priority::Priority6),
185            7 => Ok(Priority::Priority7),
186            8 => Ok(Priority::Priority8),
187            9 => Ok(Priority::Priority9),
188            10 => Ok(Priority::Priority10),
189            11 => Ok(Priority::Priority11),
190            12 => Ok(Priority::Priority12),
191            13 => Ok(Priority::Priority13),
192            14 => Ok(Priority::Priority14),
193            15 => Ok(Priority::Priority15),
194            _ => Err(Error::InvalidInterruptPriority),
195        }
196    }
197}
198
199impl TryFrom<u8> for Priority {
200    type Error = Error;
201
202    fn try_from(value: u8) -> Result<Self, Self::Error> {
203        Priority::try_from(value as u32)
204    }
205}
206
207/// The interrupts reserved by the HAL
208#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
209pub static RESERVED_INTERRUPTS: &[u32] = PRIORITY_TO_INTERRUPT;
210
211/// Enable an interrupt by directly binding it to a available CPU interrupt
212///
213/// ⚠️ This installs a *raw trap handler*, the `handler` user provides is written directly into the
214/// CPU interrupt vector table. That means:
215///
216/// - Provided handler will be used as an actual trap-handler
217/// - It is user's responsibility to:
218///   - Save and restore all registers they use.
219///   - Clear the interrupt source if necessary.
220///   - Return using the `mret` instruction.
221/// - The handler should be declared as naked function. The compiler will not insert a function
222///   prologue/epilogue for the user, normal Rust `fn` will result in an error.
223///
224/// Unless you are sure that you need such low-level control to achieve the lowest possible latency,
225/// you most likely want to use [`enable`] instead.
226///
227/// Trying using a reserved interrupt from [`RESERVED_INTERRUPTS`] will return
228/// an error.
229pub fn enable_direct(
230    interrupt: Interrupt,
231    level: Priority,
232    cpu_interrupt: CpuInterrupt,
233    handler: unsafe extern "C" fn(),
234) -> Result<(), Error> {
235    if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
236        return Err(Error::CpuInterruptReserved);
237    }
238    if matches!(level, Priority::None) {
239        return Err(Error::InvalidInterruptPriority);
240    }
241    unsafe {
242        map(Cpu::current(), interrupt, cpu_interrupt);
243        set_priority(Cpu::current(), cpu_interrupt, level);
244
245        let mt = mtvec::read();
246
247        assert_eq!(
248            mt.trap_mode().into_usize(),
249            mtvec::TrapMode::Vectored.into_usize()
250        );
251
252        let base_addr = mt.address() as usize;
253
254        let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4);
255
256        let instr = encode_jal_x0(handler as usize, int_slot)?;
257
258        if crate::debugger::debugger_connected() {
259            core::ptr::write_volatile(int_slot as *mut u32, instr);
260        } else {
261            crate::debugger::DEBUGGER_LOCK.lock(|| {
262                let wp = crate::debugger::clear_watchpoint(1);
263                core::ptr::write_volatile(int_slot as *mut u32, instr);
264                crate::debugger::restore_watchpoint(1, wp);
265            });
266        }
267
268        core::arch::asm!("fence.i");
269
270        enable_cpu_interrupt(cpu_interrupt);
271    }
272    Ok(())
273}
274
275// helper: returns correctly encoded RISC-V `jal` instruction
276fn encode_jal_x0(target: usize, pc: usize) -> Result<u32, Error> {
277    let offset = (target as isize) - (pc as isize);
278
279    const MIN: isize = -(1isize << 20);
280    const MAX: isize = (1isize << 20) - 1;
281
282    assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));
283
284    let imm = offset as u32;
285    let imm20 = (imm >> 20) & 0x1;
286    let imm10_1 = (imm >> 1) & 0x3ff;
287    let imm11 = (imm >> 11) & 0x1;
288    let imm19_12 = (imm >> 12) & 0xff;
289
290    let instr = (imm20 << 31)
291        | (imm19_12 << 12)
292        | (imm11 << 20)
293        | (imm10_1 << 21)
294        // https://lhtin.github.io/01world/app/riscv-isa/?xlen=32&insn_name=jal
295        | 0b1101111u32;
296
297    Ok(instr)
298}
299
300/// Disable the given peripheral interrupt.
301pub fn disable(core: Cpu, interrupt: Interrupt) {
302    map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
303}
304
305/// Get status of peripheral interrupts
306#[inline]
307pub fn status(_core: Cpu) -> InterruptStatus {
308    cfg_if::cfg_if! {
309        if #[cfg(interrupts_status_registers = "3")] {
310            InterruptStatus::from(
311                INTERRUPT_CORE0::regs().core_0_intr_status(0).read().bits(),
312                INTERRUPT_CORE0::regs().core_0_intr_status(1).read().bits(),
313                INTERRUPT_CORE0::regs().core_0_intr_status(2).read().bits(),
314            )
315        } else if #[cfg(interrupts_status_registers = "4")] {
316            InterruptStatus::from(
317                INTERRUPT_CORE0::regs().core_0_intr_status(0).read().bits(),
318                INTERRUPT_CORE0::regs().core_0_intr_status(1).read().bits(),
319                INTERRUPT_CORE0::regs().core_0_intr_status(2).read().bits(),
320                INTERRUPT_CORE0::regs().core_0_intr_status(3).read().bits(),
321            )
322        } else {
323            InterruptStatus::from(
324                INTERRUPT_CORE0::regs().core_0_intr_status(0).read().bits(),
325                INTERRUPT_CORE0::regs().core_0_intr_status(1).read().bits(),
326            )
327        }
328    }
329}
330
331/// Assign a peripheral interrupt to an CPU interrupt.
332///
333/// # Safety
334///
335/// Do not use CPU interrupts in the [`RESERVED_INTERRUPTS`].
336pub unsafe fn map(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
337    map_raw(core, interrupt, which as u32)
338}
339
340fn map_raw(core: Cpu, interrupt: Interrupt, cpu_interrupt_number: u32) {
341    let interrupt_number = interrupt as usize;
342
343    match core {
344        Cpu::ProCpu => {
345            INTERRUPT_CORE0::regs()
346                .core_0_intr_map(interrupt_number)
347                .write(|w| unsafe { w.bits(cpu_interrupt_number) });
348        }
349        #[cfg(multi_core)]
350        Cpu::AppCpu => {
351            INTERRUPT_CORE1::regs()
352                .core_1_intr_map(interrupt_number)
353                .write(|w| unsafe { w.bits(cpu_interrupt_number) });
354        }
355    }
356}
357
358/// Get cpu interrupt assigned to peripheral interrupt
359#[inline]
360unsafe fn assigned_cpu_interrupt(interrupt: Interrupt) -> Option<CpuInterrupt> {
361    let cpu_intr = INTERRUPT_CORE0::regs()
362        .core_0_intr_map(interrupt as usize)
363        .read()
364        .bits();
365
366    if cpu_intr > 0 && cpu_intr != DISABLED_CPU_INTERRUPT {
367        Some(unsafe { core::mem::transmute::<u32, CpuInterrupt>(cpu_intr) })
368    } else {
369        None
370    }
371}
372
373pub(crate) fn bound_cpu_interrupt_for(_cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
374    unsafe { assigned_cpu_interrupt(interrupt) }
375}
376
377mod vectored {
378    use super::*;
379    use crate::interrupt::IsrCallback;
380
381    // Setup interrupts ready for vectoring
382    #[doc(hidden)]
383    pub(crate) unsafe fn init_vectoring() {
384        for (num, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(1..) {
385            let which = unsafe { core::mem::transmute::<u32, CpuInterrupt>(num) };
386            set_kind(Cpu::current(), which, InterruptKind::Level);
387            unsafe {
388                set_priority(
389                    Cpu::current(),
390                    which,
391                    core::mem::transmute::<u8, Priority>(prio),
392                );
393                enable_cpu_interrupt(which);
394            }
395        }
396    }
397
398    /// Get the interrupts configured for the core at the given priority
399    /// matching the given status
400    #[inline]
401    pub(crate) fn configured_interrupts(
402        core: Cpu,
403        status: InterruptStatus,
404        priority: Priority,
405    ) -> InterruptStatus {
406        unsafe {
407            let mut res = InterruptStatus::empty();
408
409            for interrupt_nr in status.iterator() {
410                // safety: cast is safe because of repr(u16)
411                if let Some(cpu_interrupt) =
412                    assigned_cpu_interrupt(core::mem::transmute::<u16, Interrupt>(
413                        interrupt_nr as u16,
414                    ))
415                    && priority_by_core(core, cpu_interrupt) == priority
416                {
417                    res.set(interrupt_nr);
418                }
419            }
420            res
421        }
422    }
423
424    /// Enables a interrupt at a given priority
425    ///
426    /// Note that interrupts still need to be enabled globally for interrupts
427    /// to be serviced.
428    pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
429        enable_on_cpu(Cpu::current(), interrupt, level)
430    }
431
432    pub(crate) fn enable_on_cpu(
433        cpu: Cpu,
434        interrupt: Interrupt,
435        level: Priority,
436    ) -> Result<(), Error> {
437        if matches!(level, Priority::None) {
438            return Err(Error::InvalidInterruptPriority);
439        }
440        unsafe {
441            let cpu_interrupt = core::mem::transmute::<u32, CpuInterrupt>(
442                PRIORITY_TO_INTERRUPT[(level as usize) - 1],
443            );
444            map(cpu, interrupt, cpu_interrupt);
445            enable_cpu_interrupt(cpu_interrupt);
446        }
447        Ok(())
448    }
449
450    /// Binds the given interrupt to the given handler.
451    ///
452    /// # Safety
453    ///
454    /// This will replace any previously bound interrupt handler
455    pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: IsrCallback) {
456        unsafe {
457            let ptr =
458                &pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as *const _ as *mut usize;
459
460            if crate::debugger::debugger_connected() {
461                ptr.write_volatile(handler.raw_value());
462            } else {
463                crate::debugger::DEBUGGER_LOCK.lock(|| {
464                    let wp = crate::debugger::clear_watchpoint(1);
465                    ptr.write_volatile(handler.raw_value());
466                    crate::debugger::restore_watchpoint(1, wp);
467                });
468            }
469        }
470    }
471
472    /// Returns the currently bound interrupt handler.
473    pub fn bound_handler(interrupt: Interrupt) -> Option<IsrCallback> {
474        unsafe {
475            let addr = pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as usize;
476            if addr == 0 {
477                return None;
478            }
479
480            Some(IsrCallback::from_raw(addr))
481        }
482    }
483}
484
485#[cfg(not(plic))]
486mod classic {
487    use super::{CpuInterrupt, InterruptKind, Priority};
488    use crate::{peripherals::INTERRUPT_CORE0, system::Cpu};
489
490    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
491    pub(super) static DISABLED_CPU_INTERRUPT: u32 = 0;
492
493    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
494    pub(super) static PRIORITY_TO_INTERRUPT: &[u32] =
495        &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
496
497    // First element is not used, just there to avoid a -1 in the interrupt handler.
498    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
499    pub(super) static INTERRUPT_TO_PRIORITY: [Priority; 16] = [
500        Priority::None,
501        Priority::Priority1,
502        Priority::Priority2,
503        Priority::Priority3,
504        Priority::Priority4,
505        Priority::Priority5,
506        Priority::Priority6,
507        Priority::Priority7,
508        Priority::Priority8,
509        Priority::Priority9,
510        Priority::Priority10,
511        Priority::Priority11,
512        Priority::Priority12,
513        Priority::Priority13,
514        Priority::Priority14,
515        Priority::Priority15,
516    ];
517
518    /// Enable a CPU interrupt
519    ///
520    /// # Safety
521    ///
522    /// Make sure there is an interrupt handler registered.
523    pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
524        let cpu_interrupt_number = which as isize;
525        let intr = INTERRUPT_CORE0::regs();
526        intr.cpu_int_enable()
527            .modify(|r, w| unsafe { w.bits((1 << cpu_interrupt_number) | r.bits()) });
528    }
529
530    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
531    ///
532    /// The vectored interrupt handler will take care of clearing edge interrupt
533    /// bits.
534    pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
535        unsafe {
536            let intr = INTERRUPT_CORE0::regs();
537            let cpu_interrupt_number = which as isize;
538
539            let interrupt_type = match kind {
540                InterruptKind::Level => 0,
541                InterruptKind::Edge => 1,
542            };
543            intr.cpu_int_type().modify(|r, w| {
544                w.bits(
545                    r.bits() & !(1 << cpu_interrupt_number)
546                        | (interrupt_type << cpu_interrupt_number),
547                )
548            });
549        }
550    }
551
552    /// Set the priority level of an CPU interrupt
553    ///
554    /// # Safety
555    ///
556    /// Great care must be taken when using this function; avoid changing the
557    /// priority of interrupts 1 - 15.
558    pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
559        let intr = INTERRUPT_CORE0::regs();
560        intr.cpu_int_pri(which as usize)
561            .write(|w| unsafe { w.map().bits(priority as u8) });
562    }
563
564    /// Clear a CPU interrupt
565    #[inline]
566    pub fn clear(_core: Cpu, which: CpuInterrupt) {
567        unsafe {
568            let cpu_interrupt_number = which as usize;
569            let intr = INTERRUPT_CORE0::regs();
570            intr.cpu_int_clear()
571                .write(|w| w.bits(1 << cpu_interrupt_number));
572        }
573    }
574
575    /// Get interrupt priority
576    #[inline]
577    pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
578        priority(cpu_interrupt)
579    }
580
581    /// Get interrupt priority - called by assembly code
582    #[inline]
583    pub(super) fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
584        let intr = INTERRUPT_CORE0::regs();
585        unsafe {
586            core::mem::transmute::<u8, Priority>(
587                intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(),
588            )
589        }
590    }
591
592    /// Get the current run level (the level below which interrupts are masked).
593    pub fn current_runlevel() -> Priority {
594        let intr = INTERRUPT_CORE0::regs();
595        let prev_interrupt_priority = intr.cpu_int_thresh().read().bits().saturating_sub(1) as u8;
596
597        unwrap!(Priority::try_from(prev_interrupt_priority))
598    }
599
600    /// Changes the current run level (the level below which interrupts are
601    /// masked), and returns the previous run level.
602    ///
603    /// # Safety
604    ///
605    /// This function must only be used to raise the runlevel and to restore it
606    /// to a previous value. It must not be used to arbitrarily lower the
607    /// runlevel.
608    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
609        let prev_interrupt_priority = current_runlevel();
610
611        // The CPU responds to interrupts `>= level`, but we want to also disable
612        // interrupts at `level` so we set the threshold to `level + 1`.
613        INTERRUPT_CORE0::regs()
614            .cpu_int_thresh()
615            .write(|w| unsafe { w.bits(level as u32 + 1) });
616
617        prev_interrupt_priority
618    }
619}
620
621#[cfg(plic)]
622mod plic {
623    use super::{CpuInterrupt, InterruptKind, Priority};
624    use crate::{peripherals::PLIC_MX, system::Cpu};
625
626    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
627    pub(super) static DISABLED_CPU_INTERRUPT: u32 = 31;
628
629    // don't use interrupts reserved for CLIC (0,3,4,7)
630    // for some reason also CPU interrupt 8 doesn't work by default since it's
631    // disabled after reset - so don't use that, too
632    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
633    pub(super) static PRIORITY_TO_INTERRUPT: &[u32] =
634        &[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
635
636    // First element is not used, just there to avoid a -1 in the interrupt handler.
637    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
638    pub(super) static INTERRUPT_TO_PRIORITY: [Priority; 20] = [
639        Priority::None,
640        Priority::Priority1,
641        Priority::Priority2,
642        Priority::None,
643        Priority::None,
644        Priority::Priority3,
645        Priority::Priority4,
646        Priority::None,
647        Priority::None,
648        Priority::Priority5,
649        Priority::Priority6,
650        Priority::Priority7,
651        Priority::Priority8,
652        Priority::Priority9,
653        Priority::Priority10,
654        Priority::Priority11,
655        Priority::Priority12,
656        Priority::Priority13,
657        Priority::Priority14,
658        Priority::Priority15,
659    ];
660
661    /// Enable a CPU interrupt
662    ///
663    /// # Safety
664    ///
665    /// Make sure there is an interrupt handler registered.
666    pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
667        PLIC_MX::regs().mxint_enable().modify(|r, w| {
668            let old = r.cpu_mxint_enable().bits();
669            let new = old | (1 << (which as isize));
670            unsafe { w.cpu_mxint_enable().bits(new) }
671        });
672    }
673
674    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
675    ///
676    /// The vectored interrupt handler will take care of clearing edge interrupt
677    /// bits.
678    pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
679        let interrupt_type = match kind {
680            InterruptKind::Level => 0,
681            InterruptKind::Edge => 1,
682        };
683
684        PLIC_MX::regs().mxint_type().modify(|r, w| {
685            let old = r.cpu_mxint_type().bits();
686            let new = old & !(1 << (which as isize)) | (interrupt_type << (which as isize));
687            unsafe { w.cpu_mxint_type().bits(new) }
688        });
689    }
690
691    /// Set the priority level of an CPU interrupt
692    ///
693    /// # Safety
694    ///
695    /// Great care must be taken when using this function; avoid changing the
696    /// priority of interrupts 1 - 15.
697    pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
698        PLIC_MX::regs()
699            .mxint_pri(which as usize)
700            .modify(|_, w| unsafe { w.cpu_mxint_pri().bits(priority as u8) });
701    }
702
703    /// Clear a CPU interrupt
704    #[inline]
705    pub fn clear(_core: Cpu, which: CpuInterrupt) {
706        PLIC_MX::regs().mxint_clear().modify(|r, w| {
707            let old = r.cpu_mxint_clear().bits();
708            let new = old | (1 << (which as isize));
709            unsafe { w.cpu_mxint_clear().bits(new) }
710        });
711    }
712
713    /// Get interrupt priority for the CPU
714    #[inline]
715    pub fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
716        priority(cpu_interrupt)
717    }
718
719    #[inline]
720    /// Get interrupt priority.
721    pub fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
722        let prio = PLIC_MX::regs()
723            .mxint_pri(cpu_interrupt as usize)
724            .read()
725            .cpu_mxint_pri()
726            .bits();
727        unsafe { core::mem::transmute::<u8, Priority>(prio) }
728    }
729
730    /// Get the current run level (the level below which interrupts are masked).
731    pub fn current_runlevel() -> Priority {
732        let prev_interrupt_priority = PLIC_MX::regs()
733            .mxint_thresh()
734            .read()
735            .cpu_mxint_thresh()
736            .bits()
737            .saturating_sub(1);
738
739        unwrap!(Priority::try_from(prev_interrupt_priority))
740    }
741
742    /// Changes the current run level (the level below which interrupts are
743    /// masked), and returns the previous run level.
744    ///
745    /// # Safety
746    ///
747    /// This function must only be used to raise the runlevel and to restore it
748    /// to a previous value. It must not be used to arbitrarily lower the
749    /// runlevel.
750    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
751        let prev_interrupt_priority = current_runlevel();
752
753        // The CPU responds to interrupts `>= level`, but we want to also disable
754        // interrupts at `level` so we set the threshold to `level + 1`.
755        PLIC_MX::regs()
756            .mxint_thresh()
757            .write(|w| unsafe { w.cpu_mxint_thresh().bits(level as u8 + 1) });
758
759        prev_interrupt_priority
760    }
761}
762
763#[cfg(feature = "rt")]
764mod rt {
765    use esp_riscv_rt::TrapFrame;
766
767    use super::*;
768
769    /// # Safety
770    ///
771    /// This function is called from an assembly trap handler.
772    #[doc(hidden)]
773    #[unsafe(link_section = ".trap.rust")]
774    #[unsafe(export_name = "_start_trap_rust_hal")]
775    unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
776        assert!(
777            mcause::read().is_exception(),
778            "Arrived into _start_trap_rust_hal but mcause is not an exception!"
779        );
780        unsafe extern "C" {
781            fn ExceptionHandler(tf: *mut TrapFrame);
782        }
783        unsafe {
784            ExceptionHandler(trap_frame);
785        }
786    }
787
788    #[doc(hidden)]
789    #[unsafe(no_mangle)]
790    unsafe fn _setup_interrupts() {
791        unsafe extern "C" {
792            static _vector_table: u32;
793        }
794
795        // disable all known interrupts
796        // at least after the 2nd stage bootloader there are some interrupts enabled
797        // (e.g. UART)
798        for peripheral_interrupt in 0..255 {
799            crate::peripherals::Interrupt::try_from(peripheral_interrupt)
800                .map(|intr| {
801                    #[cfg(multi_core)]
802                    disable(Cpu::AppCpu, intr);
803                    disable(Cpu::ProCpu, intr);
804                })
805                .ok();
806        }
807
808        unsafe {
809            let vec_table = (&_vector_table as *const u32).addr();
810            mtvec::write({
811                let mut mtvec = mtvec::Mtvec::from_bits(0);
812                mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
813                mtvec.set_address(vec_table);
814                mtvec
815            });
816
817            crate::interrupt::init_vectoring();
818        };
819
820        #[cfg(plic)]
821        unsafe {
822            core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
823        }
824    }
825
826    #[unsafe(no_mangle)]
827    #[ram]
828    unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
829        let core = Cpu::current();
830        let status = status(core);
831
832        // this has no effect on level interrupts, but the interrupt may be an edge one
833        // so we clear it anyway
834        clear(core, cpu_intr);
835
836        let prio = INTERRUPT_TO_PRIORITY[cpu_intr as usize];
837        let configured_interrupts = vectored::configured_interrupts(core, status, prio);
838
839        // Change the current runlevel so that interrupt handlers can access the correct runlevel.
840        let level = unsafe { change_current_runlevel(prio) };
841
842        // When nesting is possible, we run the nestable interrupts first. This ensures that we
843        // don't violate assumptions made by non-nestable handlers.
844
845        if prio != Priority::max() {
846            for interrupt_nr in configured_interrupts.iterator() {
847                let handler =
848                    unsafe { pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler } as usize;
849                let nested = (handler & 1) == 0;
850                if nested {
851                    let handler: fn() =
852                        unsafe { core::mem::transmute::<usize, fn()>(handler & !1) };
853
854                    unsafe { riscv::interrupt::nested(handler) };
855                }
856            }
857        }
858
859        // Now we can run the non-nestable interrupt handlers.
860
861        for interrupt_nr in configured_interrupts.iterator() {
862            let handler =
863                unsafe { pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler } as usize;
864            let not_nested = (handler & 1) == 1;
865            if not_nested || prio == Priority::max() {
866                let handler: fn() = unsafe { core::mem::transmute::<usize, fn()>(handler & !1) };
867
868                handler();
869            }
870        }
871
872        unsafe { change_current_runlevel(level) };
873    }
874}