bl702-hal 0.0.3

HAL for the Bouffalo Lab BL702 microcontroller family
Documentation
//! Interrupt handling - RISCV
//!
//! When the `vectored` feature is enabled, CPU interrupts 1 through 15 are
//! reserved for each of the possible interrupt priorities.
//!
//! ```rust
//! interrupt1() => Priority::Priority1
//! interrupt2() => Priority::Priority2
//! ...
//! interrupt15() => Priority::Priority15
//! ```

use riscv::register::mcause;

use crate::pac::{self};

// User code shouldn't usually take the mutable TrapFrame or the TrapFrame in
// general. However this makes things like preemtive multitasking easier in
// future
extern "C" {
    fn interrupt1(frame: &mut TrapFrame);
    fn interrupt2(frame: &mut TrapFrame);
    fn interrupt3(frame: &mut TrapFrame);
    fn interrupt4(frame: &mut TrapFrame);
    fn interrupt5(frame: &mut TrapFrame);
    fn interrupt6(frame: &mut TrapFrame);
    fn interrupt7(frame: &mut TrapFrame);
    fn interrupt8(frame: &mut TrapFrame);
    fn interrupt9(frame: &mut TrapFrame);
    fn interrupt10(frame: &mut TrapFrame);
    fn interrupt11(frame: &mut TrapFrame);
    fn interrupt12(frame: &mut TrapFrame);
    fn interrupt13(frame: &mut TrapFrame);
    fn interrupt14(frame: &mut TrapFrame);
    fn interrupt15(frame: &mut TrapFrame);
    fn interrupt16(frame: &mut TrapFrame);
    fn interrupt17(frame: &mut TrapFrame);
    fn interrupt18(frame: &mut TrapFrame);
    fn interrupt19(frame: &mut TrapFrame);
    fn interrupt20(frame: &mut TrapFrame);
    fn interrupt21(frame: &mut TrapFrame);
    fn interrupt22(frame: &mut TrapFrame);
    fn interrupt23(frame: &mut TrapFrame);
    fn interrupt24(frame: &mut TrapFrame);
    fn interrupt25(frame: &mut TrapFrame);
    fn interrupt26(frame: &mut TrapFrame);
    fn interrupt27(frame: &mut TrapFrame);
    fn interrupt28(frame: &mut TrapFrame);
    fn interrupt29(frame: &mut TrapFrame);
    fn interrupt30(frame: &mut TrapFrame);
    fn interrupt31(frame: &mut TrapFrame);
}

/// Interrupt kind
pub enum InterruptKind {
    /// Level interrupt
    Level,
    /// Edge interrupt
    Edge,
}

/// Enumeration of available CPU interrupts.
/// It is possible to create a handler for each of the interrupts. (e.g.
/// `interrupt3`)
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum CpuInterrupt {
    Interrupt1 = 1,
    Interrupt2,
    Interrupt3,
    Interrupt4,
    Interrupt5,
    Interrupt6,
    Interrupt7,
    Interrupt8,
    Interrupt9,
    Interrupt10,
    Interrupt11,
    Interrupt12,
    Interrupt13,
    Interrupt14,
    Interrupt15,
    Interrupt16,
    Interrupt17,
    Interrupt18,
    Interrupt19,
    Interrupt20,
    Interrupt21,
    Interrupt22,
    Interrupt23,
    Interrupt24,
    Interrupt25,
    Interrupt26,
    Interrupt27,
    Interrupt28,
    Interrupt29,
    Interrupt30,
    Interrupt31,
}

/// Interrupt priority levels.
#[repr(u8)]
pub enum Priority {
    None,
    Priority1,
    Priority2,
    Priority3,
    Priority4,
    Priority5,
    Priority6,
    Priority7,
    Priority8,
    Priority9,
    Priority10,
    Priority11,
    Priority12,
    Priority13,
    Priority14,
    Priority15,
}

