rticx-riscv 0.2.0

RTICX distribution for single-core RISC-V targets (generic SLIC, ESP32-C3, ESP32-C6)
// ESP32-C3: uses the `INTERRUPT_CORE0::cpu_int_thresh` register as the
// priority threshold and `SYSTEM::cpu_intr_from_cpu{0..3}` as dispatcher
// software interrupts. Upstream: `upstream/exports/riscv_esp32c3.rs`.
use esp32c3::INTERRUPT_CORE0;
pub use esp32c3::Interrupt;
pub use riscv::register::mcause;

// Re-export the standard `riscv::interrupt` helpers for the critical-section
// function generated by the core pass backend bindings.
pub use riscv::interrupt;

#[inline(always)]
pub fn run<F>(priority: u8, f: F)
where
    F: FnOnce(),
{
    unsafe {
        if priority == 1 {
            //if priority is 1, priority thresh should be 1
            f();

            (*INTERRUPT_CORE0::ptr())
                .cpu_int_thresh()
                .write(|w| w.cpu_int_thresh().bits(1));
        } else {
            // read current thresh
            let initial = (*INTERRUPT_CORE0::ptr())
                .cpu_int_thresh()
                .read()
                .cpu_int_thresh()
                .bits();
            f();
            // write back old thresh
            (*INTERRUPT_CORE0::ptr())
                .cpu_int_thresh()
                .write(|w| w.cpu_int_thresh().bits(initial));
        }
    }
}

/// Lock implementation using the priority threshold and a global Critical
/// Section (CS) for the ceiling = 15 (maximum priority) fallback.
///
/// # Safety
///
/// The caller upholds the SRP contract: `*ptr` is exclusive for the
/// duration of `f`, and `ceiling` matches the resource ceiling.
#[inline(always)]
pub unsafe fn lock<T, R>(ptr: *mut T, ceiling: u8, f: impl FnOnce(&mut T) -> R) -> R {
    unsafe {
        if ceiling == 15 {
            // Maximum priority: disable all interrupts via a critical section.
            critical_section::with(|_| f(&mut *ptr))
        } else {
            let current = (*INTERRUPT_CORE0::ptr())
                .cpu_int_thresh()
                .read()
                .cpu_int_thresh()
                .bits();

            // The ESP32-C3 lets interrupts with priority equal to the
            // threshold through, so we raise the threshold to `ceiling + 1`.
            (*INTERRUPT_CORE0::ptr())
                .cpu_int_thresh()
                .write(|w| w.cpu_int_thresh().bits(ceiling + 1));
            let r = f(&mut *ptr);

            (*INTERRUPT_CORE0::ptr())
                .cpu_int_thresh()
                .write(|w| w.cpu_int_thresh().bits(current));

            r
        }
    }
}

/// Sets the given software interrupt as pending.
#[inline(always)]
pub fn pend(int: Interrupt) {
    unsafe {
        let system = esp32c3::SYSTEM::steal();
        match int {
            Interrupt::FROM_CPU_INTR0 => system
                .cpu_intr_from_cpu(0)
                .write(|w| w.cpu_intr().set_bit()),
            Interrupt::FROM_CPU_INTR1 => system
                .cpu_intr_from_cpu(1)
                .write(|w| w.cpu_intr().set_bit()),
            Interrupt::FROM_CPU_INTR2 => system
                .cpu_intr_from_cpu(2)
                .write(|w| w.cpu_intr().set_bit()),
            Interrupt::FROM_CPU_INTR3 => system
                .cpu_intr_from_cpu(3)
                .write(|w| w.cpu_intr().set_bit()),
            _ => panic!("Unsupported software interrupt"),
        };
    }
}

/// Sets the given software interrupt as not pending.
pub fn unpend(int: Interrupt) {
    unsafe {
        let system = esp32c3::SYSTEM::steal();
        match int {
            Interrupt::FROM_CPU_INTR0 => system
                .cpu_intr_from_cpu(0)
                .write(|w| w.cpu_intr().clear_bit()),
            Interrupt::FROM_CPU_INTR1 => system
                .cpu_intr_from_cpu(1)
                .write(|w| w.cpu_intr().clear_bit()),
            Interrupt::FROM_CPU_INTR2 => system
                .cpu_intr_from_cpu(2)
                .write(|w| w.cpu_intr().clear_bit()),
            Interrupt::FROM_CPU_INTR3 => system
                .cpu_intr_from_cpu(3)
                .write(|w| w.cpu_intr().clear_bit()),
            _ => panic!("Unsupported software interrupt"),
        };
    }
}

/// Maps the peripheral interrupt `int` to CPU interrupt `cpu_int_id` and
/// enables it at the given priority.
pub fn enable(int: Interrupt, prio: u8, cpu_int_id: u8) {
    unsafe {
        // Map the peripheral interrupt to a CPU interrupt.
        (INTERRUPT_CORE0::ptr() as *mut u32)
            .offset(int as isize)
            .write_volatile(cpu_int_id as u32);

        // Set the interrupt's priority.
        (*INTERRUPT_CORE0::ptr())
            .cpu_int_pri(cpu_int_id as usize)
            .modify(|_, w| w.bits(prio as u32));

        // Enable the CPU interrupt.
        (*INTERRUPT_CORE0::ptr())
            .cpu_int_enable()
            .modify(|r, w| w.bits((1 << cpu_int_id) | r.bits()));
    }
}