1#[cfg(feature = "rt")]
13#[instability::unstable]
14pub use esp_riscv_rt::TrapFrame;
15
16#[cfg_attr(interrupt_controller = "riscv_basic", path = "riscv/basic.rs")]
17#[cfg_attr(interrupt_controller = "plic", path = "riscv/plic.rs")]
18#[cfg_attr(interrupt_controller = "clic", path = "riscv/clic.rs")]
19mod cpu_int;
20
21#[cfg(feature = "unstable")]
24pub(crate) use riscv::interrupt::free;
25
26use crate::{
27 interrupt::{PriorityError, RunLevel},
28 peripherals::Interrupt,
29 system::Cpu,
30};
31
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34#[instability::unstable]
35pub enum InterruptKind {
36 Level,
38 Edge,
40}
41
42for_each_interrupt!(
43 (all $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
44 paste::paste! {
45 #[repr(u32)]
47 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
49 #[instability::unstable]
50 pub enum CpuInterrupt {
51 $(
52 #[doc = concat!(" Interrupt number ", stringify!($n), ".")]
53 [<Interrupt $n>] = $n,
54 )*
55 }
56
57 impl CpuInterrupt {
58 #[inline]
59 pub(crate) fn from_u32(n: u32) -> Option<Self> {
60 match n {
61 $(n if n == $n && n != DISABLED_CPU_INTERRUPT => Some(Self:: [<Interrupt $n>]),)*
62 _ => None
63 }
64 }
65 }
66 }
67 };
68);
69
70for_each_classified_interrupt!(
71 (direct_bindable $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
72 paste::paste! {
73 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
75 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
76 #[instability::unstable]
77 pub enum DirectBindableCpuInterrupt {
78 $(
79 #[doc = concat!(" Direct bindable CPU interrupt number ", stringify!($idx_in_class), ".")]
80 #[doc = " "]
81 #[doc = concat!(" Corresponds to CPU interrupt ", stringify!($n), ".")]
82 [<Interrupt $idx_in_class>] = $n,
83 )*
84 }
85
86 impl From<DirectBindableCpuInterrupt> for CpuInterrupt {
87 fn from(bindable: DirectBindableCpuInterrupt) -> CpuInterrupt {
88 match bindable {
89 $(
90 DirectBindableCpuInterrupt::[<Interrupt $idx_in_class>] => CpuInterrupt::[<Interrupt $n>],
91 )*
92 }
93 }
94 }
95 }
96 };
97);
98
99impl CpuInterrupt {
100 #[inline]
101 #[cfg(feature = "rt")]
102 pub(crate) fn is_vectored(self) -> bool {
103 const VECTORED_CPU_INTERRUPT_RANGE: core::ops::RangeInclusive<u32> = PRIORITY_TO_INTERRUPT
105 [0] as u32
106 ..=PRIORITY_TO_INTERRUPT[PRIORITY_TO_INTERRUPT.len() - 1] as u32;
107 VECTORED_CPU_INTERRUPT_RANGE.contains(&(self as u32))
108 }
109
110 #[inline]
112 #[instability::unstable]
113 pub fn enable(self) {
114 cpu_int::enable_cpu_interrupt_raw(self as u32);
115 }
116
117 #[inline]
119 #[instability::unstable]
120 pub fn clear(self) {
121 cpu_int::clear_raw(self as u32);
122 }
123
124 #[inline]
130 #[instability::unstable]
131 pub fn set_kind(self, kind: InterruptKind) {
132 cpu_int::set_kind_raw(self as u32, kind);
133 }
134
135 #[inline]
137 #[instability::unstable]
138 pub fn set_priority(self, priority: Priority) {
139 cpu_int::set_priority_raw(self as u32, priority);
140 }
141
142 #[inline]
144 #[instability::unstable]
145 pub fn priority(self) -> Priority {
146 unwrap!(Priority::try_from_u32(self.level()))
147 }
148
149 #[inline]
150 pub(crate) fn level(self) -> u32 {
151 cpu_int::cpu_interrupt_priority_raw(self as u32) as u32
152 }
153}
154
155for_each_interrupt_priority!(
156 (all $( ($idx:literal, $n:literal, $ident:ident, $level:ident) ),*) => {
157 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
162 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
163 #[repr(u8)]
164 pub enum Priority {
165 $(
166 #[doc = concat!(" Priority level ", stringify!($n), ".")]
167 $ident = $n,
168 )*
169 }
170
171 impl Priority {
172 fn iter() -> impl Iterator<Item = Priority> {
173 [$(Priority::$ident,)*].into_iter()
174 }
175 }
176
177 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
179 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
180 #[repr(u8)]
181 pub enum ElevatedRunLevel {
182 $(
183 #[doc = concat!("Run level ", stringify!($n), ".")]
184 $level = $n,
185 )*
186 }
187
188 impl ElevatedRunLevel {
189 pub const fn from_priority(priority: Priority) -> Self {
191 match priority {
192 $(Priority::$ident => Self::$level,)*
193 }
194 }
195 }
196 };
197);
198
199impl Priority {
200 #[allow(unused_assignments)]
202 #[instability::unstable]
203 pub const fn max() -> Priority {
204 const {
205 let mut last = Self::min();
206 for_each_interrupt_priority!(
207 ($_idx:literal, $_n:literal, $ident:ident, $_level:ident) => {
208 last = Self::$ident;
209 };
210 );
211 last
212 }
213 }
214
215 pub const fn min() -> Priority {
217 Priority::Priority1
218 }
219
220 pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
221 let result;
222 for_each_interrupt_priority!(
223 (all $( ($idx:literal, $n:literal, $ident:ident, $_level:ident) ),*) => {
224 result = match priority {
225 $($n => Ok(Priority::$ident),)*
226 _ => Err(PriorityError::InvalidInterruptPriority),
227 }
228 };
229 );
230 result
231 }
232}
233
234impl ElevatedRunLevel {
235 #[instability::unstable]
237 pub const fn max() -> ElevatedRunLevel {
238 Self::from_priority(Priority::max())
239 }
240
241 pub const fn min() -> ElevatedRunLevel {
243 Self::from_priority(Priority::min())
244 }
245
246 pub(crate) fn try_from_u32(level: u32) -> Result<Self, PriorityError> {
247 Priority::try_from_u32(level).map(Self::from_priority)
248 }
249}
250
251#[instability::unstable]
252impl TryFrom<u32> for ElevatedRunLevel {
253 type Error = PriorityError;
254
255 fn try_from(value: u32) -> Result<Self, Self::Error> {
256 Self::try_from_u32(value)
257 }
258}
259
260impl From<Priority> for ElevatedRunLevel {
261 fn from(priority: Priority) -> Self {
262 Self::from_priority(priority)
263 }
264}
265
266#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
267pub(super) static DISABLED_CPU_INTERRUPT: u32 = property!("interrupts.disabled_interrupt");
268
269const VECTOR_COUNT: usize = const {
271 let mut count = 0;
272 for_each_interrupt!(([vector $n:tt] $_:literal) => { count += 1; };);
273
274 core::assert!(count == Priority::max() as usize);
275
276 count
277};
278
279#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
281pub(super) static PRIORITY_TO_INTERRUPT: [CpuInterrupt; VECTOR_COUNT] = const {
282 let mut counter = 0;
283 let mut vector = [CpuInterrupt::Interrupt0; VECTOR_COUNT];
284
285 for_each_interrupt!(
286 ([vector $_n:tt] $interrupt:literal) => {
287 vector[counter] = paste::paste! { CpuInterrupt::[<Interrupt $interrupt>] };
288 counter += 1;
289 };
290 );
291 vector
292};
293
294#[instability::unstable]
310pub fn enable_direct(
311 interrupt: Interrupt,
312 level: Priority,
313 cpu_interrupt: DirectBindableCpuInterrupt,
314 handler: unsafe extern "C" fn(),
315) {
316 cfg_select! {
317 interrupt_controller = "clic" => {
318 let clic = unsafe { crate::soc::pac::CLIC::steal() };
319
320 clic.int_attr(cpu_interrupt as usize).modify(|_, w| {
322 w.shv().hardware();
323 w.trig().positive_level()
324 });
325
326 let mtvt_table: *mut [u32; 48];
327 unsafe { core::arch::asm!("csrr {0}, 0x307", out(reg) mtvt_table) };
328
329 let int_slot = mtvt_table
330 .cast::<u32>()
331 .wrapping_add(cpu_interrupt as usize);
332
333 let instr = handler as usize as u32;
334 }
335 _ => {
336 use riscv::register::mtvec;
337 let mt = mtvec::read();
338
339 assert_eq!(
340 mt.trap_mode().into_usize(),
341 mtvec::TrapMode::Vectored.into_usize()
342 );
343
344 let base_addr = mt.address() as usize;
345
346 let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4) as *mut u32;
347
348 let instr = encode_jal_x0(handler as usize, int_slot as usize);
349 }
350 }
351
352 if crate::debugger::debugger_connected() {
353 unsafe { core::ptr::write_volatile(int_slot, instr) };
354 } else {
355 crate::debugger::DEBUGGER_LOCK.lock(|| unsafe {
356 let wp = crate::debugger::clear_watchpoint(1);
357 core::ptr::write_volatile(int_slot, instr);
358 crate::debugger::restore_watchpoint(1, wp);
359 });
360 }
361 unsafe {
362 core::arch::asm!("fence.i");
363 }
364
365 #[cfg(esp32p4)]
366 unsafe {
367 crate::soc::cache_writeback_addr(mtvt_table as u32, 48 * 4);
369 crate::soc::cache_invalidate_icache_addr(mtvt_table as u32, 48 * 4);
371 }
372
373 super::map_raw(Cpu::current(), interrupt, cpu_interrupt as u32);
374 cpu_int::set_priority_raw(cpu_interrupt as u32, level);
375 cpu_int::set_kind_raw(cpu_interrupt as u32, InterruptKind::Level);
376 cpu_int::enable_cpu_interrupt_raw(cpu_interrupt as u32);
377}
378
379#[cfg(not(interrupt_controller = "clic"))]
381fn encode_jal_x0(target: usize, pc: usize) -> u32 {
382 let offset = (target as isize) - (pc as isize);
383
384 const MIN: isize = -(1isize << 20);
385 const MAX: isize = (1isize << 20) - 1;
386
387 assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));
388
389 let imm = offset as u32;
390 let imm20 = (imm >> 20) & 0x1;
391 let imm10_1 = (imm >> 1) & 0x3ff;
392 let imm11 = (imm >> 11) & 0x1;
393 let imm19_12 = (imm >> 12) & 0xff;
394
395 (imm20 << 31)
396 | (imm19_12 << 12)
397 | (imm11 << 20)
398 | (imm10_1 << 21)
399 | 0b1101111u32
401}
402
403pub(crate) fn current_raw_runlevel() -> u32 {
407 cpu_int::current_runlevel() as u32
408}
409
410pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
418 let previous = cpu_int::change_current_runlevel(level);
419 unwrap!(RunLevel::try_from_u32(previous as u32))
420}
421
422fn cpu_wait_mode_on() -> bool {
423 cfg_select! {
424 soc_has_pcr => crate::peripherals::PCR::regs()
425 .cpu_waiti_conf()
426 .read()
427 .cpu_wait_mode_force_on()
428 .bit_is_set(),
429 soc_has_hp_sys => crate::peripherals::HP_SYS::regs()
430 .cpu_waiti_conf()
431 .read()
432 .cpu_wait_mode_force_on()
433 .bit_is_set(),
434 _ => crate::peripherals::SYSTEM::regs()
435 .cpu_per_conf()
436 .read()
437 .cpu_wait_mode_force_on()
438 .bit_is_set(),
439 }
440}
441
442#[inline(always)]
451#[instability::unstable]
452pub fn wait_for_interrupt() {
453 if crate::debugger::debugger_connected() && !cpu_wait_mode_on() {
454 return;
458 }
459 unsafe { core::arch::asm!("wfi") };
460}
461
462pub(crate) fn priority_to_cpu_interrupt(_interrupt: Interrupt, level: Priority) -> CpuInterrupt {
463 PRIORITY_TO_INTERRUPT[(level as usize) - 1]
464}
465
466#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
472pub(crate) unsafe fn init_vectoring() {
473 use riscv::register::mtvec;
474
475 unsafe extern "C" {
476 static _vector_table: u32;
477 }
478
479 unsafe {
480 let vec_table = (&raw const _vector_table).addr();
481
482 #[cfg(not(interrupt_controller = "clic"))]
483 {
484 mtvec::write({
485 let mut mtvec = mtvec::Mtvec::from_bits(0);
486 mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
487 mtvec.set_address(vec_table);
488 mtvec
489 });
490 }
491
492 #[cfg(interrupt_controller = "clic")]
493 {
494 mtvec::write({
495 let mut mtvec = mtvec::Mtvec::from_bits(0x03); mtvec.set_address(vec_table);
497 mtvec
498 });
499
500 let mtvt_table = match Cpu::current() {
502 Cpu::ProCpu => {
503 unsafe extern "C" {
504 static _mtvt_table: u32;
505 }
506 (&raw const _mtvt_table).addr()
507 }
508 #[cfg(multi_core)]
509 Cpu::AppCpu => {
510 unsafe extern "C" {
511 static _mtvt_table2: u32;
512 }
513 (&raw const _mtvt_table2).addr()
514 }
515 };
516 core::arch::asm!("csrw 0x307, {0}", in(reg) mtvt_table);
517 }
518 };
519
520 #[cfg(feature = "rt")]
523 cpu_int::init();
524
525 for (int, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(Priority::iter()) {
527 let num = int as u32;
528 cpu_int::set_kind_raw(num, InterruptKind::Level);
529 cpu_int::set_priority_raw(num, prio);
530 cpu_int::enable_cpu_interrupt_raw(num);
531 }
532}
533
534#[cfg(feature = "rt")]
535pub(crate) mod rt {
536 use esp_riscv_rt::TrapFrame;
537 use riscv::register::mcause;
538
539 use super::*;
540 use crate::interrupt::InterruptStatus;
541
542 #[cfg(not(interrupt_controller = "clic"))]
544 const INTERRUPT_COUNT: usize = const {
545 let mut count = 0;
546 for_each_interrupt!(([$_class:tt $n:tt] $_:literal) => { count += 1; };);
547 count
548 };
549
550 #[cfg(not(interrupt_controller = "clic"))]
552 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
553 pub(super) static INTERRUPT_TO_PRIORITY: [Option<Priority>; INTERRUPT_COUNT] = const {
554 let mut priorities = [None; INTERRUPT_COUNT];
555
556 for_each_interrupt!(
557 ([vector $n:tt] $int:literal) => {
558 for_each_interrupt_priority!(($n, $__:tt, $ident:ident, $_level:ident) => { priorities[$int] = Some(Priority::$ident); };);
559 };
560 );
561
562 priorities
563 };
564
565 #[doc(hidden)]
569 #[unsafe(link_section = ".trap.rust")]
570 #[unsafe(export_name = "_start_trap_rust_hal")]
571 unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
572 assert!(
573 mcause::read().is_exception(),
574 "Arrived into _start_trap_rust_hal but mcause is not an exception!"
575 );
576 unsafe extern "C" {
577 fn ExceptionHandler(tf: *mut TrapFrame);
578 }
579 unsafe {
580 ExceptionHandler(trap_frame);
581 }
582 }
583
584 #[doc(hidden)]
585 #[unsafe(no_mangle)]
586 #[unsafe(link_section = ".init")]
587 unsafe fn _setup_interrupts() {
588 crate::soc::riscv_preinit();
589 crate::interrupt::setup_interrupts();
590
591 #[cfg(interrupt_controller = "plic")]
592 unsafe {
593 core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
594 }
595 }
596
597 #[unsafe(no_mangle)]
598 #[crate::ram]
599 unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
600 let status = InterruptStatus::current();
601
602 cpu_intr.clear();
605
606 cfg_select! {
607 interrupt_controller = "clic" => {
608 let prio = cpu_int::current_runlevel();
609 let mcause = riscv::register::mcause::read();
610 }
611 _ => {
612 let prio = unwrap!(INTERRUPT_TO_PRIORITY[cpu_intr as usize]);
615 let level = unsafe {
616 change_current_runlevel(RunLevel::Interrupt(ElevatedRunLevel::from(prio)))
617 };
618 let prio = prio as u8;
619 }
620 }
621
622 let handle_interrupts = || unsafe {
623 for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
624 crate::interrupt::should_handle(Cpu::current(), interrupt_nr as u32, prio as u32)
625 }) {
626 let handler =
627 crate::soc::pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler;
628
629 handler();
630 }
631 };
632
633 if prio != Priority::max() as u8 {
637 unsafe {
638 riscv::interrupt::nested(handle_interrupts);
639 }
640 } else {
641 handle_interrupts();
642 }
643
644 cfg_select! {
645 interrupt_controller = "clic" => {
646 unsafe { core::arch::asm!("csrw 0x342, {}", in(reg) mcause.bits()) }
651 }
652 _ => {
653 unsafe { change_current_runlevel(level) };
654 }
655 }
656 }
657}