/// Assign a peripheral interrupt to an CPU interrupt.
///
/// Great care must be taken when using the `vectored` feature (enabled by
/// default). Avoid interrupts 1 - 15 when interrupt vectoring is enabled.
pub unsafe fn map(_core: u32, interrupt: u32, which: CpuInterrupt) {
    // let interrupt_number = interrupt as isize;
    // let cpu_interrupt_number = which as isize;
    // let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
    // let intr_map_base = intr.mac_intr_map.as_ptr();
    // intr_map_base
    //     .offset(interrupt_number)
    //     .write_volatile(cpu_interrupt_number as u32);
}

/// Enable a CPU interrupt
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
    // let cpu_interrupt_number = which as isize;
    // let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
    // intr.cpu_int_enable
    //     .modify(|r, w| w.bits((1 << cpu_interrupt_number) | r.bits()));
}

/// Disable the given peripheral interrupt.
pub fn disable(_core: u32, interrupt: u32) {
    // unsafe {
    //     let interrupt_number = interrupt as isize;
    //     let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
    //     let intr_map_base = intr.mac_intr_map.as_ptr();
    //     intr_map_base.offset(interrupt_number).write_volatile(0);
    // }
}

/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
///
/// This is safe to call when the `vectored` feature is enabled. The vectored
/// interrupt handler will take care of clearing edge interrupt bits.
pub fn set_kind(_core: u32, which: CpuInterrupt, kind: InterruptKind) {
    // unsafe {
    //     let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
    //     let cpu_interrupt_number = which as isize;

    //     let interrupt_type = match kind {
    //         InterruptKind::Level => 0,
    //         InterruptKind::Edge => 1,
    //     };
    //     intr.cpu_int_type.modify(|r, w| {
    //         w.bits(
    //             r.bits() & !(1 << cpu_interrupt_number) | (interrupt_type << cpu_interrupt_number),
    //         )
    //     });
    // }
}

/// Set the priority level of an CPU interrupt
///
/// Great care must be taken when using the `vectored` feature (enabled by
/// default). Avoid changing the priority of interrupts 1 - 15 when interrupt
/// vectoring is enabled.
pub unsafe fn set_priority(_core: u32, which: CpuInterrupt, priority: Priority) {
    // let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
    // let cpu_interrupt_number = which as isize;
    // let intr_prio_base = intr.cpu_int_pri_0.as_ptr();

    // intr_prio_base
    //     .offset(cpu_interrupt_number as isize)
    //     .write_volatile(priority as u32);
}

/// Clear a CPU interrupt
#[inline]
pub fn clear(_core: u32, which: CpuInterrupt) {
    unsafe {
        let cpu_interrupt_number = which as isize;
        // let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
        // intr.cpu_int_clear
        //     .write(|w| w.bits(1 << cpu_interrupt_number));
    }
}

/// Get status of peripheral interrupts
#[inline]
pub fn get_status(_core: u32) -> u128 {
    0
    // unsafe {
    //     crate::pac::
    //     ((*crate::pac::INTERRUPT_CORE0::PTR)
    //         .intr_status_reg_0
    //         .read()
    //         .bits() as u128)
    //         | ((*crate::pac::INTERRUPT_CORE0::PTR)
    //             .intr_status_reg_1
    //             .read()
    //             .bits() as u128)
    //             << 32
    // }
}

#[cfg(feature = "vectored")]
pub use vectored::*;

#[cfg(feature = "vectored")]
mod vectored {
    use procmacros::ram;

    use super::*;

    // Setup interrupts 1-15 ready for vectoring
    #[doc(hidden)]
    pub(crate) unsafe fn init_vectoring() {
        for i in 1..=15 {
            set_kind(
                crate::get_core(),
                core::mem::transmute(i),
                InterruptKind::Level,
            );
            set_priority(
                crate::get_core(),
                core::mem::transmute(i),
                core::mem::transmute(i as u8),
            );
            enable_cpu_interrupt(core::mem::transmute(i));
        }
    }

