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}