Skip to content

Commit

Permalink
Adapt SLIC to current version of the RISC-V ecosystem
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Dec 28, 2023
1 parent 955f070 commit e6706f7
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 114 deletions.
3 changes: 2 additions & 1 deletion hifive1-test/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[target.'cfg(all(target_arch = "riscv32", target_os = "none"))']
runner = "riscv64-unknown-elf-gdb -q -x gdb_init"
runner = "qemu-system-riscv32 -machine sifive_e,revb=true -nographic -kernel"
# runner = "riscv64-unknown-elf-gdb -q -x gdb_init"
# runner = "probe-run --chip fe310-g002 --verbose"
rustflags = [
"-C", "link-arg=-Thifive1-link.x",
Expand Down
3 changes: 0 additions & 3 deletions hifive1-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,3 @@ bare-metal = "0.2"
riscv-rt = {git = "https://github.com/rust-embedded/riscv-rt.git", branch = "master"}
# riscv-rt = {path = "../../../github/riscv-rt", features = ["atomic-emulation-trap"]}
panic-halt = "0.2.0"

[features]
qemu = [] # feature for running on qemu (CLINT timer is 10MHz instead of 32KHz)
11 changes: 3 additions & 8 deletions hifive1-test/examples/clint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ use hifive1::{pin, sprintln};
use riscv_rt::entry;
extern crate riscv_slic;

#[cfg(feature = "qemu")]
const PERIOD: u64 = 10000000; // 10 MHz in QEMU
#[cfg(not(feature = "qemu"))]
const PERIOD: u64 = 32_768; // 32.768 kHz in HW

// generate SLIC code for this example
riscv_slic::codegen!(
pac = e310x,
Expand All @@ -31,7 +26,7 @@ fn MachineTimer() {
let mtimecmp = CLINT::mtimecmp0();
let val = mtimecmp.read();
sprintln!("--- update MTIMECMP (mtimecmp = {}) ---", val);
mtimecmp.write(val + PERIOD);
mtimecmp.write(val + CLINT::freq() as u64);
riscv_slic::pend(SoftInterrupt::SoftMedium);
}

Expand Down Expand Up @@ -83,12 +78,12 @@ fn main() -> ! {
// 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(PERIOD);
mtimer.mtimecmp0.write(CLINT::freq() as u64);
mtimer.mtime.write(0);

sprintln!("Configuring SLIC...");
// make sure that interrupts are off
unsafe { riscv_slic::disable() };
riscv_slic::disable();
riscv_slic::clear_interrupts();
// Set priorities
unsafe {
Expand Down
11 changes: 3 additions & 8 deletions hifive1-test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ use hifive1::{pin, sprintln};
use riscv_rt::entry;
extern crate riscv_slic;

#[cfg(feature = "qemu")]
const PERIOD: u64 = 10000000; // 10 MHz in QEMU
#[cfg(not(feature = "qemu"))]
const PERIOD: u64 = 32_768; // 32.768 kHz in HW

// generate SLIC code for this example
riscv_slic::codegen!(
pac = e310x,
Expand All @@ -31,7 +26,7 @@ fn MachineTimer() {
let mtimecmp = CLINT::mtimecmp0();
let val = mtimecmp.read();
sprintln!("--- update MTIMECMP (mtimecmp = {}) ---", val);
mtimecmp.write(val + PERIOD);
mtimecmp.write(val + CLINT::freq() as u64);
riscv_slic::pend(SoftInterrupt::SoftMedium);
}

Expand Down Expand Up @@ -83,12 +78,12 @@ fn main() -> ! {
// 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(PERIOD);
mtimer.mtimecmp0.write(CLINT::freq() as u64);
mtimer.mtime.write(0);

sprintln!("Configuring SLIC...");
// make sure that interrupts are off
unsafe { riscv_slic::disable() };
riscv_slic::disable();
riscv_slic::clear_interrupts();
// Set priorities
unsafe {
Expand Down
59 changes: 42 additions & 17 deletions riscv-slic-macros/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,78 @@ pub fn api_mod() -> TokenStream {
quote!(
use riscv_slic::InterruptNumber; // expose the InterruptNumber trait

/// Clears all interrupt flags to avoid interruptions of SLIC and HW controller.
#[inline]
#[no_mangle]
pub unsafe fn __slic_clear() {
export_swi_clear();
}

/// Returns the current priority threshold of the SLIC.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
#[no_mangle]
pub unsafe fn __slic_get_threshold() -> u8 {
pub unsafe fn __riscv_slic_get_threshold() -> u8 {
__SLIC.get_threshold()
}

/// Sets the priority threshold of the external interrupt controller and the SLIC.
/// Sets the priority threshold of the SLIC.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
#[no_mangle]
pub unsafe fn __slic_set_threshold(thresh: u8) {
pub unsafe fn __riscv_slic_set_threshold(thresh: u8) {
__SLIC.set_threshold(thresh);
// check if we need to trigger a software interrupt after changing the threshold
if __SLIC.is_ready() {
export_swi_set();
__riscv_slic_swi_pend();
}
}

/// Returns the interrupt priority of a given software interrupt source.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
#[no_mangle]
pub unsafe fn __slic_get_priority(interrupt: u16) -> u8 {
pub unsafe fn __riscv_slic_get_priority(interrupt: u16) -> u8 {
__SLIC.get_priority(interrupt)
}

/// Sets the interrupt priority of a given software interrupt
/// source in the external interrupt controller and the SLIC.
/// Sets the interrupt priority of a given software interrupt source in the SLIC.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
#[no_mangle]
pub unsafe fn __slic_set_priority(interrupt: u16, priority: u8) {
pub unsafe fn __riscv_slic_set_priority(interrupt: u16, priority: u8) {
__SLIC.set_priority(interrupt, priority);
}

/// Marks a software interrupt as pending.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
#[no_mangle]
pub unsafe fn __slic_pend(interrupt: u16) {
pub unsafe fn __riscv_slic_pend(interrupt: u16) {
__SLIC.pend(interrupt);
if __SLIC.is_ready() {
export_swi_set();
__riscv_slic_swi_pend();
}
}

/// Polls the SLIC for pending software interrupts and runs them.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
#[no_mangle]
pub unsafe fn __riscv_slic_run() {
while let Some((priority, interrupt)) = __SLIC.pop() {
riscv_slic::run(priority, || __SOFTWARE_INTERRUPTS[interrupt as usize]());
}
}
)
Expand Down
18 changes: 14 additions & 4 deletions riscv-slic-macros/src/export/clint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,26 @@ pub fn export_quote(input: &CodegenInput) -> TokenStream {
let backend = input.backend.as_ref().unwrap();
let hart_id = &backend.hart_id;
quote! {
/// Triggers a machine software interrupt via the CLINT peripheral
/// Triggers a machine software interrupt via the CLINT peripheral.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
pub unsafe fn export_swi_set() {
#[no_mangle]
pub unsafe fn __riscv_slic_swi_pend() {
let msip = #pac::CLINT::mswi().msip(#pac::clint::HartId::#hart_id);
msip.pend();
}

/// Clears the Machine Software Interrupt Pending bit via the CLINT peripheral
/// Clears the Machine Software Interrupt Pending bit via the CLINT peripheral.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
pub unsafe fn export_swi_clear() {
#[no_mangle]
pub unsafe fn __riscv_slic_swi_unpend() {
let msip = #pac::CLINT::mswi().msip(#pac::clint::HartId::#hart_id);
msip.unpend();
}
Expand Down
22 changes: 16 additions & 6 deletions riscv-slic-macros/src/export/ssoft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,26 @@ impl Parse for ExportBackendInput {

pub fn export_quote(_input: &CodegenInput) -> TokenStream {
quote! {
/// Triggers a machine software interrupt via the CLINT peripheral
/// Triggers a supervisor software interrupt via the `SIP` register.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
pub unsafe fn export_swi_set() {
riscv_slic::riscv::register::mip::set_ssoft();
#[no_mangle]
pub unsafe fn __riscv_slic_swi_pend() {
riscv_slic::riscv::register::sip::set_ssoft();
}

/// Clears the Machine Software Interrupt Pending bit via the CLINT peripheral
/// Clears the Supervisor Software Interrupt Pending bit in the `SIP` register.
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
pub unsafe fn export_swi_clear() {
riscv_slic::riscv::register::mip::clear_ssoft();
#[no_mangle]
pub unsafe fn __riscv_slic_swi_unpend() {
riscv_slic::riscv::register::sip::clear_ssoft();
}
}
}
18 changes: 14 additions & 4 deletions riscv-slic-macros/src/export/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,25 @@ impl Parse for BackendInput {
pub fn export_quote(input: &CodegenInput) -> TokenStream {
quote! {
/// Triggers a software interrupt
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
pub unsafe fn export_swi_set() {
todo!();
#[no_mangle]
pub unsafe fn __riscv_slic_swi_pend() {
todo!("define how to trigger a software interrupt");
}

/// Clears a software interrupt
///
/// # Safety
///
/// This function is only for `riscv-slic` internal use. Do not call it directly.
#[inline]
pub unsafe fn export_swi_clear() {
todo!();
#[no_mangle]
pub unsafe fn __riscv_slic_swi_unpend() {
todo!("define how to clear a software interrupt");
}
}
}
3 changes: 1 addition & 2 deletions riscv-slic-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ mod export;
mod input;
mod swi;

// Ex. codegen!(pac, [HW1, HW2], [SW1, SW2])
// Ex. codegen!(e310x, [GPIO1, RTC], [Task1, Task2])
// Ex. codegen!(pac = <pac crate>, swi = [list, of, software, interrupts], backend = <backend-specific configuration>)
#[proc_macro]
pub fn codegen(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as input::CodegenInput);
Expand Down
24 changes: 12 additions & 12 deletions riscv-slic-macros/src/swi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ fn interrupts_enum(input: &[Ident]) -> Vec<TokenStream> {
fn swi_handler_signature() -> TokenStream {
match () {
#[cfg(feature = "msoft")]
() => "MachineSoft".parse().unwrap(),
() => "unsafe fn MachineSoft()".parse().unwrap(),
#[cfg(feature = "ssoft")]
() => "SupervisorSoft".parse().unwrap(),
() => "unsafe fn SupervisorSoft()".parse().unwrap(),
}
}

Expand All @@ -38,10 +38,12 @@ pub fn swi_mod(input: &CodegenInput) -> TokenStream {
unsafe impl riscv_slic::InterruptNumber for Interrupt {
const MAX_INTERRUPT_NUMBER: u16 = #n_interrupts as u16 - 1;

#[inline]
fn number(self) -> u16 {
self as _
}

#[inline]
fn from_number(value: u16) -> Result<Self, u16> {
if value > Self::MAX_INTERRUPT_NUMBER {
Err(value)
Expand All @@ -56,22 +58,20 @@ pub fn swi_mod(input: &CodegenInput) -> TokenStream {
#(fn #swi_handlers ();)*
}

#[no_mangle]
pub static __SOFTWARE_INTERRUPTS: [unsafe extern "C" fn(); #n_interrupts] = [
/// Array of software interrupt handlers in the order of the `Interrupt` enum.
static __SOFTWARE_INTERRUPTS: [unsafe extern "C" fn(); #n_interrupts] = [
#(#swi_handlers),*
];

pub static mut __SLIC: riscv_slic::SLIC<#n_interrupts> = riscv_slic::SLIC::new();
/// The static SLIC instance
static mut __SLIC: riscv_slic::SLIC<#n_interrupts> = riscv_slic::SLIC::new();

/// Software interrupt handler to be used with the SLIC.
#[no_mangle]
#[allow(non_snake_case)]
pub unsafe fn #swi_handler_signature() {
export_swi_clear(); // We clear the software interrupt flag to allow nested interrupts
riscv_slic::nested_isr(|| {
while let Some((priority, interrupt)) = __SLIC.pop() {
riscv_slic::run(priority, || __SOFTWARE_INTERRUPTS[interrupt as usize]());
}
});
#swi_handler_signature {
__riscv_slic_swi_unpend();
riscv_slic::riscv::interrupt::nested(|| unsafe { __riscv_slic_run() });
}
)
}
6 changes: 3 additions & 3 deletions riscv-slic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ version = "0.1.0"
edition = "2021"

[dependencies]
heapless = "0.8.0"
portable-atomic = { version = "1.4", default-features = false, features = ["require-cas"] }
heapless = "0.7.0"
riscv-peripheral ={ git = "https://github.com/romancardenas/riscv-peripheral.git", branch = "main" }
riscv ={ git = "https://github.com/rust-embedded/riscv.git", branch = "add-ecall" }
riscv-slic-macros = { path = "../riscv-slic-macros" }

[features]
msoft = [] # do not enable this feature directly. Use one of the *-backend features instead
ssoft = [] # do not enable this feature directly. Use one of the *-backend features instead
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
ssoft-backend = ["ssoft", "riscv-slic-macros/ssoft-backend"] # enable this feature to use supervisor-level software interrupts as SWI backend
Expand Down
Loading

0 comments on commit e6706f7

Please sign in to comment.