    /// Get the interrupts configured for the core
    #[inline]
    fn get_configured_interrupts(_core: Cpu, mut status: u128) -> [u128; 15] {
        unsafe {
            let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
            let intr_map_base = intr.mac_intr_map.as_ptr();
            let intr_prio_base = intr.cpu_int_pri_0.as_ptr();

            let mut prios = [0u128; 15];

            while status != 0 {
                let interrupt_nr = status.trailing_zeros();
                let i = interrupt_nr as isize;
                let cpu_interrupt = intr_map_base.offset(i).read_volatile();
                // safety: cast is safe because of repr(u32)
                let cpu_interrupt: CpuInterrupt = core::mem::transmute(cpu_interrupt);
                let prio = intr_prio_base
                    .offset(cpu_interrupt as isize)
                    .read_volatile();

                prios[prio as usize] |= 1 << i;
                status &= !(1u128 << interrupt_nr);
            }

            prios
        }
    }

    /// Interrupt Error
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub enum Error {
        InvalidInterruptPriority,
    }

    /// Enables a interrupt at a given priority
    ///
    /// Note that interrupts still need to be enabled globally for interrupts
    /// to be serviced.
    pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
        if matches!(level, Priority::None) {
            return Err(Error::InvalidInterruptPriority);
        }
        unsafe {
            let cpu_interrupt = core::mem::transmute(level as u8 as u32);
            map(crate::get_core(), interrupt, cpu_interrupt);
            enable_cpu_interrupt(cpu_interrupt);
        }
        Ok(())
    }

    #[ram]
    unsafe fn handle_interrupts(cpu_intr: CpuInterrupt, context: &mut TrapFrame) {
        let status = get_status(crate::get_core());

        // this has no effect on level interrupts, but the interrupt may be an edge one
        // so we clear it anyway
        clear(crate::get_core(), cpu_intr);

        let configured_interrupts = get_configured_interrupts(crate::get_core(), status);
        let mut interrupt_mask = status & configured_interrupts[cpu_intr as usize];
        while interrupt_mask != 0 {
            let interrupt_nr = interrupt_mask.trailing_zeros();
            // Interrupt::try_from can fail if interrupt already de-asserted:
            // silently ignore
            if let Ok(interrupt) = pac::Interrupt::try_from(interrupt_nr as u8) {
                handle_interrupt(interrupt, context)
            }
            interrupt_mask &= !(1u128 << interrupt_nr);
        }
    }

    #[ram]
    unsafe fn handle_interrupt(interrupt: Interrupt, save_frame: &mut TrapFrame) {
        extern "C" {
            // defined in each hal
            fn EspDefaultHandler(interrupt: Interrupt);
        }
        let handler = pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler;
        if handler as *const _ == EspDefaultHandler as *const unsafe extern "C" fn() {
            EspDefaultHandler(interrupt);
        } else {
            let handler: fn(&mut TrapFrame) = core::mem::transmute(handler);
            handler(save_frame);
        }
    }

    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt1(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt1, context)
    }

    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt2(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt2, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt3(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt3, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt4(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt4, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt5(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt5, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt6(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt6, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt7(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt7, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt8(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt8, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt9(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt9, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt10(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt10, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt11(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt11, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt12(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt12, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt13(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt13, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt14(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt14, context)
    }
    #[no_mangle]
    #[ram]
    pub unsafe fn interrupt15(context: &mut TrapFrame) {
        handle_interrupts(CpuInterrupt::Interrupt15, context)
    }
}

/// Registers saved in trap handler
#[doc(hidden)]
#[allow(missing_docs)]
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct TrapFrame {
    pub ra: usize,
    pub t0: usize,
    pub t1: usize,
    pub t2: usize,
    pub t3: usize,
    pub t4: usize,
    pub t5: usize,
    pub t6: usize,
    pub a0: usize,
    pub a1: usize,
    pub a2: usize,
    pub a3: usize,
    pub a4: usize,
    pub a5: usize,
    pub a6: usize,
    pub a7: usize,
    pub s0: usize,
    pub s1: usize,
    pub s2: usize,
    pub s3: usize,
    pub s4: usize,
    pub s5: usize,
    pub s6: usize,
    pub s7: usize,
    pub s8: usize,
    pub s9: usize,
    pub s10: usize,
    pub s11: usize,
    pub gp: usize,
    pub tp: usize,
    pub sp: usize,
}

/// # Safety
///
/// This function is called from an assembly trap handler.
#[doc(hidden)]
#[link_section = ".trap.rust"]
#[export_name = "_start_trap_rust_hal"]
pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
    extern "C" {
        // defined in riscv-rt
        pub fn DefaultHandler();
    }

    let cause = mcause::read();
    if cause.is_exception() {
        let pc = riscv::register::mepc::read();
        handle_exception(pc, trap_frame);
    } else {
        let code = riscv::register::mcause::read().code();
        match code {
            1 => interrupt1(trap_frame.as_mut().unwrap()),
            2 => interrupt2(trap_frame.as_mut().unwrap()),
            3 => interrupt3(trap_frame.as_mut().unwrap()),
            4 => interrupt4(trap_frame.as_mut().unwrap()),
            5 => interrupt5(trap_frame.as_mut().unwrap()),
            6 => interrupt6(trap_frame.as_mut().unwrap()),
            7 => interrupt7(trap_frame.as_mut().unwrap()),
            8 => interrupt8(trap_frame.as_mut().unwrap()),
            9 => interrupt9(trap_frame.as_mut().unwrap()),
            10 => interrupt10(trap_frame.as_mut().unwrap()),
            11 => interrupt11(trap_frame.as_mut().unwrap()),
            12 => interrupt12(trap_frame.as_mut().unwrap()),
            13 => interrupt13(trap_frame.as_mut().unwrap()),
            14 => interrupt14(trap_frame.as_mut().unwrap()),
            16 => interrupt16(trap_frame.as_mut().unwrap()),
            15 => interrupt15(trap_frame.as_mut().unwrap()),
            17 => interrupt17(trap_frame.as_mut().unwrap()),
            18 => interrupt18(trap_frame.as_mut().unwrap()),
            19 => interrupt19(trap_frame.as_mut().unwrap()),
            20 => interrupt20(trap_frame.as_mut().unwrap()),
            21 => interrupt21(trap_frame.as_mut().unwrap()),
            22 => interrupt22(trap_frame.as_mut().unwrap()),
            23 => interrupt23(trap_frame.as_mut().unwrap()),
            24 => interrupt24(trap_frame.as_mut().unwrap()),
            25 => interrupt25(trap_frame.as_mut().unwrap()),
            26 => interrupt26(trap_frame.as_mut().unwrap()),
            27 => interrupt27(trap_frame.as_mut().unwrap()),
            28 => interrupt28(trap_frame.as_mut().unwrap()),
            29 => interrupt29(trap_frame.as_mut().unwrap()),
            30 => interrupt30(trap_frame.as_mut().unwrap()),
            31 => interrupt31(trap_frame.as_mut().unwrap()),
            _ => DefaultHandler(),
        };
    }
}

/// Apply atomic emulation if needed. Call the default exception handler
/// otherwise.
///
/// # Safety
///
/// This function is called from an trap handler.
#[doc(hidden)]
unsafe fn handle_exception(pc: usize, trap_frame: *mut TrapFrame) {
    let insn: usize = *(pc as *const _);
    let needs_atomic_emulation = (insn & 0b1111111) == 0b0101111;

    if !needs_atomic_emulation {
        extern "C" {
            fn ExceptionHandler(tf: *mut TrapFrame);
        }
        ExceptionHandler(trap_frame);

        return;
    }

    extern "C" {
        pub fn _start_trap_atomic_rust(trap_frame: *mut riscv_atomic_emulation_trap::TrapFrame);
    }

    let mut atomic_emulation_trap_frame = riscv_atomic_emulation_trap::TrapFrame {
        pc: riscv::register::mepc::read(),
        ra: (*trap_frame).ra,
        sp: (*trap_frame).sp,
        gp: (*trap_frame).gp,
        tp: (*trap_frame).tp,
        t0: (*trap_frame).t0,
        t1: (*trap_frame).t1,
        t2: (*trap_frame).t2,
        fp: (*trap_frame).s0,
        s1: (*trap_frame).s1,
        a0: (*trap_frame).a0,
        a1: (*trap_frame).a1,
        a2: (*trap_frame).a2,
        a3: (*trap_frame).a3,
        a4: (*trap_frame).a4,
        a5: (*trap_frame).a5,
        a6: (*trap_frame).a6,
        a7: (*trap_frame).a7,
        s2: (*trap_frame).s2,
        s3: (*trap_frame).s3,
        s4: (*trap_frame).s4,
        s5: (*trap_frame).s5,
        s6: (*trap_frame).s6,
        s7: (*trap_frame).s7,
        s8: (*trap_frame).s8,
        s9: (*trap_frame).s9,
        s10: (*trap_frame).s10,
        s11: (*trap_frame).s11,
        t3: (*trap_frame).t3,
        t4: (*trap_frame).t4,
        t5: (*trap_frame).t5,
        t6: (*trap_frame).t6,
    };

    _start_trap_atomic_rust(&mut atomic_emulation_trap_frame);

    riscv::register::mepc::write(atomic_emulation_trap_frame.pc);
    (*trap_frame).ra = atomic_emulation_trap_frame.ra;
    (*trap_frame).sp = atomic_emulation_trap_frame.sp;
    (*trap_frame).gp = atomic_emulation_trap_frame.gp;
    (*trap_frame).tp = atomic_emulation_trap_frame.tp;
    (*trap_frame).t0 = atomic_emulation_trap_frame.t0;
    (*trap_frame).t1 = atomic_emulation_trap_frame.t1;
    (*trap_frame).t2 = atomic_emulation_trap_frame.t2;
    (*trap_frame).s0 = atomic_emulation_trap_frame.fp;
    (*trap_frame).s1 = atomic_emulation_trap_frame.s1;
    (*trap_frame).a0 = atomic_emulation_trap_frame.a0;
    (*trap_frame).a1 = atomic_emulation_trap_frame.a1;
    (*trap_frame).a2 = atomic_emulation_trap_frame.a2;
    (*trap_frame).a3 = atomic_emulation_trap_frame.a3;
    (*trap_frame).a4 = atomic_emulation_trap_frame.a4;
    (*trap_frame).a5 = atomic_emulation_trap_frame.a5;
    (*trap_frame).a6 = atomic_emulation_trap_frame.a6;
    (*trap_frame).a7 = atomic_emulation_trap_frame.a7;
    (*trap_frame).s2 = atomic_emulation_trap_frame.s2;
    (*trap_frame).s3 = atomic_emulation_trap_frame.s3;
    (*trap_frame).s4 = atomic_emulation_trap_frame.s4;
    (*trap_frame).s5 = atomic_emulation_trap_frame.s5;
    (*trap_frame).s6 = atomic_emulation_trap_frame.s6;
    (*trap_frame).s7 = atomic_emulation_trap_frame.s7;
    (*trap_frame).s8 = atomic_emulation_trap_frame.s8;
    (*trap_frame).s9 = atomic_emulation_trap_frame.s9;
    (*trap_frame).s10 = atomic_emulation_trap_frame.s10;
    (*trap_frame).s11 = atomic_emulation_trap_frame.s11;
    (*trap_frame).t3 = atomic_emulation_trap_frame.t3;
    (*trap_frame).t4 = atomic_emulation_trap_frame.t4;
    (*trap_frame).t5 = atomic_emulation_trap_frame.t5;
    (*trap_frame).t6 = atomic_emulation_trap_frame.t6;
}

#[doc(hidden)]
#[no_mangle]
pub fn _setup_interrupts() {
    extern "C" {
        // static _vector_table_hal: *const u32;
    }

    unsafe {
        // let vec_table = &_vector_table_hal as *const _ as usize;
        // riscv::register::mtvec::write(vec_table, riscv::register::mtvec::TrapMode::Vectored);

        #[cfg(feature = "vectored")]
        crate::interrupt::init_vectoring();
    };
}