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