From 9ac2ec96ff684f4047764f8a661256ca3db547f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Thu, 11 Apr 2024 10:59:46 +0200 Subject: [PATCH 1/7] First draft --- .github/workflows/riscv-rt.yaml | 4 +- riscv-rt/CHANGELOG.md | 3 + riscv-rt/Cargo.toml | 9 ++- riscv-rt/examples/empty.rs | 8 +- riscv-rt/link.x.in | 17 ++++ riscv-rt/macros/Cargo.toml | 4 + riscv-rt/macros/src/lib.rs | 133 ++++++++++++++++++++++++++++++++ riscv-rt/src/asm.rs | 41 +++++++++- riscv-rt/src/lib.rs | 6 ++ 9 files changed, 216 insertions(+), 9 deletions(-) diff --git a/.github/workflows/riscv-rt.yaml b/.github/workflows/riscv-rt.yaml index 0c20eb31..5cfd35bc 100644 --- a/.github/workflows/riscv-rt.yaml +++ b/.github/workflows/riscv-rt.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, riscv-rt-asm ] + branches: [ master, vectored-rt ] pull_request: merge_group: @@ -39,6 +39,8 @@ jobs: run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=s-mode - name : Build (single-hart) run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=single-hart + - name : Build (v-trap) + run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=v-trap - name: Build (all features) run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --all-features diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 56866394..7348a237 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add `pre_init_trap` to detect early errors during the boot process. +- Add `v-trap` feature to enable interrupt handling in vectored mode. +- Add `interrupt` proc macro to help defining interrupt handlers. +If `v-trap` feature is enabled, this macro also generates its corresponding trap. ### Changed diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 1c33bea1..319891f8 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -12,13 +12,14 @@ license = "ISC" edition = "2021" links = "riscv-rt" # Prevent multiple versions of riscv-rt being linked -[features] -s-mode = [] -single-hart = [] - [dependencies] riscv = {path = "../riscv", version = "0.11.1"} riscv-rt-macros = { path = "macros", version = "0.2.1" } [dev-dependencies] panic-halt = "0.2.0" + +[features] +s-mode = ["riscv-rt-macros/s-mode"] +single-hart = [] +v-trap = ["riscv-rt-macros/v-trap"] diff --git a/riscv-rt/examples/empty.rs b/riscv-rt/examples/empty.rs index 5e4d2384..b770cba0 100644 --- a/riscv-rt/examples/empty.rs +++ b/riscv-rt/examples/empty.rs @@ -4,10 +4,16 @@ extern crate panic_halt; extern crate riscv_rt; -use riscv_rt::entry; +use riscv_rt::{entry, interrupt}; #[entry] fn main() -> ! { // do something here loop {} } + +#[interrupt] +fn MachineSoft() { + // do something here + loop {} +} diff --git a/riscv-rt/link.x.in b/riscv-rt/link.x.in index 720d5719..462ddd58 100644 --- a/riscv-rt/link.x.in +++ b/riscv-rt/link.x.in @@ -28,6 +28,23 @@ PROVIDE(_max_hart_id = 0); PROVIDE(_hart_stack_size = 2K); PROVIDE(_heap_size = 0); +/** TRAP ENTRY POINTS **/ + +/* Default trap entry point. The riscv-rt crate provides a weak alias of this function, + which saves caller saved registers, calls _start_trap_rust, restores caller saved registers + and then returns. Users can override this alias by defining the symbol themselves */ +EXTERN(_start_trap); + +/* When vectored trap mode is enabled, each interrupt source must implement its own + trap entry point. By default, all interrupts start in _start_trap. However, users can + override these alias by defining the symbol themselves */ +PROVIDE(_start_SupervisorSoft_trap = _start_trap); +PROVIDE(_start_MachineSoft_trap = _start_trap); +PROVIDE(_start_SupervisorTimer_trap = _start_trap); +PROVIDE(_start_MachineTimer_trap = _start_trap); +PROVIDE(_start_SupervisorExternal_trap = _start_trap); +PROVIDE(_start_MachineExternal_trap = _start_trap); + /** EXCEPTION HANDLERS **/ /* Default exception handler. The riscv-rt crate provides a weak alias of this function, diff --git a/riscv-rt/macros/Cargo.toml b/riscv-rt/macros/Cargo.toml index f300fa1b..17b8e9d1 100644 --- a/riscv-rt/macros/Cargo.toml +++ b/riscv-rt/macros/Cargo.toml @@ -22,3 +22,7 @@ proc-macro2 = "1.0" [dependencies.syn] version = "1.0" features = ["extra-traits", "full"] + +[features] +s-mode = [] +v-trap = [] diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 14021902..c76f7d71 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -312,3 +312,136 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream { let res = format!("core::arch::global_asm!(\n\"{}\"\n);", instructions); res.parse().unwrap() } + +enum RiscvArch { + Rv32, + Rv64, +} + +#[proc_macro_attribute] +pub fn interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream { + interrupt(args, input, RiscvArch::Rv32) +} + +#[proc_macro_attribute] +pub fn interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream { + interrupt(args, input, RiscvArch::Rv64) +} + +fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenStream { + let f = parse_macro_input!(input as ItemFn); + + // check the function signature + let valid_signature = f.sig.constness.is_none() + && f.sig.asyncness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[interrupt]` function must have signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + // XXX should we blacklist other attributes? + let attrs = f.attrs; + let ident = f.sig.ident; + let block = f.block; + + #[cfg(not(feature = "v-trap"))] + let start_trap = proc_macro2::TokenStream::new(); + #[cfg(feature = "v-trap")] + let start_trap = v_trap::start_interrupt_trap_asm(&ident, _arch); + + quote!( + #start_trap + #[export_name = #ident] + #(#attrs)* + pub unsafe fn #ident() #block + ) + .into() +} + +#[cfg(feature = "v-trap")] +mod v_trap { + use super::*; + + const TRAP_SIZE: usize = 16; + + #[rustfmt::skip] + const TRAP_FRAME: [&str; TRAP_SIZE] = [ + "ra", + "t0", + "t1", + "t2", + "t3", + "t4", + "t5", + "t6", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", + ]; + + pub fn start_interrupt_trap_asm( + ident: &syn::Ident, + arch: RiscvArch, + ) -> proc_macro2::TokenStream { + let function = ident.to_string(); + let (width, store, load) = match arch { + RiscvArch::Rv32 => (4, "sw", "lw"), + RiscvArch::Rv64 => (8, "sd", "ld"), + }; + + let (mut stores, mut loads) = (Vec::new(), Vec::new()); + for (i, r) in TRAP_FRAME.iter().enumerate() { + stores.push(format!(" {store} {r}, {i}*{width}(sp)")); + loads.push(format!(" {load} {r}, {i}*{width}(sp)")); + } + let store = stores.join("\n"); + let load = loads.join("\n"); + + #[cfg(feature = "s-mode")] + let ret = "sret"; + #[cfg(not(feature = "s-mode"))] + let ret = "mret"; + + let instructions = format!( + " +core::arch::global_asm!( + \".section .trap, \\\"ax\\\" + .align {width} + _start_{function}_trap: + addi sp, sp, - {TRAP_SIZE} * {width} +{store} + call {function} +{load} + addi sp, sp, {TRAP_SIZE} * {width} + {ret}\" +);" + ); + + instructions.parse().unwrap() + } +} diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index 1aa962b3..d8f98e51 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -245,11 +245,14 @@ _mp_hook: 2: li a0, 1 ret", // Default implementation of `_setup_interrupts` sets the trap vector to `_start_trap`. - // Trap mode is set to `Direct` by default. // Users can override this function by defining their own `_setup_interrupts` ".weak _setup_interrupts -_setup_interrupts: - la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode +_setup_interrupts:", + #[cfg(not(feature = "v-trap"))] + "la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode + #[cfg(feature = "v-trap")] + "la t0, _vector_table + ori t0, t0, 0x1", // _vector_table is 16-byte aligned, so we must set the bit 0 to activate the Vectored trap mode #[cfg(feature = "s-mode")] "csrw stvec, t0", #[cfg(not(feature = "s-mode"))] @@ -334,6 +337,38 @@ trap_handler!( (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)] ); +#[cfg(feature = "v-trap")] +cfg_global_asm!( + // Set the vector mode to vectored. + ".section .trap, \"ax\" + .weak _vector_table + .type _vector_table, @function + + .option push + .balign 0x100 // TODO check if this is the correct alignment + .option norelax + .option norvc + + _vector_table: + j _start_trap // Interrupt 0 is used for exceptions + j _start_SupervisorSoft_trap + j _start_trap // Interrupt 2 is reserved + j _start_MachineSoft_trap + j _start_trap // Interrupt 4 is reserved + j _start_SupervisorTimer_trap + j _start_trap // Interrupt 6 is reserved + j _start_MachineTimer_trap + j _start_trap // Interrupt 8 is reserved + j _start_SupervisorExternal_trap + j _start_trap // Interrupt 10 is reserved + j _start_MachineExternal_trap + + // default table does not include the remaining interrupts. + // Targets with extra interrupts should override this table. + + .option pop", +); + #[rustfmt::skip] global_asm!( ".section .text.abort diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 832a9fb3..72272168 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -429,6 +429,12 @@ use riscv::register::mcause as xcause; pub use riscv_rt_macros::{entry, pre_init}; +#[cfg(riscv32)] +pub use riscv_rt_macros::interrupt_riscv32 as interrupt; + +#[cfg(riscv64)] +pub use riscv_rt_macros::interrupt_riscv64 as interrupt; + /// We export this static with an informative name so that if an application attempts to link /// two copies of riscv-rt together, linking will fail. We also declare a links key in /// Cargo.toml which is the more modern way to solve the same problem, but we have to keep From 245179f0838972c7d3f990141530de3b24c83616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Thu, 11 Apr 2024 11:44:51 +0200 Subject: [PATCH 2/7] fix interrupt macro --- riscv-rt/macros/src/lib.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index c76f7d71..8374cabe 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -331,6 +331,16 @@ pub fn interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream { fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenStream { let f = parse_macro_input!(input as ItemFn); + // check the function arguments + if !f.sig.inputs.is_empty() { + return parse::Error::new( + f.sig.inputs.first().unwrap().span(), + "`#[interrupt]` function should not have arguments", + ) + .to_compile_error() + .into(); + } + // check the function signature let valid_signature = f.sig.constness.is_none() && f.sig.asyncness.is_none() @@ -362,6 +372,7 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt // XXX should we blacklist other attributes? let attrs = f.attrs; let ident = f.sig.ident; + let export_name = format!("{:#}", ident); let block = f.block; #[cfg(not(feature = "v-trap"))] @@ -371,7 +382,7 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt quote!( #start_trap - #[export_name = #ident] + #[export_name = #export_name] #(#attrs)* pub unsafe fn #ident() #block ) @@ -404,7 +415,7 @@ mod v_trap { "a7", ]; - pub fn start_interrupt_trap_asm( + pub(crate) fn start_interrupt_trap_asm( ident: &syn::Ident, arch: RiscvArch, ) -> proc_macro2::TokenStream { From 52d51855fe3b348f2f668961e029656a7c15ab7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Fri, 12 Apr 2024 11:43:08 +0200 Subject: [PATCH 3/7] add global label to traps --- riscv-rt/macros/src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 8374cabe..2eff2e82 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -370,21 +370,18 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt } // XXX should we blacklist other attributes? - let attrs = f.attrs; - let ident = f.sig.ident; + let ident = &f.sig.ident; let export_name = format!("{:#}", ident); - let block = f.block; #[cfg(not(feature = "v-trap"))] let start_trap = proc_macro2::TokenStream::new(); #[cfg(feature = "v-trap")] - let start_trap = v_trap::start_interrupt_trap_asm(&ident, _arch); + let start_trap = v_trap::start_interrupt_trap_asm(ident, _arch); quote!( #start_trap #[export_name = #export_name] - #(#attrs)* - pub unsafe fn #ident() #block + #f ) .into() } @@ -443,6 +440,7 @@ mod v_trap { core::arch::global_asm!( \".section .trap, \\\"ax\\\" .align {width} + .global _start_{function}_trap _start_{function}_trap: addi sp, sp, - {TRAP_SIZE} * {width} {store} From e3aa1c231e0e713aabdf358f868e1e7ef9a1d64f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Fri, 12 Apr 2024 18:55:36 +0200 Subject: [PATCH 4/7] use _continue_interrupt_flag to reduce binary size --- riscv-rt/macros/src/lib.rs | 196 ++++++++++++++++++++++++++++--------- riscv-rt/src/asm.rs | 61 +----------- 2 files changed, 154 insertions(+), 103 deletions(-) diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 2eff2e82..f48f7870 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -313,11 +313,122 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream { res.parse().unwrap() } +#[derive(Clone, Copy)] enum RiscvArch { Rv32, Rv64, } +const TRAP_SIZE: usize = 16; + +#[rustfmt::skip] +const TRAP_FRAME: [&str; TRAP_SIZE] = [ + "ra", + "t0", + "t1", + "t2", + "t3", + "t4", + "t5", + "t6", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", +]; + +fn store_trap bool>(arch: RiscvArch, mut filter: T) -> String { + let (width, store) = match arch { + RiscvArch::Rv32 => (4, "sw"), + RiscvArch::Rv64 => (8, "sd"), + }; + let mut stores = Vec::new(); + for (i, reg) in TRAP_FRAME + .iter() + .enumerate() + .filter(|(_, ®)| filter(reg)) + { + stores.push(format!("{store} {reg}, {i}*{width}(sp)")); + } + stores.join("\n") +} + +fn load_trap(arch: RiscvArch) -> String { + let (width, load) = match arch { + RiscvArch::Rv32 => (4, "lw"), + RiscvArch::Rv64 => (8, "ld"), + }; + let mut loads = Vec::new(); + for (i, reg) in TRAP_FRAME.iter().enumerate() { + loads.push(format!("{load} {reg}, {i}*{width}(sp)")); + } + loads.join("\n") +} + +#[proc_macro] +pub fn weak_start_trap_riscv32(_input: TokenStream) -> TokenStream { + weak_start_trap(RiscvArch::Rv32) +} + +#[proc_macro] +pub fn weak_start_trap_riscv64(_input: TokenStream) -> TokenStream { + weak_start_trap(RiscvArch::Rv64) +} + +fn weak_start_trap(arch: RiscvArch) -> TokenStream { + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, + }; + // ensure we do not break that sp is 16-byte aligned + if (TRAP_SIZE * width) % 16 != 0 { + return parse::Error::new(Span::call_site(), "Trap frame size must be 16-byte aligned") + .to_compile_error() + .into(); + } + let store = store_trap(arch, |_| true); + let load = load_trap(arch); + + #[cfg(feature = "s-mode")] + let ret = "sret"; + #[cfg(not(feature = "s-mode"))] + let ret = "mret"; + + let instructions: proc_macro2::TokenStream = format!( + " +core::arch::global_asm!( +\".section .trap, \\\"ax\\\" +.align {width} +.weak _start_trap +_start_trap: + addi sp, sp, - {TRAP_SIZE} * {width} + {store} + add a0, sp, zero + jal ra, _start_trap_rust + {load} + addi sp, sp, {TRAP_SIZE} * {width} + {ret} +\");" + ) + .parse() + .unwrap(); + + #[cfg(feature = "v-trap")] + let v_trap = v_trap::continue_interrupt_trap(arch); + #[cfg(not(feature = "v-trap"))] + let v_trap = proc_macro2::TokenStream::new(); + + quote!( + #instructions + #v_trap + ) + .into() +} + #[proc_macro_attribute] pub fn interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream { interrupt(args, input, RiscvArch::Rv32) @@ -376,7 +487,7 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt #[cfg(not(feature = "v-trap"))] let start_trap = proc_macro2::TokenStream::new(); #[cfg(feature = "v-trap")] - let start_trap = v_trap::start_interrupt_trap_asm(ident, _arch); + let start_trap = v_trap::start_interrupt_trap(ident, _arch); quote!( #start_trap @@ -390,45 +501,41 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt mod v_trap { use super::*; - const TRAP_SIZE: usize = 16; - - #[rustfmt::skip] - const TRAP_FRAME: [&str; TRAP_SIZE] = [ - "ra", - "t0", - "t1", - "t2", - "t3", - "t4", - "t5", - "t6", - "a0", - "a1", - "a2", - "a3", - "a4", - "a5", - "a6", - "a7", - ]; - - pub(crate) fn start_interrupt_trap_asm( + pub(crate) fn start_interrupt_trap( ident: &syn::Ident, arch: RiscvArch, ) -> proc_macro2::TokenStream { - let function = ident.to_string(); - let (width, store, load) = match arch { - RiscvArch::Rv32 => (4, "sw", "lw"), - RiscvArch::Rv64 => (8, "sd", "ld"), + let interrupt = ident.to_string(); + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, }; + let store = store_trap(arch, |r| r == "a0"); - let (mut stores, mut loads) = (Vec::new(), Vec::new()); - for (i, r) in TRAP_FRAME.iter().enumerate() { - stores.push(format!(" {store} {r}, {i}*{width}(sp)")); - loads.push(format!(" {load} {r}, {i}*{width}(sp)")); - } - let store = stores.join("\n"); - let load = loads.join("\n"); + let instructions = format!( + " +core::arch::global_asm!( + \".section .trap, \\\"ax\\\" + .align {width} + .global _start_{interrupt}_trap + _start_{interrupt}_trap: + addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame + {store} // store trap partially (only register a0) + la a0, {interrupt} // load interrupt handler address into a0 + j _continue_interrupt_trap // jump to common part of interrupt trap +\");" + ); + + instructions.parse().unwrap() + } + + pub(crate) fn continue_interrupt_trap(arch: RiscvArch) -> proc_macro2::TokenStream { + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, + }; + let store = store_trap(arch, |reg| reg != "a0"); + let load = load_trap(arch); #[cfg(feature = "s-mode")] let ret = "sret"; @@ -439,16 +546,15 @@ mod v_trap { " core::arch::global_asm!( \".section .trap, \\\"ax\\\" - .align {width} - .global _start_{function}_trap - _start_{function}_trap: - addi sp, sp, - {TRAP_SIZE} * {width} -{store} - call {function} -{load} - addi sp, sp, {TRAP_SIZE} * {width} - {ret}\" -);" + .align {width} // TODO is this necessary? + .global _continue_interrupt_trap + _continue_interrupt_trap: + {store} // store trap partially (all registers except a0) + jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0) + {load} // restore trap frame + addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame + {ret} // return from interrupt +\");" ); instructions.parse().unwrap() diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index d8f98e51..384b2b03 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -277,65 +277,10 @@ _pre_init_trap: j _pre_init_trap", ); -/// Trap entry point (_start_trap). It saves caller saved registers, calls -/// _start_trap_rust, restores caller saved registers and then returns. -/// -/// # Usage -/// -/// The macro takes 5 arguments: -/// - `$STORE`: the instruction used to store a register in the stack (e.g. `sd` for riscv64) -/// - `$LOAD`: the instruction used to load a register from the stack (e.g. `ld` for riscv64) -/// - `$BYTES`: the number of bytes used to store a register (e.g. 8 for riscv64) -/// - `$TRAP_SIZE`: the number of registers to store in the stack (e.g. 32 for all the user registers) -/// - list of tuples of the form `($REG, $LOCATION)`, where: -/// - `$REG`: the register to store/load -/// - `$LOCATION`: the location in the stack where to store/load the register -#[rustfmt::skip] -macro_rules! trap_handler { - ($STORE:ident, $LOAD:ident, $BYTES:literal, $TRAP_SIZE:literal, [$(($REG:ident, $LOCATION:literal)),*]) => { - // ensure we do not break that sp is 16-byte aligned - const _: () = assert!(($TRAP_SIZE * $BYTES) % 16 == 0); - global_asm!( - " - .section .trap, \"ax\" - .weak _start_trap - _start_trap:", - // save space for trap handler in stack - concat!("addi sp, sp, -", stringify!($TRAP_SIZE * $BYTES)), - // save registers in the desired order - $(concat!(stringify!($STORE), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)* - // call rust trap handler - "add a0, sp, zero - jal ra, _start_trap_rust", - // restore registers in the desired order - $(concat!(stringify!($LOAD), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)* - // free stack - concat!("addi sp, sp, ", stringify!($TRAP_SIZE * $BYTES)), - ); - cfg_global_asm!( - // return from trap - #[cfg(feature = "s-mode")] - "sret", - #[cfg(not(feature = "s-mode"))] - "mret", - ); - }; -} - -#[rustfmt::skip] #[cfg(riscv32)] -trap_handler!( - sw, lw, 4, 16, - [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7), - (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)] -); -#[rustfmt::skip] +riscv_rt_macros::weak_start_trap_riscv32!(); #[cfg(riscv64)] -trap_handler!( - sd, ld, 8, 16, - [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7), - (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)] -); +riscv_rt_macros::weak_start_trap_riscv64!(); #[cfg(feature = "v-trap")] cfg_global_asm!( @@ -345,7 +290,7 @@ cfg_global_asm!( .type _vector_table, @function .option push - .balign 0x100 // TODO check if this is the correct alignment + .balign 0x4 // TODO check if this is the correct alignment .option norelax .option norvc From 7bdb6e96fbdaf9099ed194997117305f6d2e151a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Sat, 4 May 2024 17:55:31 +0200 Subject: [PATCH 5/7] _dispatch_interrupt function and docs --- .github/workflows/riscv-rt.yaml | 2 +- riscv-rt/CHANGELOG.md | 2 + riscv-rt/link.x.in | 19 ++-- riscv-rt/macros/src/lib.rs | 163 ++++++++++++++++++++------------ riscv-rt/src/asm.rs | 15 ++- riscv-rt/src/lib.rs | 68 +++++++++++-- 6 files changed, 185 insertions(+), 84 deletions(-) diff --git a/.github/workflows/riscv-rt.yaml b/.github/workflows/riscv-rt.yaml index 5cfd35bc..10a27b3b 100644 --- a/.github/workflows/riscv-rt.yaml +++ b/.github/workflows/riscv-rt.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, vectored-rt ] + branches: [ master ] pull_request: merge_group: diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 7348a237..073b4505 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -19,6 +19,8 @@ If `v-trap` feature is enabled, this macro also generates its corresponding trap - Moved all the assembly code to `asm.rs` - Use `weak` symbols for functions such as `_mp_hook` or `_start_trap` - `abort` is now `weak`, so it is possible to link third-party libraries including this symbol. +- `_start_trap_rust` now only deals with exceptions. When an interrupt is detected, it now calls +to `_dispatch_interrupt`. ### Removed diff --git a/riscv-rt/link.x.in b/riscv-rt/link.x.in index 462ddd58..fc73f8ac 100644 --- a/riscv-rt/link.x.in +++ b/riscv-rt/link.x.in @@ -35,15 +35,20 @@ PROVIDE(_heap_size = 0); and then returns. Users can override this alias by defining the symbol themselves */ EXTERN(_start_trap); +/* Default interrupt trap entry point. When vectored trap mode is enabled, + the riscv-rt crate provides an implementation of this function, which saves caller saved + registers, calls the the DefaultHandler ISR, restores caller saved registers and returns. */ +PROVIDE(_start_DefaultHandler_trap = _start_trap); + /* When vectored trap mode is enabled, each interrupt source must implement its own trap entry point. By default, all interrupts start in _start_trap. However, users can override these alias by defining the symbol themselves */ -PROVIDE(_start_SupervisorSoft_trap = _start_trap); -PROVIDE(_start_MachineSoft_trap = _start_trap); -PROVIDE(_start_SupervisorTimer_trap = _start_trap); -PROVIDE(_start_MachineTimer_trap = _start_trap); -PROVIDE(_start_SupervisorExternal_trap = _start_trap); -PROVIDE(_start_MachineExternal_trap = _start_trap); +PROVIDE(_start_SupervisorSoft_trap = _start_DefaultHandler_trap); +PROVIDE(_start_MachineSoft_trap = _start_DefaultHandler_trap); +PROVIDE(_start_SupervisorTimer_trap = _start_DefaultHandler_trap); +PROVIDE(_start_MachineTimer_trap = _start_DefaultHandler_trap); +PROVIDE(_start_SupervisorExternal_trap = _start_DefaultHandler_trap); +PROVIDE(_start_MachineExternal_trap = _start_DefaultHandler_trap); /** EXCEPTION HANDLERS **/ @@ -61,7 +66,7 @@ PROVIDE(Breakpoint = ExceptionHandler); PROVIDE(LoadMisaligned = ExceptionHandler); PROVIDE(LoadFault = ExceptionHandler); PROVIDE(StoreMisaligned = ExceptionHandler); -PROVIDE(StoreFault = ExceptionHandler);; +PROVIDE(StoreFault = ExceptionHandler); PROVIDE(UserEnvCall = ExceptionHandler); PROVIDE(SupervisorEnvCall = ExceptionHandler); PROVIDE(MachineEnvCall = ExceptionHandler); diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index f48f7870..1e1f32a5 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -319,9 +319,11 @@ enum RiscvArch { Rv64, } +/// Size of the trap frame (in number of registers) const TRAP_SIZE: usize = 16; #[rustfmt::skip] +/// List of the register names to be stored in the trap frame const TRAP_FRAME: [&str; TRAP_SIZE] = [ "ra", "t0", @@ -341,6 +343,14 @@ const TRAP_FRAME: [&str; TRAP_SIZE] = [ "a7", ]; +/// Generate the assembly instructions to store the trap frame. +/// +/// The `arch` parameter is used to determine the width of the registers. +/// +/// The `filter` function is used to filter which registers to store. +/// This is useful to optimize the binary size in vectored interrupt mode, which divides the trap +/// frame storage in two parts: the first part saves space in the stack and stores only the `a0` register, +/// while the second part stores the remaining registers. fn store_trap bool>(arch: RiscvArch, mut filter: T) -> String { let (width, store) = match arch { RiscvArch::Rv32 => (4, "sw"), @@ -357,6 +367,8 @@ fn store_trap bool>(arch: RiscvArch, mut filter: T) -> String stores.join("\n") } +/// Generate the assembly instructions to load the trap frame. +/// The `arch` parameter is used to determine the width of the registers. fn load_trap(arch: RiscvArch) -> String { let (width, load) = match arch { RiscvArch::Rv32 => (4, "lw"), @@ -369,16 +381,31 @@ fn load_trap(arch: RiscvArch) -> String { loads.join("\n") } +/// Generates weak `_start_trap` function in assembly for RISCV-32 targets. +/// +/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`. +/// The trap frame is allocated on the stack and deallocated after the call. #[proc_macro] pub fn weak_start_trap_riscv32(_input: TokenStream) -> TokenStream { weak_start_trap(RiscvArch::Rv32) } +/// Generates weak `_start_trap` function in assembly for RISCV-64 targets. +/// +/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`. +/// The trap frame is allocated on the stack and deallocated after the call. #[proc_macro] pub fn weak_start_trap_riscv64(_input: TokenStream) -> TokenStream { weak_start_trap(RiscvArch::Rv64) } +/// Generates weak `_start_trap` function in assembly. +/// +/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`. +/// The trap frame is allocated on the stack and deallocated after the call. +/// +/// The `arch` parameter is used to determine the width of the registers. +/// The macro also ensures that the trap frame size is 16-byte aligned. fn weak_start_trap(arch: RiscvArch) -> TokenStream { let width = match arch { RiscvArch::Rv32 => 4, @@ -398,7 +425,7 @@ fn weak_start_trap(arch: RiscvArch) -> TokenStream { #[cfg(not(feature = "s-mode"))] let ret = "mret"; - let instructions: proc_macro2::TokenStream = format!( + format!( " core::arch::global_asm!( \".section .trap, \\\"ax\\\" @@ -415,26 +442,76 @@ _start_trap: \");" ) .parse() - .unwrap(); + .unwrap() +} - #[cfg(feature = "v-trap")] - let v_trap = v_trap::continue_interrupt_trap(arch); - #[cfg(not(feature = "v-trap"))] - let v_trap = proc_macro2::TokenStream::new(); +/// Generates vectored interrupt trap functions in assembly for RISCV-32 targets. +#[cfg(feature = "v-trap")] +#[proc_macro] +pub fn vectored_interrupt_trap_riscv32(_input: TokenStream) -> TokenStream { + vectored_interrupt_trap(RiscvArch::Rv32) +} - quote!( - #instructions - #v_trap - ) - .into() +/// Generates vectored interrupt trap functions in assembly for RISCV-64 targets. +#[cfg(feature = "v-trap")] +#[proc_macro] +pub fn vectored_interrupt_trap_riscv64(_input: TokenStream) -> TokenStream { + vectored_interrupt_trap(RiscvArch::Rv64) +} + +#[cfg(feature = "v-trap")] +/// Generates global '_start_DefaultHandler_trap' and '_continue_interrupt_trap' functions in assembly. +/// The '_start_DefaultHandler_trap' function stores the trap frame partially (only register a0) and +/// jumps to the interrupt handler. The '_continue_interrupt_trap' function stores the trap frame +/// partially (all registers except a0), jumps to the interrupt handler, and restores the trap frame. +fn vectored_interrupt_trap(arch: RiscvArch) -> TokenStream { + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, + }; + let store_start = store_trap(arch, |reg| reg == "a0"); + let store_continue = store_trap(arch, |reg| reg != "a0"); + let load = load_trap(arch); + + #[cfg(feature = "s-mode")] + let ret = "sret"; + #[cfg(not(feature = "s-mode"))] + let ret = "mret"; + + let instructions = format!( + " +core::arch::global_asm!( +\".section .trap, \\\"ax\\\" + +.global _start_DefaultHandler_trap +_start_DefaultHandler_trap: + addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame + {store_start} // store trap partially (only register a0) + la a0, DefaultHandler // load interrupt handler address into a0 + +.global _continue_interrupt_trap +_continue_interrupt_trap: + {store_continue} // store trap partially (all registers except a0) + jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0) + {load} // restore trap frame + addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame + {ret} // return from interrupt +\");" + ); + + instructions.parse().unwrap() } #[proc_macro_attribute] +/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. +/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. pub fn interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream { interrupt(args, input, RiscvArch::Rv32) } #[proc_macro_attribute] +/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. +/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. pub fn interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream { interrupt(args, input, RiscvArch::Rv64) } @@ -487,7 +564,7 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt #[cfg(not(feature = "v-trap"))] let start_trap = proc_macro2::TokenStream::new(); #[cfg(feature = "v-trap")] - let start_trap = v_trap::start_interrupt_trap(ident, _arch); + let start_trap = start_interrupt_trap(ident, _arch); quote!( #start_trap @@ -498,25 +575,19 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt } #[cfg(feature = "v-trap")] -mod v_trap { - use super::*; - - pub(crate) fn start_interrupt_trap( - ident: &syn::Ident, - arch: RiscvArch, - ) -> proc_macro2::TokenStream { - let interrupt = ident.to_string(); - let width = match arch { - RiscvArch::Rv32 => 4, - RiscvArch::Rv64 => 8, - }; - let store = store_trap(arch, |r| r == "a0"); +fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::TokenStream { + let interrupt = ident.to_string(); + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, + }; + let store = store_trap(arch, |r| r == "a0"); - let instructions = format!( - " + let instructions = format!( + " core::arch::global_asm!( \".section .trap, \\\"ax\\\" - .align {width} + .align 2 .global _start_{interrupt}_trap _start_{interrupt}_trap: addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame @@ -524,39 +595,7 @@ core::arch::global_asm!( la a0, {interrupt} // load interrupt handler address into a0 j _continue_interrupt_trap // jump to common part of interrupt trap \");" - ); - - instructions.parse().unwrap() - } - - pub(crate) fn continue_interrupt_trap(arch: RiscvArch) -> proc_macro2::TokenStream { - let width = match arch { - RiscvArch::Rv32 => 4, - RiscvArch::Rv64 => 8, - }; - let store = store_trap(arch, |reg| reg != "a0"); - let load = load_trap(arch); + ); - #[cfg(feature = "s-mode")] - let ret = "sret"; - #[cfg(not(feature = "s-mode"))] - let ret = "mret"; - - let instructions = format!( - " -core::arch::global_asm!( - \".section .trap, \\\"ax\\\" - .align {width} // TODO is this necessary? - .global _continue_interrupt_trap - _continue_interrupt_trap: - {store} // store trap partially (all registers except a0) - jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0) - {load} // restore trap frame - addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame - {ret} // return from interrupt -\");" - ); - - instructions.parse().unwrap() - } + instructions.parse().unwrap() } diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index 384b2b03..247400f4 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -282,6 +282,11 @@ riscv_rt_macros::weak_start_trap_riscv32!(); #[cfg(riscv64)] riscv_rt_macros::weak_start_trap_riscv64!(); +#[cfg(all(riscv32, feature = "v-trap"))] +riscv_rt_macros::vectored_interrupt_trap_riscv32!(); +#[cfg(all(riscv64, feature = "v-trap"))] +riscv_rt_macros::vectored_interrupt_trap_riscv64!(); + #[cfg(feature = "v-trap")] cfg_global_asm!( // Set the vector mode to vectored. @@ -297,15 +302,15 @@ cfg_global_asm!( _vector_table: j _start_trap // Interrupt 0 is used for exceptions j _start_SupervisorSoft_trap - j _start_trap // Interrupt 2 is reserved + j _start_DefaultHandler_trap // Interrupt 2 is reserved j _start_MachineSoft_trap - j _start_trap // Interrupt 4 is reserved + j _start_DefaultHandler_trap // Interrupt 4 is reserved j _start_SupervisorTimer_trap - j _start_trap // Interrupt 6 is reserved + j _start_DefaultHandler_trap // Interrupt 6 is reserved j _start_MachineTimer_trap - j _start_trap // Interrupt 8 is reserved + j _start_DefaultHandler_trap // Interrupt 8 is reserved j _start_SupervisorExternal_trap - j _start_trap // Interrupt 10 is reserved + j _start_DefaultHandler_trap // Interrupt 10 is reserved j _start_MachineExternal_trap // default table does not include the remaining interrupts. diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 72272168..7d6a6d59 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -354,6 +354,18 @@ //! } //! ``` //! +//! You can also use the `#[interrupt]` macro to define interrupt handlers: +//! +//! ``` no_run +//! #[riscv_rt::interrupt] +//! fn MachineTimer() { +//! // ... +//! } +//! ``` +//! +//! In direct mode, this macro is equivalent to defining a function with the same name. +//! However, in vectored mode, this macro will generate a proper trap handler for the interrupt. +//! //! If interrupt handler is not explicitly defined, `DefaultHandler` is called. //! //! ### `DefaultHandler` @@ -413,6 +425,33 @@ //! FLASH : ORIGIN = 0x20000000, LENGTH = 16M //! } //! ``` +//! +//! ## `v-trap` +//! +//! The vectored trap feature (`v-trap`) can be activated via [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html). +//! +//! For example: +//! ``` text +//! [dependencies] +//! riscv-rt = {features=["v-trap"]} +//! ``` +//! When the vectored trap feature is enabled, the trap vector is set to `_vector_table` in vectored mode. +//! This table is a list of `j _start_INTERRUPT_trap` instructions, where `INTERRUPT` is the name of the interrupt. +//! +//! ### Defining interrupt handlers in vectored mode +//! +//! In vectored mode, each interrupt must also have a corresponding trap handler. +//! Therefore, using `export_name` or `no_mangle` is not enough to define an interrupt handler. +//! The [`interrupt`] macro will generate the trap handler for the interrupt: +//! +//! ``` no_run +//! #[riscv_rt::interrupt] +//! fn MachineTimer() { +//! // ... +//! } +//! ``` +//! +//! This will generate a function named `_start_MachineTimer_trap` that calls the interrupt handler `MachineTimer`. // NOTE: Adapted from cortex-m/src/lib.rs #![no_std] @@ -481,7 +520,7 @@ pub struct TrapFrame { pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { extern "C" { fn ExceptionHandler(trap_frame: &TrapFrame); - fn DefaultHandler(); + fn _dispatch_interrupt(code: usize); } let cause = xcause::read(); @@ -499,15 +538,8 @@ pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { } else { ExceptionHandler(trap_frame); } - } else if code < __INTERRUPTS.len() { - let h = &__INTERRUPTS[code]; - if let Some(handler) = h { - handler(); - } else { - DefaultHandler(); - } } else { - DefaultHandler(); + _dispatch_interrupt(code); } } @@ -549,6 +581,24 @@ pub static __EXCEPTIONS: [Option; 16] = [ Some(StorePageFault), ]; +#[export_name = "_dispatch_interrupt"] +unsafe extern "C" fn dispatch_interrupt(code: usize) { + extern "C" { + fn DefaultHandler(); + } + + if code < __INTERRUPTS.len() { + let h = &__INTERRUPTS[code]; + if let Some(handler) = h { + handler(); + } else { + DefaultHandler(); + } + } else { + DefaultHandler(); + } +} + extern "C" { fn SupervisorSoft(); fn MachineSoft(); From 537c3e7f97469a1643a0fa7572634135a2f25bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Thu, 9 May 2024 11:46:32 +0200 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: rmsyn <117854522+rmsyn@users.noreply.github.com> --- riscv-rt/macros/src/lib.rs | 18 +++++++++--------- riscv-rt/src/asm.rs | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 1e1f32a5..73fee64c 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -426,9 +426,9 @@ fn weak_start_trap(arch: RiscvArch) -> TokenStream { let ret = "mret"; format!( - " + r#" core::arch::global_asm!( -\".section .trap, \\\"ax\\\" +".section .trap, \\"ax\\" .align {width} .weak _start_trap _start_trap: @@ -439,7 +439,7 @@ _start_trap: {load} addi sp, sp, {TRAP_SIZE} * {width} {ret} -\");" +");"# ) .parse() .unwrap() @@ -479,9 +479,9 @@ fn vectored_interrupt_trap(arch: RiscvArch) -> TokenStream { let ret = "mret"; let instructions = format!( - " + r#" core::arch::global_asm!( -\".section .trap, \\\"ax\\\" +".section .trap, \\"ax\\" .global _start_DefaultHandler_trap _start_DefaultHandler_trap: @@ -496,7 +496,7 @@ _continue_interrupt_trap: {load} // restore trap frame addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame {ret} // return from interrupt -\");" +");"# ); instructions.parse().unwrap() @@ -584,9 +584,9 @@ fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::Tok let store = store_trap(arch, |r| r == "a0"); let instructions = format!( - " + r#" core::arch::global_asm!( - \".section .trap, \\\"ax\\\" + ".section .trap, \\"ax\\" .align 2 .global _start_{interrupt}_trap _start_{interrupt}_trap: @@ -594,7 +594,7 @@ core::arch::global_asm!( {store} // store trap partially (only register a0) la a0, {interrupt} // load interrupt handler address into a0 j _continue_interrupt_trap // jump to common part of interrupt trap -\");" +");"# ); instructions.parse().unwrap() diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index 247400f4..7883c446 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -290,7 +290,7 @@ riscv_rt_macros::vectored_interrupt_trap_riscv64!(); #[cfg(feature = "v-trap")] cfg_global_asm!( // Set the vector mode to vectored. - ".section .trap, \"ax\" + r#".section .trap, "ax" .weak _vector_table .type _vector_table, @function @@ -316,7 +316,7 @@ cfg_global_asm!( // default table does not include the remaining interrupts. // Targets with extra interrupts should override this table. - .option pop", + .option pop"#, ); #[rustfmt::skip] From 5f0b9c6c99d7546984727d8fc4355a1c88409a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Thu, 9 May 2024 13:56:53 +0200 Subject: [PATCH 7/7] new store/load functions --- riscv-rt/macros/src/lib.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 73fee64c..007016b8 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -356,15 +356,14 @@ fn store_trap bool>(arch: RiscvArch, mut filter: T) -> String RiscvArch::Rv32 => (4, "sw"), RiscvArch::Rv64 => (8, "sd"), }; - let mut stores = Vec::new(); - for (i, reg) in TRAP_FRAME + + TRAP_FRAME .iter() .enumerate() .filter(|(_, ®)| filter(reg)) - { - stores.push(format!("{store} {reg}, {i}*{width}(sp)")); - } - stores.join("\n") + .map(|(i, reg)| format!("{store} {reg}, {i}*{width}(sp)")) + .collect::>() + .join("\n") } /// Generate the assembly instructions to load the trap frame. @@ -374,11 +373,12 @@ fn load_trap(arch: RiscvArch) -> String { RiscvArch::Rv32 => (4, "lw"), RiscvArch::Rv64 => (8, "ld"), }; - let mut loads = Vec::new(); - for (i, reg) in TRAP_FRAME.iter().enumerate() { - loads.push(format!("{load} {reg}, {i}*{width}(sp)")); - } - loads.join("\n") + TRAP_FRAME + .iter() + .enumerate() + .map(|(i, reg)| format!("{load} {reg}, {i}*{width}(sp)")) + .collect::>() + .join("\n") } /// Generates weak `_start_trap` function in assembly for RISCV-32 targets. @@ -428,7 +428,7 @@ fn weak_start_trap(arch: RiscvArch) -> TokenStream { format!( r#" core::arch::global_asm!( -".section .trap, \\"ax\\" +".section .trap, \"ax\" .align {width} .weak _start_trap _start_trap: @@ -481,7 +481,7 @@ fn vectored_interrupt_trap(arch: RiscvArch) -> TokenStream { let instructions = format!( r#" core::arch::global_asm!( -".section .trap, \\"ax\\" +".section .trap, \"ax\" .global _start_DefaultHandler_trap _start_DefaultHandler_trap: @@ -586,7 +586,7 @@ fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::Tok let instructions = format!( r#" core::arch::global_asm!( - ".section .trap, \\"ax\\" + ".section .trap, \"ax\" .align 2 .global _start_{interrupt}_trap _start_{interrupt}_trap: