From 31aac9fa6858b4c31369a6336c06fb031b7355ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Sun, 8 Sep 2024 00:04:23 +0100 Subject: [PATCH] New MECALL backend --- hifive1-test/Cargo.toml | 19 ++++- hifive1-test/examples/clint.rs | 58 +++++++------- hifive1-test/examples/mecall.rs | 104 +++++++++++++++++++++++++ hifive1-test/src/main.rs | 47 ++++++----- riscv-slic-macros/Cargo.toml | 1 + riscv-slic-macros/src/api.rs | 27 ++++--- riscv-slic-macros/src/export.rs | 5 ++ riscv-slic-macros/src/export/clint.rs | 4 +- riscv-slic-macros/src/export/mecall.rs | 45 +++++++++++ riscv-slic-macros/src/input.rs | 5 +- riscv-slic-macros/src/lib.rs | 4 +- riscv-slic-macros/src/swi.rs | 31 +++++--- riscv-slic/Cargo.toml | 3 +- riscv-slic/src/api.rs | 18 ++++- riscv-slic/src/lib.rs | 2 +- 15 files changed, 289 insertions(+), 84 deletions(-) create mode 100644 hifive1-test/examples/mecall.rs create mode 100644 riscv-slic-macros/src/export/mecall.rs diff --git a/hifive1-test/Cargo.toml b/hifive1-test/Cargo.toml index 97c652f..40bada7 100644 --- a/hifive1-test/Cargo.toml +++ b/hifive1-test/Cargo.toml @@ -6,9 +6,20 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -riscv-slic = {path = "../riscv-slic", features = ["clint-backend"]} -e310x = { git = "https://github.com/greenlsi/e310x.git", branch = "master"} -hifive1 = { git = "https://github.com/romancardenas/hifive1.git", branch = "master", features = ["board-redv"] } +riscv-slic = { path = "../riscv-slic" } +riscv-rt = { git = "https://github.com/rust-embedded/riscv", branch = "riscv-pac-only" } # TODO use crates.io +hifive1 = { git = "https://github.com/greenlsi/hifive1.git", branch = "master", features = ["board-redv"] } # TODO use crates.io bare-metal = "0.2" -riscv-rt = "0.12.0" panic-halt = "0.2.0" + +[features] +clint = ["riscv-slic/clint-backend"] +mecall = ["riscv-slic/mecall-backend"] + +[[example]] +name = "clint" +required-features = ["clint"] + +[[example]] +name = "mecall" +required-features = ["mecall"] diff --git a/hifive1-test/examples/clint.rs b/hifive1-test/examples/clint.rs index b842ff9..7152933 100644 --- a/hifive1-test/examples/clint.rs +++ b/hifive1-test/examples/clint.rs @@ -2,32 +2,30 @@ #![no_main] extern crate panic_halt; -use e310x::CLINT; -use hifive1::hal::prelude::*; -use hifive1::hal::DeviceResources; -use hifive1::{pin, sprintln}; - -use riscv_rt::entry; extern crate riscv_slic; +use hifive1::{ + hal::{ + e310x::{self, CLINT}, + prelude::*, + DeviceResources, + }, + pin, sprintln, +}; + // generate SLIC code for this example riscv_slic::codegen!( pac = e310x, swi = [SoftLow, SoftMedium, SoftHigh], - backend = [hart_id = HART0] + backend = [hart_id = H0] ); - -use slic::Interrupt as SoftInterrupt; // Re-export of automatically generated enum of interrupts in previous macro +use slic::SoftwareInterrupt; // Re-export of automatically generated enum of interrupts in previous macro /// HW handler for MachineTimer interrupts triggered by CLINT. -#[allow(non_snake_case)] -#[no_mangle] -fn MachineTimer() { +#[riscv_rt::core_interrupt(CoreInterrupt::MachineTimer)] +fn machine_timer() { let mtimecmp = CLINT::mtimecmp0(); - let val = mtimecmp.read(); - sprintln!("--- update MTIMECMP (mtimecmp = {}) ---", val); - mtimecmp.write(val + CLINT::freq() as u64); - riscv_slic::pend(SoftInterrupt::SoftMedium); + mtimecmp.modify(|val| *val += CLINT::freq() as u64); } /// Handler for SoftHigh task (high priority). @@ -43,9 +41,9 @@ fn SoftHigh() { #[no_mangle] fn SoftMedium() { sprintln!(" start SoftMedium"); - riscv_slic::pend(SoftInterrupt::SoftLow); + riscv_slic::pend(SoftwareInterrupt::SoftLow); sprintln!(" middle SoftMedium"); - riscv_slic::pend(SoftInterrupt::SoftHigh); + riscv_slic::pend(SoftwareInterrupt::SoftHigh); sprintln!(" stop SoftMedium"); } @@ -57,7 +55,7 @@ fn SoftLow() { sprintln!("stop SoftLow"); } -#[entry] +#[riscv_rt::entry] fn main() -> ! { let resources = DeviceResources::take().unwrap(); let peripherals = resources.peripherals; @@ -87,11 +85,10 @@ fn main() -> ! { riscv_slic::clear_interrupts(); // Set priorities unsafe { - riscv_slic::set_priority(SoftInterrupt::SoftLow, 1); // low priority - riscv_slic::set_priority(SoftInterrupt::SoftMedium, 2); // medium priority - riscv_slic::set_priority(SoftInterrupt::SoftHigh, 3); // high priority + riscv_slic::set_priority(SoftwareInterrupt::SoftLow, 1); // low priority + riscv_slic::set_priority(SoftwareInterrupt::SoftMedium, 2); // medium priority + riscv_slic::set_priority(SoftwareInterrupt::SoftHigh, 3); // high priority } - sprintln!("Done!"); sprintln!("Enabling interrupts..."); unsafe { @@ -99,15 +96,14 @@ fn main() -> ! { CLINT::mtimer_enable(); riscv_slic::enable(); } - //let mut delay = CLINT::delay(); + + sprintln!("Done!"); + loop { - // read stack pointer - let pc: usize; - unsafe { - core::arch::asm!("mv {}, sp", out(reg) pc); - } - sprintln!("Program counter: {:x}", pc); - sprintln!("Going to sleep!"); + sprintln!("Waiting for interrupts..."); riscv_slic::riscv::asm::wfi(); + sprintln!("Interrupt received!"); + riscv_slic::pend(SoftwareInterrupt::SoftMedium); + sprintln!(); } } diff --git a/hifive1-test/examples/mecall.rs b/hifive1-test/examples/mecall.rs new file mode 100644 index 0000000..d75c2d7 --- /dev/null +++ b/hifive1-test/examples/mecall.rs @@ -0,0 +1,104 @@ +#![no_std] +#![no_main] + +extern crate panic_halt; +extern crate riscv_slic; + +use hifive1::{ + hal::{ + e310x::{self, CLINT}, + prelude::*, + DeviceResources, + }, + pin, sprintln, +}; + +// generate SLIC code for this example +riscv_slic::codegen!(pac = e310x, swi = [SoftLow, SoftMedium, SoftHigh]); +use slic::SoftwareInterrupt; // Re-export of automatically generated enum of interrupts in previous macro + +/// HW handler for MachineTimer interrupts triggered by CLINT. +#[riscv_rt::core_interrupt(CoreInterrupt::MachineTimer)] +fn machine_timer() { + let mtimecmp = CLINT::mtimecmp0(); + mtimecmp.modify(|val| *val += CLINT::freq() as u64); +} + +/// Handler for SoftHigh task (high priority). +#[allow(non_snake_case)] +#[no_mangle] +fn SoftHigh() { + sprintln!(" start SoftHigh"); + sprintln!(" stop SoftHigh"); +} + +/// Handler for SoftMedium task (medium priority). This task pends both SoftLow and SoftHigh. +#[allow(non_snake_case)] +#[no_mangle] +fn SoftMedium() { + sprintln!(" start SoftMedium"); + riscv_slic::pend(SoftwareInterrupt::SoftLow); + sprintln!(" middle SoftMedium"); + riscv_slic::pend(SoftwareInterrupt::SoftHigh); + sprintln!(" stop SoftMedium"); +} + +/// Handler for SoftLow task (low priority). +#[allow(non_snake_case)] +#[no_mangle] +fn SoftLow() { + sprintln!("start SoftLow"); + sprintln!("stop SoftLow"); +} + +#[riscv_rt::entry] +fn main() -> ! { + let resources = DeviceResources::take().unwrap(); + let peripherals = resources.peripherals; + + let clocks = hifive1::configure_clocks(peripherals.PRCI, peripherals.AONCLK, 64.mhz().into()); + let gpio = resources.pins; + + // Configure UART for stdout + hifive1::stdout::configure( + peripherals.UART0, + pin!(gpio, uart0_tx), + pin!(gpio, uart0_rx), + 115_200.bps(), + clocks, + ); + + sprintln!("Configuring CLINT..."); + // First, we make sure that all PLIC the interrupts are disabled and set the interrupts priorities + CLINT::disable(); + let mtimer = CLINT::mtimer(); + mtimer.mtimecmp0.write(CLINT::freq() as u64); + mtimer.mtime.write(0); + + sprintln!("Configuring SLIC..."); + // make sure that interrupts are off + riscv_slic::disable(); + riscv_slic::clear_interrupts(); + // Set priorities + unsafe { + riscv_slic::set_priority(SoftwareInterrupt::SoftLow, 1); // low priority + riscv_slic::set_priority(SoftwareInterrupt::SoftMedium, 2); // medium priority + riscv_slic::set_priority(SoftwareInterrupt::SoftHigh, 3); // high priority + } + + sprintln!("Enabling interrupts..."); + unsafe { + riscv_slic::set_interrupts(); + CLINT::mtimer_enable(); + riscv_slic::enable(); + } + + sprintln!("Done!"); + + loop { + sprintln!("Waiting for interrupts..."); + riscv_slic::riscv::asm::wfi(); + sprintln!("Interrupt received!"); + riscv_slic::pend(SoftwareInterrupt::SoftMedium); + } +} diff --git a/hifive1-test/src/main.rs b/hifive1-test/src/main.rs index 0021420..cf532b9 100644 --- a/hifive1-test/src/main.rs +++ b/hifive1-test/src/main.rs @@ -2,32 +2,33 @@ #![no_main] extern crate panic_halt; -use e310x::CLINT; -use hifive1::hal::prelude::*; -use hifive1::hal::DeviceResources; -use hifive1::{pin, sprintln}; - -use riscv_rt::entry; extern crate riscv_slic; +use hifive1::{ + hal::{ + e310x::{self, CLINT}, + prelude::*, + DeviceResources, + }, + pin, sprintln, +}; + // generate SLIC code for this example riscv_slic::codegen!( pac = e310x, swi = [SoftLow, SoftMedium, SoftHigh], - backend = [hart_id = HART0] + backend = [hart_id = H0] ); - -use slic::Interrupt as SoftInterrupt; // Re-export of automatically generated enum of interrupts in previous macro +use slic::SoftwareInterrupt; // Re-export of automatically generated enum of interrupts in previous macro /// HW handler for MachineTimer interrupts triggered by CLINT. -#[allow(non_snake_case)] -#[no_mangle] -fn MachineTimer() { +#[riscv_rt::core_interrupt(CoreInterrupt::MachineTimer)] +fn machine_timer() { let mtimecmp = CLINT::mtimecmp0(); let val = mtimecmp.read(); sprintln!("--- update MTIMECMP (mtimecmp = {}) ---", val); mtimecmp.write(val + CLINT::freq() as u64); - riscv_slic::pend(SoftInterrupt::SoftMedium); + riscv_slic::pend(SoftwareInterrupt::SoftMedium); } /// Handler for SoftHigh task (high priority). @@ -43,9 +44,9 @@ fn SoftHigh() { #[no_mangle] fn SoftMedium() { sprintln!(" start SoftMedium"); - riscv_slic::pend(SoftInterrupt::SoftLow); + riscv_slic::pend(SoftwareInterrupt::SoftLow); sprintln!(" middle SoftMedium"); - riscv_slic::pend(SoftInterrupt::SoftHigh); + riscv_slic::pend(SoftwareInterrupt::SoftHigh); sprintln!(" stop SoftMedium"); } @@ -57,7 +58,7 @@ fn SoftLow() { sprintln!("stop SoftLow"); } -#[entry] +#[riscv_rt::entry] fn main() -> ! { let resources = DeviceResources::take().unwrap(); let peripherals = resources.peripherals; @@ -87,9 +88,9 @@ fn main() -> ! { riscv_slic::clear_interrupts(); // Set priorities unsafe { - riscv_slic::set_priority(SoftInterrupt::SoftLow, 1); // low priority - riscv_slic::set_priority(SoftInterrupt::SoftMedium, 2); // medium priority - riscv_slic::set_priority(SoftInterrupt::SoftHigh, 3); // high priority + riscv_slic::set_priority(SoftwareInterrupt::SoftLow, 1); // low priority + riscv_slic::set_priority(SoftwareInterrupt::SoftMedium, 2); // medium priority + riscv_slic::set_priority(SoftwareInterrupt::SoftHigh, 3); // high priority } sprintln!("Done!"); @@ -101,7 +102,13 @@ fn main() -> ! { } //let mut delay = CLINT::delay(); loop { + // read stack pointer + let sp: usize; + unsafe { + core::arch::asm!("mv {}, sp", out(reg) sp); + } + sprintln!("Stack counter: {:x}", sp); sprintln!("Going to sleep!"); - unsafe { riscv_slic::riscv::asm::wfi() }; + riscv_slic::riscv::asm::wfi(); } } diff --git a/riscv-slic-macros/Cargo.toml b/riscv-slic-macros/Cargo.toml index e4c957e..fb1d1ad 100644 --- a/riscv-slic-macros/Cargo.toml +++ b/riscv-slic-macros/Cargo.toml @@ -18,4 +18,5 @@ msoft = [] # do not enable this feature directly. Use one of the *-backend featu ssoft = [] # do not enable this feature directly. Use one of the *-backend features instead clint-backend = ["msoft"] # enable this feature to use the CLINT peripheral as SWI backend +mecall-backend = [] # enable this feature to use the machine-level ECALL instruction as SWI backend ssoft-backend = ["ssoft"] # enable this feature to use the supervisor-level software interrupt as SWI backend diff --git a/riscv-slic-macros/src/api.rs b/riscv-slic-macros/src/api.rs index 8b0eb79..0c9f09a 100644 --- a/riscv-slic-macros/src/api.rs +++ b/riscv-slic-macros/src/api.rs @@ -27,10 +27,12 @@ pub fn api_mod() -> TokenStream { #[no_mangle] pub unsafe fn __riscv_slic_set_threshold(thresh: u8) { critical_section::with(|cs| { - let mut slic = __SLIC.borrow_ref_mut(cs); - slic.set_threshold(thresh); - // trigger a software interrupt if the SLIC is still ready at this point - if slic.is_ready() { + if { + let mut slic = __SLIC.borrow_ref_mut(cs); + slic.set_threshold(thresh); + slic.is_ready() + } { + // trigger a software interrupt if the SLIC is still ready at this point __riscv_slic_swi_pend(); } }); @@ -48,10 +50,13 @@ pub fn api_mod() -> TokenStream { #[no_mangle] pub unsafe fn __riscv_slic_raise_threshold(priority: u8) -> Result { critical_section::with(|cs| { - let mut slic = __SLIC.borrow_ref_mut(cs); - let res = slic.raise_threshold(priority); + let (res, is_ready) = { + let mut slic = __SLIC.borrow_ref_mut(cs); + let res = slic.raise_threshold(priority); + (res, slic.is_ready()) + }; // trigger a software interrupt if the SLIC is still ready at this point - if slic.is_ready() { + if is_ready { __riscv_slic_swi_pend(); } res @@ -91,9 +96,11 @@ pub fn api_mod() -> TokenStream { #[no_mangle] pub unsafe fn __riscv_slic_pend(interrupt: u16) { critical_section::with(|cs| { - let mut slic = __SLIC.borrow_ref_mut(cs); - slic.pend(interrupt); - if slic.is_ready() { + if { + let mut slic = __SLIC.borrow_ref_mut(cs); + slic.pend(interrupt); + slic.is_ready() + } { __riscv_slic_swi_pend(); } }); diff --git a/riscv-slic-macros/src/export.rs b/riscv-slic-macros/src/export.rs index 05dc330..0787d87 100644 --- a/riscv-slic-macros/src/export.rs +++ b/riscv-slic-macros/src/export.rs @@ -3,6 +3,11 @@ mod clint; #[cfg(feature = "clint-backend")] pub use clint::{export_quote, ExportBackendInput}; +#[cfg(feature = "mecall-backend")] +mod mecall; +#[cfg(feature = "mecall-backend")] +pub use mecall::{export_quote, ExportBackendInput}; + #[cfg(feature = "ssoft-backend")] mod ssoft; #[cfg(feature = "ssoft-backend")] diff --git a/riscv-slic-macros/src/export/clint.rs b/riscv-slic-macros/src/export/clint.rs index 17bfc5c..ccf2b25 100644 --- a/riscv-slic-macros/src/export/clint.rs +++ b/riscv-slic-macros/src/export/clint.rs @@ -53,7 +53,7 @@ pub fn export_quote(input: &CodegenInput) -> TokenStream { #[inline] #[no_mangle] pub unsafe fn __riscv_slic_swi_pend() { - let msip = #pac::CLINT::mswi().msip(#pac::HartId::#hart_id); + let msip = #pac::CLINT::mswi().msip(#pac::interrupt::Hart::#hart_id); msip.pend(); } @@ -65,7 +65,7 @@ pub fn export_quote(input: &CodegenInput) -> TokenStream { #[inline] #[no_mangle] pub unsafe fn __riscv_slic_swi_unpend() { - let msip = #pac::CLINT::mswi().msip(#pac::HartId::#hart_id); + let msip = #pac::CLINT::mswi().msip(#pac::interrupt::Hart::#hart_id); msip.unpend(); } } diff --git a/riscv-slic-macros/src/export/mecall.rs b/riscv-slic-macros/src/export/mecall.rs new file mode 100644 index 0000000..023b57b --- /dev/null +++ b/riscv-slic-macros/src/export/mecall.rs @@ -0,0 +1,45 @@ +use crate::input::CodegenInput; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + Error, Result, +}; + +pub struct ExportBackendInput(); + +impl Parse for ExportBackendInput { + fn parse(input: ParseStream) -> Result { + Err(Error::new( + input.span(), + "This backend does not require any input", + )) + } +} + +pub fn export_quote(_input: &CodegenInput) -> TokenStream { + quote! { + /// Triggers an environment call exception + /// + /// # Safety + /// + /// This function is only for `riscv-slic` internal use. Do not call it directly. + #[inline] + #[no_mangle] + pub unsafe fn __riscv_slic_swi_pend() { + riscv::asm::ecall(); + } + + /// Increments the machine exception program counter by 4 + /// + /// # Safety + /// + /// This function is only for `riscv-slic` internal use. Do not call it directly. + #[inline] + #[no_mangle] + pub unsafe fn __riscv_slic_swi_unpend() { + let mepc = riscv::register::mepc::read(); + riscv::register::mepc::write(mepc + 4); + } + } +} diff --git a/riscv-slic-macros/src/input.rs b/riscv-slic-macros/src/input.rs index deaf69f..2f1653d 100644 --- a/riscv-slic-macros/src/input.rs +++ b/riscv-slic-macros/src/input.rs @@ -1,5 +1,5 @@ use syn::parse::Parse; -use syn::{bracketed, parse::ParseStream, token::Comma, Error, Ident, Result, Token}; +use syn::{bracketed, parse::ParseStream, token::Comma, Error, Ident, Path, Result, Token}; pub use crate::export::ExportBackendInput; // backend-specific input @@ -23,8 +23,9 @@ impl Parse for HandlersInput { } pub struct CodegenInput { - pub pac: Ident, + pub pac: Path, pub swi_handlers: HandlersInput, + #[allow(dead_code)] pub backend: Option, } diff --git a/riscv-slic-macros/src/lib.rs b/riscv-slic-macros/src/lib.rs index 9deaca8..fb42258 100644 --- a/riscv-slic-macros/src/lib.rs +++ b/riscv-slic-macros/src/lib.rs @@ -10,6 +10,7 @@ mod swi; #[proc_macro] pub fn codegen(input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input as input::CodegenInput); + let pac = &input.pac; let api_code = api::api_mod(); @@ -19,7 +20,8 @@ pub fn codegen(input: TokenStream) -> TokenStream { quote! { /// The RISC-V SLIC module pub mod slic { - use super::riscv_slic::*; + use super::#pac; + use riscv_slic::*; #api_code diff --git a/riscv-slic-macros/src/swi.rs b/riscv-slic-macros/src/swi.rs index 619e731..aa2a54c 100644 --- a/riscv-slic-macros/src/swi.rs +++ b/riscv-slic-macros/src/swi.rs @@ -1,6 +1,7 @@ use crate::input::CodegenInput; use proc_macro2::{Ident, TokenStream}; use quote::quote; +use syn::Path; /// Helper function for generating the interrupt enums. It assigns a number to each source. fn interrupts_enum(input: &[Ident]) -> Vec { @@ -11,12 +12,20 @@ fn interrupts_enum(input: &[Ident]) -> Vec { .collect() } -fn swi_handler_signature() -> TokenStream { +fn swi_handler_attribute(pac: &Path) -> TokenStream { match () { + #[cfg(feature = "mecall-backend")] + () => quote! { + #[riscv_rt::exception(#pac::interrupt::Exception::MachineEnvCall)] + }, #[cfg(feature = "msoft")] - () => "unsafe fn MachineSoft()".parse().unwrap(), + () => quote! { + #[riscv_rt::core_interrupt(#pac::interrupt::CoreInterrupt::MachineSoft)] + }, #[cfg(feature = "ssoft")] - () => "unsafe fn SupervisorSoft()".parse().unwrap(), + () => quote! { + #[riscv_rt::core_interrupt(#pac::interrupt::CoreInterrupt::SupervisorSoft)] + }, } } @@ -27,23 +36,23 @@ pub fn swi_mod(input: &CodegenInput) -> TokenStream { let swi_handlers = &input.swi_handlers; let n_interrupts = swi_handlers.len(); let swi_enums = interrupts_enum(swi_handlers); - let swi_handler_signature = swi_handler_signature(); + let swi_handler_attribute = swi_handler_attribute(&input.pac); if n_interrupts > 0 { res.push(quote!( #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[doc(hidden)] #[repr(u16)] - pub enum Interrupt { + pub enum SoftwareInterrupt { #(#swi_enums),* } - unsafe impl InterruptNumber for Interrupt { + unsafe impl riscv_slic::InterruptNumber for SoftwareInterrupt { const MAX_INTERRUPT_NUMBER: u16 = #n_interrupts as u16 - 1; #[inline] fn number(self) -> u16 { - self as _ + self as u16 } #[inline] @@ -72,11 +81,11 @@ pub fn swi_mod(input: &CodegenInput) -> TokenStream { static mut __SLIC: MutexSLIC<#n_interrupts> = new_slic(); /// Software interrupt handler to be used with the SLIC. - #[no_mangle] - #[allow(non_snake_case)] - #swi_handler_signature { + #swi_handler_attribute + unsafe fn riscv_slic_swi_handler() { __riscv_slic_swi_unpend(); - riscv::interrupt::nested(|| unsafe { __riscv_slic_pop() }); + // We nest the handler to let other interrupts trigger + riscv_slic::nested(|| unsafe { __riscv_slic_pop() }); } )); quote!(#(#res)*) diff --git a/riscv-slic/Cargo.toml b/riscv-slic/Cargo.toml index 01184bc..ddc1987 100644 --- a/riscv-slic/Cargo.toml +++ b/riscv-slic/Cargo.toml @@ -17,7 +17,7 @@ features = ["clint-backend"] [dependencies] critical-section = "1.1.2" heapless = "0.8.0" -riscv = "0.11.1" +riscv = { git = "https://github.com/rust-embedded/riscv", branch = "riscv-pac-only" } # TODO use crates.io riscv-slic-macros = { path = "../riscv-slic-macros", version = "0.1.0" } [features] @@ -25,4 +25,5 @@ msoft = [] # do not enable this feature directly. Use one of the *-backend featu ssoft = ["riscv/s-mode"] # do not enable this feature directly. Use one of the *-backend features instead clint-backend = ["msoft", "riscv-slic-macros/clint-backend"] # enable this feature to use the CLINT peripheral as SWI backend +mecall-backend = ["riscv-slic-macros/mecall-backend"] # enable this feature to use the machine-level ECALL instruction as SWI backend ssoft-backend = ["ssoft", "riscv-slic-macros/ssoft-backend"] # enable this feature to use supervisor-level software interrupts as SWI backend diff --git a/riscv-slic/src/api.rs b/riscv-slic/src/api.rs index 036f7ed..3fe721e 100644 --- a/riscv-slic/src/api.rs +++ b/riscv-slic/src/api.rs @@ -1,4 +1,4 @@ -pub use riscv::interrupt::{disable, enable}; +pub use riscv::interrupt::{disable, enable, nested}; #[cfg(feature = "msoft")] use riscv::register::mie::{clear_msoft as disable_swi, set_msoft as enable_swi}; @@ -26,6 +26,7 @@ extern "Rust" { pub fn clear_interrupts() { // SAFETY: interrupts are disabled before modifying thresholds/priorities unsafe { + #[cfg(not(feature = "mecall-backend"))] disable_swi(); __riscv_slic_swi_unpend(); __riscv_slic_set_threshold(u8::MAX); @@ -46,6 +47,7 @@ pub fn clear_interrupts() { #[inline] pub unsafe fn set_interrupts() { __riscv_slic_set_threshold(0); + #[cfg(not(feature = "mecall-backend"))] enable_swi(); } @@ -79,6 +81,20 @@ pub unsafe fn set_priority(interrupt: I, priority: u8 } /// Stabilized API for pending a software interrupt on the SLIC. +/// +/// # Note +/// +/// When working with the `mecall-backend` feature, special care must be taken +/// when using this function **inside** an interrupt handler. This is because +/// this backend uses the `ecall` instruction to trigger software interrupts. +/// If you call this function inside an interrupt handler, the `mepc` register +/// will be incremented by 4, leading to unexpected behavior. +/// To avoid this, make sure that: +/// +/// - You only call this function **outside** of interrupt handlers. +/// - If you need to trigger a software interrupt inside an interrupt handler, +/// do it inside a [`nested`] block. This will ensure that the +/// `mepc` register is not incremented incorrectly. #[inline] pub fn pend(interrupt: I) { // SAFETY: it is safe to pend a software interrupt diff --git a/riscv-slic/src/lib.rs b/riscv-slic/src/lib.rs index c728ee3..381b22c 100644 --- a/riscv-slic/src/lib.rs +++ b/riscv-slic/src/lib.rs @@ -10,7 +10,7 @@ mod slic; pub use api::*; pub use slic::{new_slic, MutexSLIC}; -/// Trait for enums of interrupt numbers. +/// Trait for enums of software interrupt numbers. /// /// This trait should only be implemented by the [`riscv_slic_macros::codegen`] /// macro for the enum of available software interrupts.