1#[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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub enum Error {
36    InvalidInterruptPriority,
38    CpuInterruptReserved,
40}
41
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44pub enum InterruptKind {
45    Level,
47    Edge,
49}
50
51#[repr(u32)]
55#[derive(Debug, Copy, Clone)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57pub enum CpuInterrupt {
58    Interrupt1 = 1,
60    Interrupt2,
62    Interrupt3,
64    Interrupt4,
66    Interrupt5,
68    Interrupt6,
70    Interrupt7,
72    Interrupt8,
74    Interrupt9,
76    Interrupt10,
78    Interrupt11,
80    Interrupt12,
82    Interrupt13,
84    Interrupt14,
86    Interrupt15,
88    Interrupt16,
90    Interrupt17,
92    Interrupt18,
94    Interrupt19,
96    Interrupt20,
98    Interrupt21,
100    Interrupt22,
102    Interrupt23,
104    Interrupt24,
106    Interrupt25,
108    Interrupt26,
110    Interrupt27,
112    Interrupt28,
114    Interrupt29,
116    Interrupt30,
118    Interrupt31,
120}
121
122#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
124#[cfg_attr(feature = "defmt", derive(defmt::Format))]
125#[repr(u8)]
126pub enum Priority {
127    None = 0,
129    Priority1,
131    Priority2,
133    Priority3,
135    Priority4,
137    Priority5,
139    Priority6,
141    Priority7,
143    Priority8,
145    Priority9,
147    Priority10,
149    Priority11,
151    Priority12,
153    Priority13,
155    Priority14,
157    Priority15,
159}
160
161impl Priority {
162    pub const fn max() -> Priority {
164        Priority::Priority15
165    }
166
167    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#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
209pub static RESERVED_INTERRUPTS: &[u32] = PRIORITY_TO_INTERRUPT;
210
211pub 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
275fn 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        | 0b1101111u32;
296
297    Ok(instr)
298}
299
300pub fn disable(core: Cpu, interrupt: Interrupt) {
302    map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
303}
304
305#[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
331pub 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#[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    #[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    #[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                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    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    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    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    #[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    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    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    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    #[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    #[inline]
577    pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
578        priority(cpu_interrupt)
579    }
580
581    #[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    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    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
609        let prev_interrupt_priority = current_runlevel();
610
611        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    #[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    #[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    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    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    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    #[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    #[inline]
715    pub fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
716        priority(cpu_interrupt)
717    }
718
719    #[inline]
720    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    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    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
751        let prev_interrupt_priority = current_runlevel();
752
753        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    #[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        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        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        let level = unsafe { change_current_runlevel(prio) };
841
842        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        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}