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(
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
280fn 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 | 0b1101111u32;
301
302 Ok(instr)
303}
304
305pub fn disable(core: Cpu, interrupt: Interrupt) {
307 map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
308}
309
310#[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
336pub 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#[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 #[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 #[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 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 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 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 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 #[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 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 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 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 #[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 #[inline]
582 pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
583 priority(cpu_interrupt)
584 }
585
586 #[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 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 pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
614 let prev_interrupt_priority = current_runlevel();
615
616 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 #[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 #[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 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 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 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 #[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 #[inline]
720 pub fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
721 priority(cpu_interrupt)
722 }
723
724 #[inline]
725 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 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 pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
756 let prev_interrupt_priority = current_runlevel();
757
758 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 #[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 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 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 let level = unsafe { change_current_runlevel(prio) };
846
847 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 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}