1use procmacros::ram;
4use xtensa_lx::interrupt;
5#[cfg(esp32)]
6pub(crate) use xtensa_lx::interrupt::free;
7#[cfg(feature = "rt")]
8use xtensa_lx_rt::exception::Context;
9
10pub use self::vectored::*;
11use super::InterruptStatus;
12use crate::{interrupt::IsrCallback, pac, peripherals::Interrupt, system::Cpu};
13
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub enum Error {
18 InvalidInterrupt,
20 CpuInterruptReserved,
22}
23
24#[derive(Debug, Copy, Clone)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[repr(u32)]
31pub enum CpuInterrupt {
32 Interrupt0LevelPriority1 = 0,
34 Interrupt1LevelPriority1,
36 Interrupt2LevelPriority1,
38 Interrupt3LevelPriority1,
40 Interrupt4LevelPriority1,
42 Interrupt5LevelPriority1,
44 Interrupt6Timer0Priority1,
46 Interrupt7SoftwarePriority1,
48 Interrupt8LevelPriority1,
50 Interrupt9LevelPriority1,
52 Interrupt10EdgePriority1,
54 Interrupt11ProfilingPriority3,
56 Interrupt12LevelPriority1,
58 Interrupt13LevelPriority1,
60 Interrupt14NmiPriority7,
62 Interrupt15Timer1Priority3,
64 Interrupt16Timer2Priority5,
66 Interrupt17LevelPriority1,
68 Interrupt18LevelPriority1,
70 Interrupt19LevelPriority2,
72 Interrupt20LevelPriority2,
74 Interrupt21LevelPriority2,
76 Interrupt22EdgePriority3,
78 Interrupt23LevelPriority3,
80 Interrupt24LevelPriority4,
82 Interrupt25LevelPriority4,
84 Interrupt26LevelPriority5,
86 Interrupt27LevelPriority3,
88 Interrupt28EdgePriority4,
90 Interrupt29SoftwarePriority3,
92 Interrupt30EdgePriority4,
94 Interrupt31EdgePriority5,
96}
97
98impl CpuInterrupt {
99 fn from_u32(n: u32) -> Option<Self> {
100 if n < 32 {
101 Some(unsafe { core::mem::transmute::<u32, Self>(n) })
102 } else {
103 None
104 }
105 }
106
107 fn is_internal(self) -> bool {
108 matches!(
109 self,
110 Self::Interrupt6Timer0Priority1
111 | Self::Interrupt7SoftwarePriority1
112 | Self::Interrupt11ProfilingPriority3
113 | Self::Interrupt15Timer1Priority3
114 | Self::Interrupt16Timer2Priority5
115 | Self::Interrupt29SoftwarePriority3
116 )
117 }
118
119 fn is_peripheral(self) -> bool {
120 !self.is_internal()
121 }
122}
123
124#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
126pub static RESERVED_INTERRUPTS: &[u32] = &[
127 CpuInterrupt::Interrupt1LevelPriority1 as _,
128 CpuInterrupt::Interrupt19LevelPriority2 as _,
129 CpuInterrupt::Interrupt23LevelPriority3 as _,
130 CpuInterrupt::Interrupt10EdgePriority1 as _,
131 CpuInterrupt::Interrupt22EdgePriority3 as _,
132];
133
134pub(crate) fn setup_interrupts() {
135 for peripheral_interrupt in 0..255 {
139 Interrupt::try_from(peripheral_interrupt)
140 .map(|intr| {
141 #[cfg(multi_core)]
142 disable(Cpu::AppCpu, intr);
143 disable(Cpu::ProCpu, intr);
144 })
145 .ok();
146 }
147}
148
149pub fn enable_direct(interrupt: Interrupt, cpu_interrupt: CpuInterrupt) -> Result<(), Error> {
156 if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
157 return Err(Error::CpuInterruptReserved);
158 }
159 unsafe {
160 map(Cpu::current(), interrupt, cpu_interrupt);
161
162 xtensa_lx::interrupt::enable_mask(1 << cpu_interrupt as u32);
163 }
164 Ok(())
165}
166
167pub unsafe fn map(cpu: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
176 let interrupt_number = interrupt as usize;
177 let cpu_interrupt_number = which as u32;
178 match cpu {
179 Cpu::ProCpu => unsafe {
180 (*core0_interrupt_peripheral())
181 .core_0_intr_map(interrupt_number)
182 .write(|w| w.bits(cpu_interrupt_number));
183 },
184 #[cfg(multi_core)]
185 Cpu::AppCpu => unsafe {
186 (*core1_interrupt_peripheral())
187 .core_1_intr_map(interrupt_number)
188 .write(|w| w.bits(cpu_interrupt_number));
189 },
190 }
191}
192
193pub(crate) fn bound_cpu_interrupt_for(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
195 let cpu_intr = match cpu {
196 Cpu::ProCpu => unsafe {
197 (*core0_interrupt_peripheral())
198 .core_0_intr_map(interrupt as usize)
199 .read()
200 .bits()
201 },
202 #[cfg(multi_core)]
203 Cpu::AppCpu => unsafe {
204 (*core1_interrupt_peripheral())
205 .core_1_intr_map(interrupt as usize)
206 .read()
207 .bits()
208 },
209 };
210 let cpu_intr = CpuInterrupt::from_u32(cpu_intr)?;
211
212 if cpu_intr.is_peripheral() {
213 Some(cpu_intr)
214 } else {
215 None
216 }
217}
218
219pub fn disable(core: Cpu, interrupt: Interrupt) {
221 unsafe { map(core, interrupt, CpuInterrupt::Interrupt16Timer2Priority5) }
222}
223
224pub fn clear(_core: Cpu, which: CpuInterrupt) {
226 unsafe {
227 xtensa_lx::interrupt::clear(1 << which as u32);
228 }
229}
230
231#[cfg(interrupts_status_registers = "3")]
233pub fn status(core: Cpu) -> InterruptStatus {
234 unsafe {
235 match core {
236 Cpu::ProCpu => InterruptStatus::from(
237 (*core0_interrupt_peripheral())
238 .core_0_intr_status(0)
239 .read()
240 .bits(),
241 (*core0_interrupt_peripheral())
242 .core_0_intr_status(1)
243 .read()
244 .bits(),
245 (*core0_interrupt_peripheral())
246 .core_0_intr_status(2)
247 .read()
248 .bits(),
249 ),
250 #[cfg(multi_core)]
251 Cpu::AppCpu => InterruptStatus::from(
252 (*core1_interrupt_peripheral())
253 .core_1_intr_status(0)
254 .read()
255 .bits(),
256 (*core1_interrupt_peripheral())
257 .core_1_intr_status(1)
258 .read()
259 .bits(),
260 (*core1_interrupt_peripheral())
261 .core_1_intr_status(2)
262 .read()
263 .bits(),
264 ),
265 }
266 }
267}
268
269#[cfg(interrupts_status_registers = "4")]
271pub fn status(core: Cpu) -> InterruptStatus {
272 unsafe {
273 match core {
274 Cpu::ProCpu => InterruptStatus::from(
275 (*core0_interrupt_peripheral())
276 .core_0_intr_status(0)
277 .read()
278 .bits(),
279 (*core0_interrupt_peripheral())
280 .core_0_intr_status(1)
281 .read()
282 .bits(),
283 (*core0_interrupt_peripheral())
284 .core_0_intr_status(2)
285 .read()
286 .bits(),
287 (*core0_interrupt_peripheral())
288 .core_0_intr_status(3)
289 .read()
290 .bits(),
291 ),
292 #[cfg(multi_core)]
293 Cpu::AppCpu => InterruptStatus::from(
294 (*core1_interrupt_peripheral())
295 .core_1_intr_status(0)
296 .read()
297 .bits(),
298 (*core1_interrupt_peripheral())
299 .core_1_intr_status(1)
300 .read()
301 .bits(),
302 (*core1_interrupt_peripheral())
303 .core_1_intr_status(2)
304 .read()
305 .bits(),
306 (*core1_interrupt_peripheral())
307 .core_1_intr_status(3)
308 .read()
309 .bits(),
310 ),
311 }
312 }
313}
314
315#[cfg(esp32)]
316unsafe fn core0_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
317 pac::DPORT::PTR
318}
319
320#[cfg(esp32)]
321unsafe fn core1_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
322 pac::DPORT::PTR
323}
324
325#[cfg(any(esp32s2, esp32s3))]
326unsafe fn core0_interrupt_peripheral() -> *const crate::pac::interrupt_core0::RegisterBlock {
327 pac::INTERRUPT_CORE0::PTR
328}
329
330#[cfg(esp32s3)]
331unsafe fn core1_interrupt_peripheral() -> *const crate::pac::interrupt_core1::RegisterBlock {
332 pac::INTERRUPT_CORE1::PTR
333}
334
335pub fn current_runlevel() -> Priority {
337 let ps: u32;
338 unsafe { core::arch::asm!("rsr.ps {0}", out(reg) ps) };
339
340 let prev_interrupt_priority = ps as u8 & 0x0F;
341
342 unwrap!(Priority::try_from(prev_interrupt_priority))
343}
344
345pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
354 let token: u32;
355 unsafe {
356 match level {
357 Priority::None => core::arch::asm!("rsil {0}, 0", out(reg) token),
358 Priority::Priority1 => core::arch::asm!("rsil {0}, 1", out(reg) token),
359 Priority::Priority2 => core::arch::asm!("rsil {0}, 2", out(reg) token),
360 Priority::Priority3 => core::arch::asm!("rsil {0}, 3", out(reg) token),
361 };
362 }
363
364 let prev_interrupt_priority = token as u8 & 0x0F;
365
366 unwrap!(Priority::try_from(prev_interrupt_priority))
367}
368
369mod vectored {
370 use super::*;
371
372 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
374 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
375 #[repr(u8)]
376 pub enum Priority {
377 None = 0,
379 Priority1,
381 Priority2,
383 Priority3,
385 }
386
387 impl Priority {
388 pub const fn max() -> Priority {
390 Priority::Priority3
391 }
392
393 pub const fn min() -> Priority {
395 Priority::Priority1
396 }
397 }
398
399 impl TryFrom<u32> for Priority {
400 type Error = Error;
401
402 fn try_from(value: u32) -> Result<Self, Self::Error> {
403 match value {
404 0 => Ok(Priority::None),
405 1 => Ok(Priority::Priority1),
406 2 => Ok(Priority::Priority2),
407 3 => Ok(Priority::Priority3),
408 _ => Err(Error::InvalidInterrupt),
409 }
410 }
411 }
412
413 impl TryFrom<u8> for Priority {
414 type Error = Error;
415
416 fn try_from(value: u8) -> Result<Self, Self::Error> {
417 Self::try_from(value as u32)
418 }
419 }
420
421 impl CpuInterrupt {
422 #[inline]
423 fn level(&self) -> Priority {
424 match self {
425 CpuInterrupt::Interrupt0LevelPriority1
426 | CpuInterrupt::Interrupt1LevelPriority1
427 | CpuInterrupt::Interrupt2LevelPriority1
428 | CpuInterrupt::Interrupt3LevelPriority1
429 | CpuInterrupt::Interrupt4LevelPriority1
430 | CpuInterrupt::Interrupt5LevelPriority1
431 | CpuInterrupt::Interrupt6Timer0Priority1
432 | CpuInterrupt::Interrupt7SoftwarePriority1
433 | CpuInterrupt::Interrupt8LevelPriority1
434 | CpuInterrupt::Interrupt9LevelPriority1
435 | CpuInterrupt::Interrupt10EdgePriority1
436 | CpuInterrupt::Interrupt12LevelPriority1
437 | CpuInterrupt::Interrupt13LevelPriority1
438 | CpuInterrupt::Interrupt17LevelPriority1
439 | CpuInterrupt::Interrupt18LevelPriority1 => Priority::Priority1,
440
441 CpuInterrupt::Interrupt19LevelPriority2
442 | CpuInterrupt::Interrupt20LevelPriority2
443 | CpuInterrupt::Interrupt21LevelPriority2 => Priority::Priority2,
444
445 CpuInterrupt::Interrupt11ProfilingPriority3
446 | CpuInterrupt::Interrupt15Timer1Priority3
447 | CpuInterrupt::Interrupt22EdgePriority3
448 | CpuInterrupt::Interrupt27LevelPriority3
449 | CpuInterrupt::Interrupt29SoftwarePriority3
450 | CpuInterrupt::Interrupt23LevelPriority3 => Priority::Priority3,
451
452 CpuInterrupt::Interrupt24LevelPriority4
455 | CpuInterrupt::Interrupt25LevelPriority4
456 | CpuInterrupt::Interrupt28EdgePriority4
457 | CpuInterrupt::Interrupt30EdgePriority4
458 | CpuInterrupt::Interrupt31EdgePriority5
459 | CpuInterrupt::Interrupt16Timer2Priority5
460 | CpuInterrupt::Interrupt26LevelPriority5
461 | CpuInterrupt::Interrupt14NmiPriority7 => Priority::None,
462 }
463 }
464 }
465
466 #[inline(always)]
468 pub(crate) fn configured_interrupts(
469 core: Cpu,
470 status: InterruptStatus,
471 level: u32,
472 ) -> InterruptStatus {
473 unsafe {
474 let intr_map_base = match core {
475 Cpu::ProCpu => (*core0_interrupt_peripheral()).core_0_intr_map(0).as_ptr(),
476 #[cfg(multi_core)]
477 Cpu::AppCpu => (*core1_interrupt_peripheral()).core_1_intr_map(0).as_ptr(),
478 };
479
480 let mut res = InterruptStatus::empty();
481
482 for interrupt_nr in status.iterator() {
483 let i = interrupt_nr as isize;
484 let cpu_interrupt = intr_map_base.offset(i).read_volatile();
485 let cpu_interrupt = core::mem::transmute::<u32, CpuInterrupt>(cpu_interrupt);
487 let int_level = cpu_interrupt.level() as u32;
488
489 if int_level == level {
490 res.set(interrupt_nr);
491 }
492 }
493
494 res
495 }
496 }
497
498 pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
500 enable_on_cpu(Cpu::current(), interrupt, level)
501 }
502
503 pub(crate) fn enable_on_cpu(
504 cpu: Cpu,
505 interrupt: Interrupt,
506 level: Priority,
507 ) -> Result<(), Error> {
508 let cpu_interrupt =
509 interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?;
510
511 unsafe {
512 map(cpu, interrupt, cpu_interrupt);
513
514 xtensa_lx::interrupt::enable_mask(1 << cpu_interrupt as u32);
515 }
516 Ok(())
517 }
518
519 pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: IsrCallback) {
525 let ptr =
526 unsafe { &pac::__INTERRUPTS[interrupt as usize]._handler as *const _ as *mut usize };
527 unsafe {
528 ptr.write_volatile(handler.raw_value());
529 }
530 }
531
532 pub fn bound_handler(interrupt: Interrupt) -> Option<IsrCallback> {
534 unsafe {
535 let addr = pac::__INTERRUPTS[interrupt as usize]._handler as usize;
536 if addr == 0 {
537 return None;
538 }
539 Some(IsrCallback::from_raw(addr))
540 }
541 }
542
543 fn interrupt_level_to_cpu_interrupt(
544 level: Priority,
545 is_edge: bool,
546 ) -> Result<CpuInterrupt, Error> {
547 Ok(if is_edge {
548 match level {
549 Priority::None => return Err(Error::InvalidInterrupt),
550 Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
551 Priority::Priority2 => return Err(Error::InvalidInterrupt),
552 Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
553 }
554 } else {
555 match level {
556 Priority::None => return Err(Error::InvalidInterrupt),
557 Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
558 Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
559 Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
560 }
561 })
562 }
563
564 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
566 pub(crate) static CPU_INTERRUPT_LEVELS: [u32; 8] = [
567 0b_0000_0000_0000_0000_0000_0000_0000_0000, 0b_0000_0000_0000_0110_0011_0111_1111_1111, 0b_0000_0000_0011_1000_0000_0000_0000_0000, 0b_0010_1000_1100_0000_1000_1000_0000_0000, 0b_0101_0011_0000_0000_0000_0000_0000_0000, 0b_1000_0100_0000_0001_0000_0000_0000_0000, 0b_0000_0000_0000_0000_0000_0000_0000_0000, 0b_0000_0000_0000_0000_0100_0000_0000_0000, ];
576 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
577 pub(crate) static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
578 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
579 pub(crate) static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
580
581 #[cfg(esp32)]
582 pub(crate) mod chip_specific {
583 use super::*;
584 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
585 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
586 0b0000_0000_0000_0000_0000_0000_0000_0000,
587 0b1111_1100_0000_0000_0000_0000_0000_0000,
588 0b0000_0000_0000_0000_0000_0000_0000_0011,
589 );
590 #[inline]
591 pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
592 [
593 Interrupt::TG0_T0_EDGE,
594 Interrupt::TG0_T1_EDGE,
595 Interrupt::TG0_WDT_EDGE,
596 Interrupt::TG0_LACT_EDGE,
597 Interrupt::TG1_T0_EDGE,
598 Interrupt::TG1_T1_EDGE,
599 Interrupt::TG1_WDT_EDGE,
600 Interrupt::TG1_LACT_EDGE,
601 ]
602 .contains(&interrupt)
603 }
604 }
605
606 #[cfg(esp32s2)]
607 pub(crate) mod chip_specific {
608 use super::*;
609 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
610 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
611 0b0000_0000_0000_0000_0000_0000_0000_0000,
612 0b1100_0000_0000_0000_0000_0000_0000_0000,
613 0b0000_0000_0000_0000_0000_0011_1011_1111,
614 );
615 #[inline]
616 pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
617 [
618 Interrupt::TG0_T0_EDGE,
619 Interrupt::TG0_T1_EDGE,
620 Interrupt::TG0_WDT_EDGE,
621 Interrupt::TG0_LACT_EDGE,
622 Interrupt::TG1_T0_EDGE,
623 Interrupt::TG1_T1_EDGE,
624 Interrupt::TG1_WDT_EDGE,
625 Interrupt::TG1_LACT_EDGE,
626 Interrupt::SYSTIMER_TARGET0,
627 Interrupt::SYSTIMER_TARGET1,
628 Interrupt::SYSTIMER_TARGET2,
629 ]
630 .contains(&interrupt)
631 }
632 }
633
634 #[cfg(esp32s3)]
635 pub(crate) mod chip_specific {
636 use super::*;
637 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
638 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::empty();
639 #[inline]
640 pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
641 false
642 }
643 }
644}
645
646#[cfg(feature = "rt")]
647mod rt {
648 use super::{vectored::*, *};
649
650 #[unsafe(no_mangle)]
651 #[ram]
652 unsafe fn __level_1_interrupt(save_frame: &mut Context) {
653 unsafe {
654 handle_interrupts::<1>(save_frame);
655 }
656 }
657
658 #[unsafe(no_mangle)]
659 #[ram]
660 unsafe fn __level_2_interrupt(save_frame: &mut Context) {
661 unsafe {
662 handle_interrupts::<2>(save_frame);
663 }
664 }
665
666 #[unsafe(no_mangle)]
667 #[ram]
668 unsafe fn __level_3_interrupt(save_frame: &mut Context) {
669 unsafe {
670 handle_interrupts::<3>(save_frame);
671 }
672 }
673
674 #[inline(always)]
675 unsafe fn handle_interrupts<const LEVEL: u32>(save_frame: &mut Context) {
676 let core = Cpu::current();
677
678 let cpu_interrupt_mask =
679 interrupt::get() & interrupt::get_mask() & CPU_INTERRUPT_LEVELS[LEVEL as usize];
680
681 if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
682 let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
687
688 let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
690
691 if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
694 unsafe {
695 interrupt::clear(1 << cpu_interrupt_nr);
696 }
697 }
698
699 if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
700 unsafe { handler(save_frame) };
701 }
702 } else {
703 let status = if !cfg!(esp32s3) && (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
704 unsafe { interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE) };
710
711 chip_specific::INTERRUPT_EDGE
714 } else {
715 status(core)
718 };
719
720 let configured_interrupts = configured_interrupts(core, status, LEVEL);
721 for interrupt_nr in configured_interrupts.iterator() {
722 let handler = unsafe { pac::__INTERRUPTS[interrupt_nr as usize]._handler };
723 let handler: fn(&mut Context) = unsafe {
724 core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler)
725 };
726 handler(save_frame);
727 }
728 }
729 }
730
731 #[inline]
732 pub(crate) fn cpu_interrupt_nr_to_cpu_interrupt_handler(
733 number: u32,
734 ) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
735 use xtensa_lx_rt::*;
736 Some(match number {
738 6 => Timer0,
739 7 => Software0,
740 11 => Profiling,
741 14 => NMI,
742 15 => Timer1,
743 16 => Timer2,
744 29 => Software1,
745 _ => return None,
746 })
747 }
748
749 unsafe extern "C" {
751 fn level4_interrupt(save_frame: &mut Context);
752 fn level5_interrupt(save_frame: &mut Context);
753 #[cfg(not(all(feature = "rt", feature = "exception-handler", stack_guard_monitoring)))]
754 fn level6_interrupt(save_frame: &mut Context);
755 fn level7_interrupt(save_frame: &mut Context);
756 }
757
758 #[unsafe(no_mangle)]
759 #[unsafe(link_section = ".rwtext")]
760 unsafe fn __level_4_interrupt(save_frame: &mut Context) {
761 unsafe { level4_interrupt(save_frame) }
762 }
763
764 #[unsafe(no_mangle)]
765 #[unsafe(link_section = ".rwtext")]
766 unsafe fn __level_5_interrupt(save_frame: &mut Context) {
767 unsafe { level5_interrupt(save_frame) }
768 }
769
770 #[unsafe(no_mangle)]
771 #[unsafe(link_section = ".rwtext")]
772 unsafe fn __level_6_interrupt(save_frame: &mut Context) {
773 cfg_if::cfg_if! {
774 if #[cfg(all(feature = "rt", feature = "exception-handler", stack_guard_monitoring))] {
775 crate::exception_handler::breakpoint_interrupt(save_frame);
776 } else {
777 unsafe { level6_interrupt(save_frame) }
778 }
779 }
780 }
781
782 #[unsafe(no_mangle)]
783 #[unsafe(link_section = ".rwtext")]
784 unsafe fn __level_7_interrupt(save_frame: &mut Context) {
785 unsafe { level7_interrupt(save_frame) }
786 }
787}