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(
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
236pub fn disable(core: Cpu, interrupt: Interrupt) {
238 map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
239}
240
241#[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
267pub 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#[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 #[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 #[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 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 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 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 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 #[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 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 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 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 #[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 #[inline]
502 pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
503 priority(cpu_interrupt)
504 }
505
506 #[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 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 pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
534 let prev_interrupt_priority = current_runlevel();
535
536 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 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 intr.cpu_int_thresh()
562 .write(|w| unsafe { w.bits(interrupt_priority + 1) }); 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 #[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 #[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 #[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 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 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 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 #[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 #[inline]
679 pub fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
680 priority(cpu_interrupt)
681 }
682
683 #[inline]
684 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 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 pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
715 let prev_interrupt_priority = current_runlevel();
716
717 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 #[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(); 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 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 #[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 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 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 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}