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/// Unless you are sure, you most likely want to use [`enable`] instead.
214///
215/// Trying using a reserved interrupt from [`RESERVED_INTERRUPTS`] will return
216/// an error.
217pub fn enable_direct(
218    interrupt: Interrupt,
219    level: Priority,
220    cpu_interrupt: CpuInterrupt,
221) -> Result<(), Error> {
222    if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
223        return Err(Error::CpuInterruptReserved);
224    }
225    if matches!(level, Priority::None) {
226        return Err(Error::InvalidInterruptPriority);
227    }
228    unsafe {
229        map(Cpu::current(), interrupt, cpu_interrupt);
230        set_priority(Cpu::current(), cpu_interrupt, level);
231        enable_cpu_interrupt(cpu_interrupt);
232    }
233    Ok(())
234}
235
236/// Disable the given peripheral interrupt.
237pub fn disable(core: Cpu, interrupt: Interrupt) {
238    map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
239}
240
241/// Get status of peripheral interrupts
242#[inline]
243pub fn status(_core: Cpu) -> InterruptStatus {
244    cfg_if::cfg_if! {
245        if #[cfg(interrupts_status_registers = "3")] {
246            InterruptStatus::from(
247                INTERRUPT_CORE0::regs().core_0_intr_status(0).read().bits(),
248                INTERRUPT_CORE0::regs().core_0_intr_status(1).read().bits(),
249                INTERRUPT_CORE0::regs().core_0_intr_status(2).read().bits(),
250            )
251        } else if #[cfg(interrupts_status_registers = "4")] {
252            InterruptStatus::from(
253                INTERRUPT_CORE0::regs().core_0_intr_status(0).read().bits(),
254                INTERRUPT_CORE0::regs().core_0_intr_status(1).read().bits(),
255                INTERRUPT_CORE0::regs().core_0_intr_status(2).read().bits(),
256                INTERRUPT_CORE0::regs().core_0_intr_status(3).read().bits(),
257            )
258        } else {
259            InterruptStatus::from(
260                INTERRUPT_CORE0::regs().core_0_intr_status(0).read().bits(),
261                INTERRUPT_CORE0::regs().core_0_intr_status(1).read().bits(),
262            )
263        }
264    }
265}
266
267/// Assign a peripheral interrupt to an CPU interrupt.
268///
269/// # Safety
270///
271/// Do not use CPU interrupts in the [`RESERVED_INTERRUPTS`].
272pub unsafe fn map(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
273    map_raw(core, interrupt, which as u32)
274}
275
276fn map_raw(core: Cpu, interrupt: Interrupt, cpu_interrupt_number: u32) {
277    let interrupt_number = interrupt as usize;
278
279    match core {
280        Cpu::ProCpu => {
281            INTERRUPT_CORE0::regs()
282                .core_0_intr_map(interrupt_number)
283                .write(|w| unsafe { w.bits(cpu_interrupt_number) });
284        }
285        #[cfg(multi_core)]
286        Cpu::AppCpu => {
287            INTERRUPT_CORE1::regs()
288                .core_1_intr_map(interrupt_number)
289                .write(|w| unsafe { w.bits(cpu_interrupt_number) });
290        }
291    }
292}
293
294/// Get cpu interrupt assigned to peripheral interrupt
295#[inline]
296unsafe fn assigned_cpu_interrupt(interrupt: Interrupt) -> Option<CpuInterrupt> {
297    let interrupt_number = interrupt as isize;
298    let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
299
300    let cpu_intr = unsafe { intr_map_base.offset(interrupt_number).read_volatile() };
301    if cpu_intr > 0 && cpu_intr != DISABLED_CPU_INTERRUPT {
302        Some(unsafe { core::mem::transmute::<u32, CpuInterrupt>(cpu_intr) })
303    } else {
304        None
305    }
306}
307
308pub(crate) fn bound_cpu_interrupt_for(_cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
309    unsafe { assigned_cpu_interrupt(interrupt) }
310}
311
312mod vectored {
313    use super::*;
314
315    // Setup interrupts ready for vectoring
316    #[doc(hidden)]
317    pub(crate) unsafe fn init_vectoring() {
318        for (num, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(1..) {
319            let which = unsafe { core::mem::transmute::<u32, CpuInterrupt>(num) };
320            set_kind(Cpu::current(), which, InterruptKind::Level);
321            unsafe {
322                set_priority(
323                    Cpu::current(),
324                    which,
325                    core::mem::transmute::<u8, Priority>(prio),
326                );
327                enable_cpu_interrupt(which);
328            }
329        }
330    }
331
332    /// Get the interrupts configured for the core at the given priority
333    /// matching the given status
334    #[inline]
335    pub(crate) fn configured_interrupts(
336        core: Cpu,
337        status: InterruptStatus,
338        priority: Priority,
339    ) -> InterruptStatus {
340        unsafe {
341            let mut res = InterruptStatus::empty();
342
343            for interrupt_nr in status.iterator() {
344                // safety: cast is safe because of repr(u16)
345                if let Some(cpu_interrupt) =
346                    assigned_cpu_interrupt(core::mem::transmute::<u16, Interrupt>(
347                        interrupt_nr as u16,
348                    ))
349                    && priority_by_core(core, cpu_interrupt) == priority
350                {
351                    res.set(interrupt_nr);
352                }
353            }
354            res
355        }
356    }
357
358    /// Enables a interrupt at a given priority
359    ///
360    /// Note that interrupts still need to be enabled globally for interrupts
361    /// to be serviced.
362    pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
363        enable_on_cpu(Cpu::current(), interrupt, level)
364    }
365
366    pub(crate) fn enable_on_cpu(
367        cpu: Cpu,
368        interrupt: Interrupt,
369        level: Priority,
370    ) -> Result<(), Error> {
371        if matches!(level, Priority::None) {
372            return Err(Error::InvalidInterruptPriority);
373        }
374        unsafe {
375            let cpu_interrupt = core::mem::transmute::<u32, CpuInterrupt>(
376                PRIORITY_TO_INTERRUPT[(level as usize) - 1],
377            );
378            map(cpu, interrupt, cpu_interrupt);
379            enable_cpu_interrupt(cpu_interrupt);
380        }
381        Ok(())
382    }
383
384    /// Binds the given interrupt to the given handler.
385    ///
386    /// # Safety
387    ///
388    /// This will replace any previously bound interrupt handler
389    pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) {
390        unsafe {
391            let ptr = &pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as *const _
392                as *mut unsafe extern "C" fn();
393            ptr.write_volatile(handler);
394        }
395    }
396
397    /// Returns the currently bound interrupt handler.
398    pub fn bound_handler(interrupt: Interrupt) -> Option<unsafe extern "C" fn()> {
399        unsafe {
400            let addr = pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler;
401            if addr as usize == 0 {
402                return None;
403            }
404
405            Some(addr)
406        }
407    }
408}
409
410#[cfg(not(plic))]
411mod classic {
412    use super::{CpuInterrupt, InterruptKind, Priority};
413    use crate::{peripherals::INTERRUPT_CORE0, system::Cpu};
414
415    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
416    pub(super) static DISABLED_CPU_INTERRUPT: u32 = 0;
417
418    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
419    pub(super) static PRIORITY_TO_INTERRUPT: &[u32] =
420        &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
421
422    // First element is not used, just there to avoid a -1 in the interrupt handler.
423    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
424    pub(super) static INTERRUPT_TO_PRIORITY: [Priority; 16] = [
425        Priority::None,
426        Priority::Priority1,
427        Priority::Priority2,
428        Priority::Priority3,
429        Priority::Priority4,
430        Priority::Priority5,
431        Priority::Priority6,
432        Priority::Priority7,
433        Priority::Priority8,
434        Priority::Priority9,
435        Priority::Priority10,
436        Priority::Priority11,
437        Priority::Priority12,
438        Priority::Priority13,
439        Priority::Priority14,
440        Priority::Priority15,
441    ];
442
443    /// Enable a CPU interrupt
444    ///
445    /// # Safety
446    ///
447    /// Make sure there is an interrupt handler registered.
448    pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
449        let cpu_interrupt_number = which as isize;
450        let intr = INTERRUPT_CORE0::regs();
451        intr.cpu_int_enable()
452            .modify(|r, w| unsafe { w.bits((1 << cpu_interrupt_number) | r.bits()) });
453    }
454
455    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
456    ///
457    /// The vectored interrupt handler will take care of clearing edge interrupt
458    /// bits.
459    pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
460        unsafe {
461            let intr = INTERRUPT_CORE0::regs();
462            let cpu_interrupt_number = which as isize;
463
464            let interrupt_type = match kind {
465                InterruptKind::Level => 0,
466                InterruptKind::Edge => 1,
467            };
468            intr.cpu_int_type().modify(|r, w| {
469                w.bits(
470                    r.bits() & !(1 << cpu_interrupt_number)
471                        | (interrupt_type << cpu_interrupt_number),
472                )
473            });
474        }
475    }
476
477    /// Set the priority level of an CPU interrupt
478    ///
479    /// # Safety
480    ///
481    /// Great care must be taken when using this function; avoid changing the
482    /// priority of interrupts 1 - 15.
483    pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
484        let intr = INTERRUPT_CORE0::regs();
485        intr.cpu_int_pri(which as usize)
486            .write(|w| unsafe { w.map().bits(priority as u8) });
487    }
488
489    /// Clear a CPU interrupt
490    #[inline]
491    pub fn clear(_core: Cpu, which: CpuInterrupt) {
492        unsafe {
493            let cpu_interrupt_number = which as usize;
494            let intr = INTERRUPT_CORE0::regs();
495            intr.cpu_int_clear()
496                .write(|w| w.bits(1 << cpu_interrupt_number));
497        }
498    }
499
500    /// Get interrupt priority
501    #[inline]
502    pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
503        priority(cpu_interrupt)
504    }
505
506    /// Get interrupt priority - called by assembly code
507    #[inline]
508    pub(super) fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
509        let intr = INTERRUPT_CORE0::regs();
510        unsafe {
511            core::mem::transmute::<u8, Priority>(
512                intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(),
513            )
514        }
515    }
516
517    /// Get the current run level (the level below which interrupts are masked).
518    pub fn current_runlevel() -> Priority {
519        let intr = INTERRUPT_CORE0::regs();
520        let prev_interrupt_priority = intr.cpu_int_thresh().read().bits().saturating_sub(1) as u8;
521
522        unwrap!(Priority::try_from(prev_interrupt_priority))
523    }
524
525    /// Changes the current run level (the level below which interrupts are
526    /// masked), and returns the previous run level.
527    ///
528    /// # Safety
529    ///
530    /// This function must only be used to raise the runlevel and to restore it
531    /// to a previous value. It must not be used to arbitrarily lower the
532    /// runlevel.
533    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
534        let prev_interrupt_priority = current_runlevel();
535
536        // The CPU responds to interrupts `>= level`, but we want to also disable
537        // interrupts at `level` so we set the threshold to `level + 1`.
538        INTERRUPT_CORE0::regs()
539            .cpu_int_thresh()
540            .write(|w| unsafe { w.bits(level as u32 + 1) });
541
542        prev_interrupt_priority
543    }
544
545    #[cfg(feature = "rt")]
546    mod rt {
547        use super::*;
548
549        #[unsafe(link_section = ".trap")]
550        #[unsafe(no_mangle)]
551        pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
552            // Both C6 and H2 have 5 bits of code. The riscv crate masks 31 bits, which then
553            // causes a bounds check to be present.
554            let interrupt_id: usize = riscv::register::mcause::read().bits() & 0x1f;
555            let intr = INTERRUPT_CORE0::regs();
556            let interrupt_priority = intr.cpu_int_pri(interrupt_id).read().bits();
557
558            let prev_interrupt_priority = intr.cpu_int_thresh().read().bits();
559            if interrupt_priority < 15 {
560                // leave interrupts disabled if interrupt is of max priority.
561                intr.cpu_int_thresh()
562                    .write(|w| unsafe { w.bits(interrupt_priority + 1) }); // set the prio threshold to 1 more than current interrupt prio
563                unsafe { riscv::interrupt::enable() };
564            }
565            prev_interrupt_priority
566        }
567
568        #[unsafe(link_section = ".trap")]
569        #[unsafe(no_mangle)]
570        unsafe extern "C" fn _restore_priority(stored_prio: u32) {
571            riscv::interrupt::disable();
572            let intr = INTERRUPT_CORE0::regs();
573            intr.cpu_int_thresh()
574                .write(|w| unsafe { w.bits(stored_prio) });
575        }
576
577        /// Globally exported so assembly code can call it.
578        #[unsafe(no_mangle)]
579        unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
580            super::priority(cpu_interrupt)
581        }
582    }
583}
584
585#[cfg(plic)]
586mod plic {
587    use super::{CpuInterrupt, InterruptKind, Priority};
588    use crate::{peripherals::PLIC_MX, system::Cpu};
589
590    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
591    pub(super) static DISABLED_CPU_INTERRUPT: u32 = 31;
592
593    // don't use interrupts reserved for CLIC (0,3,4,7)
594    // for some reason also CPU interrupt 8 doesn't work by default since it's
595    // disabled after reset - so don't use that, too
596    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
597    pub(super) static PRIORITY_TO_INTERRUPT: &[u32] =
598        &[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
599
600    // First element is not used, just there to avoid a -1 in the interrupt handler.
601    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
602    pub(super) static INTERRUPT_TO_PRIORITY: [Priority; 20] = [
603        Priority::None,
604        Priority::Priority1,
605        Priority::Priority2,
606        Priority::None,
607        Priority::None,
608        Priority::Priority3,
609        Priority::Priority4,
610        Priority::None,
611        Priority::None,
612        Priority::Priority5,
613        Priority::Priority6,
614        Priority::Priority7,
615        Priority::Priority8,
616        Priority::Priority9,
617        Priority::Priority10,
618        Priority::Priority11,
619        Priority::Priority12,
620        Priority::Priority13,
621        Priority::Priority14,
622        Priority::Priority15,
623    ];
624
625    /// Enable a CPU interrupt
626    ///
627    /// # Safety
628    ///
629    /// Make sure there is an interrupt handler registered.
630    pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
631        PLIC_MX::regs().mxint_enable().modify(|r, w| {
632            let old = r.cpu_mxint_enable().bits();
633            let new = old | (1 << (which as isize));
634            unsafe { w.cpu_mxint_enable().bits(new) }
635        });
636    }
637
638    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
639    ///
640    /// The vectored interrupt handler will take care of clearing edge interrupt
641    /// bits.
642    pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
643        let interrupt_type = match kind {
644            InterruptKind::Level => 0,
645            InterruptKind::Edge => 1,
646        };
647
648        PLIC_MX::regs().mxint_type().modify(|r, w| {
649            let old = r.cpu_mxint_type().bits();
650            let new = old & !(1 << (which as isize)) | (interrupt_type << (which as isize));
651            unsafe { w.cpu_mxint_type().bits(new) }
652        });
653    }
654
655    /// Set the priority level of an CPU interrupt
656    ///
657    /// # Safety
658    ///
659    /// Great care must be taken when using this function; avoid changing the
660    /// priority of interrupts 1 - 15.
661    pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
662        PLIC_MX::regs()
663            .mxint_pri(which as usize)
664            .modify(|_, w| unsafe { w.cpu_mxint_pri().bits(priority as u8) });
665    }
666
667    /// Clear a CPU interrupt
668    #[inline]
669    pub fn clear(_core: Cpu, which: CpuInterrupt) {
670        PLIC_MX::regs().mxint_clear().modify(|r, w| {
671            let old = r.cpu_mxint_clear().bits();
672            let new = old | (1 << (which as isize));
673            unsafe { w.cpu_mxint_clear().bits(new) }
674        });
675    }
676
677    /// Get interrupt priority for the CPU
678    #[inline]
679    pub fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
680        priority(cpu_interrupt)
681    }
682
683    #[inline]
684    /// Get interrupt priority.
685    pub fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
686        let prio = PLIC_MX::regs()
687            .mxint_pri(cpu_interrupt as usize)
688            .read()
689            .cpu_mxint_pri()
690            .bits();
691        unsafe { core::mem::transmute::<u8, Priority>(prio) }
692    }
693
694    /// Get the current run level (the level below which interrupts are masked).
695    pub fn current_runlevel() -> Priority {
696        let prev_interrupt_priority = PLIC_MX::regs()
697            .mxint_thresh()
698            .read()
699            .cpu_mxint_thresh()
700            .bits()
701            .saturating_sub(1);
702
703        unwrap!(Priority::try_from(prev_interrupt_priority))
704    }
705
706    /// Changes the current run level (the level below which interrupts are
707    /// masked), and returns the previous run level.
708    ///
709    /// # Safety
710    ///
711    /// This function must only be used to raise the runlevel and to restore it
712    /// to a previous value. It must not be used to arbitrarily lower the
713    /// runlevel.
714    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
715        let prev_interrupt_priority = current_runlevel();
716
717        // The CPU responds to interrupts `>= level`, but we want to also disable
718        // interrupts at `level` so we set the threshold to `level + 1`.
719        PLIC_MX::regs()
720            .mxint_thresh()
721            .write(|w| unsafe { w.cpu_mxint_thresh().bits(level as u8 + 1) });
722
723        prev_interrupt_priority
724    }
725
726    #[cfg(feature = "rt")]
727    mod rt {
728        use super::*;
729        /// Get interrupt priority - called by assembly code
730        #[unsafe(no_mangle)]
731        pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
732            super::priority(cpu_interrupt)
733        }
734
735        #[unsafe(no_mangle)]
736        #[unsafe(link_section = ".trap")]
737        pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
738            let interrupt_id: usize = riscv::register::mcause::read().code(); // MSB is whether its exception or interrupt.
739            let interrupt_priority = PLIC_MX::regs()
740                .mxint_pri(interrupt_id)
741                .read()
742                .cpu_mxint_pri()
743                .bits();
744
745            let prev_interrupt_priority = PLIC_MX::regs()
746                .mxint_thresh()
747                .read()
748                .cpu_mxint_thresh()
749                .bits();
750            if interrupt_priority < 15 {
751                // leave interrupts disabled if interrupt is of max priority.
752                PLIC_MX::regs()
753                    .mxint_thresh()
754                    .write(|w| unsafe { w.cpu_mxint_thresh().bits(interrupt_priority + 1) });
755
756                unsafe {
757                    riscv::interrupt::enable();
758                }
759            }
760            prev_interrupt_priority as u32
761        }
762
763        #[unsafe(no_mangle)]
764        #[unsafe(link_section = ".trap")]
765        pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
766            riscv::interrupt::disable();
767            PLIC_MX::regs()
768                .mxint_thresh()
769                .write(|w| unsafe { w.cpu_mxint_thresh().bits(stored_prio as u8) });
770        }
771    }
772}
773
774#[cfg(feature = "rt")]
775mod rt {
776    use esp_riscv_rt::TrapFrame;
777
778    use super::*;
779
780    /// # Safety
781    ///
782    /// This function is called from an assembly trap handler.
783    #[doc(hidden)]
784    #[unsafe(link_section = ".trap.rust")]
785    #[unsafe(export_name = "_start_trap_rust_hal")]
786    unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
787        assert!(
788            mcause::read().is_exception(),
789            "Arrived into _start_trap_rust_hal but mcause is not an exception!"
790        );
791        unsafe extern "C" {
792            fn ExceptionHandler(tf: *mut TrapFrame);
793        }
794        unsafe {
795            ExceptionHandler(trap_frame);
796        }
797    }
798
799    #[doc(hidden)]
800    #[unsafe(no_mangle)]
801    unsafe fn _setup_interrupts() {
802        unsafe extern "C" {
803            static _vector_table: u32;
804        }
805
806        // disable all known interrupts
807        // at least after the 2nd stage bootloader there are some interrupts enabled
808        // (e.g. UART)
809        for peripheral_interrupt in 0..255 {
810            crate::peripherals::Interrupt::try_from(peripheral_interrupt)
811                .map(|intr| {
812                    #[cfg(multi_core)]
813                    disable(Cpu::AppCpu, intr);
814                    disable(Cpu::ProCpu, intr);
815                })
816                .ok();
817        }
818
819        unsafe {
820            let vec_table = (&_vector_table as *const u32).addr();
821            mtvec::write(vec_table, mtvec::TrapMode::Vectored);
822
823            crate::interrupt::init_vectoring();
824        };
825
826        #[cfg(plic)]
827        unsafe {
828            core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
829        }
830    }
831
832    #[unsafe(no_mangle)]
833    #[ram]
834    unsafe fn handle_interrupts(cpu_intr: CpuInterrupt, context: &mut TrapFrame) {
835        let core = Cpu::current();
836        let status = status(core);
837
838        // this has no effect on level interrupts, but the interrupt may be an edge one
839        // so we clear it anyway
840        clear(core, cpu_intr);
841
842        let prio = INTERRUPT_TO_PRIORITY[cpu_intr as usize];
843        let configured_interrupts = vectored::configured_interrupts(core, status, prio);
844
845        for interrupt_nr in configured_interrupts.iterator() {
846            let handler = unsafe { pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler };
847
848            let handler: fn(&mut TrapFrame) = unsafe {
849                core::mem::transmute::<unsafe extern "C" fn(), fn(&mut TrapFrame)>(handler)
850            };
851            handler(context);
852        }
853    }
854
855    // The compiler generates quite unfortunate code for
856    // ```rust,ignore
857    // #[no_mangle]
858    // #[ram]
859    // unsafe fn interrupt1(context: &mut TrapFrame) {
860    //    handle_interrupts(CpuInterrupt::Interrupt1, context)
861    // }
862    // ```
863    //
864    // Resulting in
865    // ```asm,ignore
866    // interrupt1:
867    // add	sp,sp,-16
868    // sw	ra,12(sp)
869    // sw	s0,8(sp)
870    // add	s0,sp,16
871    // mv	a1,a0
872    // li	a0,1
873    // lw	ra,12(sp)
874    // lw	s0,8(sp)
875    // add	sp,sp,16
876    // auipc	t1,0x0
877    // jr	handle_interrupts
878    // ```
879    //
880    // We can do better manually - use Rust again once/if that changes
881    macro_rules! interrupt_handler {
882        ($num:literal) => {
883            core::arch::global_asm! {
884                concat!(
885                r#"
886                    .section .rwtext, "ax"
887                    .global interrupt"#,$num,r#"
888
889                interrupt"#,$num,r#":
890                    mv a1, a0
891                    li a0,"#,$num,r#"
892                    j handle_interrupts
893                "#
894            )
895            }
896        };
897    }
898
899    interrupt_handler!(1);
900    interrupt_handler!(2);
901    interrupt_handler!(3);
902    interrupt_handler!(4);
903    interrupt_handler!(5);
904    interrupt_handler!(6);
905    interrupt_handler!(7);
906    interrupt_handler!(8);
907    interrupt_handler!(9);
908    interrupt_handler!(10);
909    interrupt_handler!(11);
910    interrupt_handler!(12);
911    interrupt_handler!(13);
912    interrupt_handler!(14);
913    interrupt_handler!(15);
914
915    #[cfg(plic)]
916    interrupt_handler!(16);
917    #[cfg(plic)]
918    interrupt_handler!(17);
919    #[cfg(plic)]
920    interrupt_handler!(18);
921    #[cfg(plic)]
922    interrupt_handler!(19);
923}