diff --git a/.github/workflows/riscv-rt.yaml b/.github/workflows/riscv-rt.yaml index 8e716b52..0c20eb31 100644 --- a/.github/workflows/riscv-rt.yaml +++ b/.github/workflows/riscv-rt.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master ] + branches: [ master, riscv-rt-asm ] pull_request: merge_group: diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index ecbb8a5d..b206df32 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- Removed _start_rust. Now, assembly directly jumps to main - Removed U-mode interrupts to align with latest RISC-V specification - Changed `Vector` union. Now, it uses `Option`, which is more idiomatic in Rust - Removed riscv-target dependency for build diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index b48cc111..14021902 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -212,7 +212,8 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { struct AsmLoopArgs { asm_template: String, - count: usize, + count_from: usize, + count_to: usize, } impl Parse for AsmLoopArgs { @@ -220,24 +221,35 @@ impl Parse for AsmLoopArgs { let template: LitStr = input.parse().unwrap(); _ = input.parse::().unwrap(); let count: LitInt = input.parse().unwrap(); - - Ok(Self { - asm_template: template.value(), - count: count.base10_parse().unwrap(), - }) + if input.parse::().is_ok() { + let count_to: LitInt = input.parse().unwrap(); + Ok(Self { + asm_template: template.value(), + count_from: count.base10_parse().unwrap(), + count_to: count_to.base10_parse().unwrap(), + }) + } else { + Ok(Self { + asm_template: template.value(), + count_from: 0, + count_to: count.base10_parse().unwrap(), + }) + } } } /// Loops an asm expression n times. /// -/// `loop_asm!` takes 2 arguments, the first is a string literal and the second is a number literal -/// See [the formatting syntax documentation in `std::fmt`](../std/fmt/index.html) -/// for details. +/// `loop_asm!` takes 2 or 3 arguments, the first is a string literal and the rest are a number literal +/// See [the formatting syntax documentation in `std::fmt`](../std/fmt/index.html) for details. /// /// Argument 1 is an assembly expression, all "{}" in this assembly expression will be replaced with the /// current loop index. /// -/// Argument 2 is the number of loops to do with the provided expression. +/// If 2 arguments are provided, the loop will start at 0 and end at the number provided in argument 2. +/// +/// If 3 arguments are provided, the loop will start at the number provided in argument 2 and end at +/// the number provided in argument 3. /// /// # Examples /// @@ -245,13 +257,14 @@ impl Parse for AsmLoopArgs { /// # use riscv_rt_macros::loop_asm; /// unsafe { /// loop_asm!("fmv.w.x f{}, x0", 32); // => core::arch::asm!("fmv.w.x f0, x0") ... core::arch::asm!("fmv.w.x f31, x0") +/// loop_asm!("fmv.w.x f{}, x0", 1, 32); // => core::arch::asm!("fmv.w.x f1, x0") ... core::arch::asm!("fmv.w.x f31, x0") /// } /// ``` #[proc_macro] pub fn loop_asm(input: TokenStream) -> TokenStream { let args = parse_macro_input!(input as AsmLoopArgs); - let tokens = (0..args.count) + let tokens = (args.count_from..args.count_to) .map(|i| { let i = i.to_string(); let asm = args.asm_template.replace("{}", &i); @@ -261,3 +274,41 @@ pub fn loop_asm(input: TokenStream) -> TokenStream { .join("\n"); tokens.parse().unwrap() } + +/// Loops a global_asm expression n times. +/// +/// `loop_global_asm!` takes 2 or 3 arguments, the first is a string literal and the rest are a number literal +/// See [the formatting syntax documentation in `std::fmt`](../std/fmt/index.html) for details. +/// +/// Argument 1 is an assembly expression, all "{}" in this assembly expression will be replaced with the +/// current loop index. +/// +/// If 2 arguments are provided, the loop will start at 0 and end at the number provided in argument 2. +/// +/// If 3 arguments are provided, the loop will start at the number provided in argument 2 and end at +/// the number provided in argument 3. +/// +/// # Examples +/// +/// ``` +/// # use riscv_rt_macros::loop_global_asm; +/// unsafe { +/// loop_global_asm!("fmv.w.x f{}, x0", 32); // => core::arch::global_asm!("fmv.w.x f0, x0") ... core::arch::global_asm!("fmv.w.x f31, x0") +/// loop_global_asm!("fmv.w.x f{}, x0", 1, 32); // => core::arch::global_asm!("fmv.w.x f1, x0") ... core::arch::global_asm!("fmv.w.x f31, x0") +/// } +/// ``` +#[proc_macro] +pub fn loop_global_asm(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as AsmLoopArgs); + + let instructions = (args.count_from..args.count_to) + .map(|i| { + let i = i.to_string(); + args.asm_template.replace("{}", &i) + }) + .collect::>() + .join("\n"); + + let res = format!("core::arch::global_asm!(\n\"{}\"\n);", instructions); + res.parse().unwrap() +} diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index 0eedebe6..b83d304d 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -52,54 +52,36 @@ _abs_start: #[cfg(not(feature = "s-mode"))] "csrw mie, 0 csrw mip, 0", - "li x1, 0 - li x2, 0 - li x3, 0 - li x4, 0 - li x5, 0 - li x6, 0 - li x7, 0 - li x8, 0 - li x9, 0 - // a0..a2 (x10..x12) skipped - li x13, 0 - li x14, 0 - li x15, 0 - li x16, 0 - li x17, 0 - li x18, 0 - li x19, 0 - li x20, 0 - li x21, 0 - li x22, 0 - li x23, 0 - li x24, 0 - li x25, 0 - li x26, 0 - li x27, 0 - li x28, 0 - li x29, 0 - li x30, 0 - li x31, 0 +); + +// ZERO OUT GENERAL-PURPOSE REGISTERS +riscv_rt_macros::loop_global_asm!(" li x{}, 0", 1, 10); +// a0..a2 (x10..x12) skipped +riscv_rt_macros::loop_global_asm!(" li x{}, 0", 13, 32); - .option push +// INITIALIZE GLOBAL POINTER +cfg_global_asm!( + ".option push .option norelax la gp, __global_pointer$ - .option pop - // Allocate stacks", - #[cfg(all(not(feature = "single-hart"), feature = "s-mode"))] + .option pop", +); + +// INITIALIZE STACK POINTER AND FRAME POINTER +#[cfg(not(feature = "single-hart"))] +cfg_global_asm!( + #[cfg(feature = "s-mode")] "mv t2, a0 // the hartid is passed as parameter by SMODE", - #[cfg(all(not(feature = "single-hart"), not(feature = "s-mode")))] + #[cfg(not(feature = "s-mode"))] "csrr t2, mhartid", - #[cfg(not(feature = "single-hart"))] "lui t0, %hi(_max_hart_id) add t0, t0, %lo(_max_hart_id) bgtu t2, t0, abort lui t0, %hi(_hart_stack_size) add t0, t0, %lo(_hart_stack_size)", - #[cfg(all(not(feature = "single-hart"), riscvm))] + #[cfg(riscvm)] "mul t0, t2, t0", - #[cfg(all(not(feature = "single-hart"), not(riscvm)))] + #[cfg(not(riscvm))] "beqz t2, 2f // Jump if single-hart mv t1, t2 mv t3, t0 @@ -109,14 +91,109 @@ _abs_start: bnez t1, 1b 2: ", "la t1, _stack_start", - #[cfg(not(feature = "single-hart"))] "sub t1, t1, t0", - "andi sp, t1, -16 // Force 16-byte alignment - // Set frame pointer - add s0, sp, zero +); +cfg_global_asm!( + "andi sp, t1, -16 // align stack to 16-bytes + add s0, sp, zero", +); + +// STORE A0..A2 IN THE STACK, AS THEY WILL BE NEEDED LATER BY main +cfg_global_asm!( + #[cfg(riscv32)] + "addi sp, sp, -4 * 3 + sw a0, 4 * 0(sp) + sw a1, 4 * 1(sp) + sw a2, 4 * 2(sp)", + #[cfg(riscv64)] + "addi sp, sp, -8 * 3 + sd a0, 8 * 0(sp) + sd a1, 8 * 1(sp) + sd a2, 8 * 2(sp)", +); - jal zero, _start_rust +// SKIP RAM INITIALIZATION IF CURRENT HART IS NOT THE BOOT HART +#[cfg(not(feature = "single-hart"))] +cfg_global_asm!( + #[cfg(not(feature = "s-mode"))] + "csrr a0, mhartid", + "call _mp_hook + mv t0, a0 + + beqz a0, 4f", +); +// IF CURRENT HART IS THE BOOT HART CALL __pre_init AND INITIALIZE RAM +cfg_global_asm!( + "call __pre_init + // Copy .data from flash to RAM + la t0, _sdata + la t2, _edata + la t1, _sidata + bgeu t0, t2, 2f +1: ", + #[cfg(target_arch = "riscv32")] + "lw t3, 0(t1) + addi t1, t1, 4 + sw t3, 0(t0) + addi t0, t0, 4 + bltu t0, t2, 1b", + #[cfg(target_arch = "riscv64")] + "ld t3, 0(t1) + addi t1, t1, 8 + sd t3, 0(t0) + addi t0, t0, 8 + bltu t0, t2, 1b", + " +2: // Zero out .bss + la t0, _sbss + la t2, _ebss + bgeu t0, t2, 4f +3: ", + #[cfg(target_arch = "riscv32")] + "sw zero, 0(t0) + addi t0, t0, 4 + bltu t0, t2, 3b", + #[cfg(target_arch = "riscv64")] + "sd zero, 0(t0) + addi t0, t0, 8 + bltu t0, t2, 3b", + " +4: // RAM initilized", +); +// INITIALIZE FLOATING POINT UNIT +#[cfg(any(riscvf, riscvd))] +cfg_global_asm!( + #[cfg(feature = "s-mode")] + "csrrc x0, sstatus, 0x4000 + csrrs x0, sstatus, 0x2000", + #[cfg(not(feature = "s-mode"))] + "csrrc x0, mstatus, 0x4000 + csrrs x0, mstatus, 0x2000", + "fscsr x0", +); +// ZERO OUT FLOATING POINT REGISTERS +#[cfg(all(riscv32, riscvd))] +riscv_rt_macros::loop_global_asm!(" fcvt.d.w f{}, x0", 32); +#[cfg(all(riscv64, riscvd))] +riscv_rt_macros::loop_global_asm!(" fmv.d.x f{}, x0", 32); +#[cfg(all(riscvf, not(riscvd)))] +riscv_rt_macros::loop_global_asm!(" fmv.w.x f{}, x0", 32); + +// SET UP INTERRUPTS, RESTORE a0..a2, AND JUMP TO MAIN RUST FUNCTION +cfg_global_asm!( + "call _setup_interrupts", + #[cfg(riscv32)] + "lw a0, 4 * 0(sp) + lw a1, 4 * 1(sp) + lw a2, 4 * 2(sp) + addi sp, sp, 4 * 3", + #[cfg(riscv64)] + "ld a0, 8 * 0(sp) + ld a1, 8 * 1(sp) + ld a2, 8 * 2(sp) + addi sp, sp, 8 * 3", + "jal zero, main .cfi_endproc", ); diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index a7a900eb..242eb664 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -404,179 +404,18 @@ #[cfg(riscv)] mod asm; -use core::sync::atomic::{compiler_fence, Ordering}; - #[cfg(feature = "s-mode")] use riscv::register::{scause as xcause, stvec as xtvec, stvec::TrapMode as xTrapMode}; #[cfg(not(feature = "s-mode"))] use riscv::register::{mcause as xcause, mtvec as xtvec, mtvec::TrapMode as xTrapMode}; -#[cfg(all(not(feature = "single-hart"), not(feature = "s-mode")))] -use riscv::register::mhartid; - -#[cfg(all(feature = "s-mode", any(riscvf, riscvd)))] -use riscv::register::sstatus as xstatus; - -#[cfg(all(not(feature = "s-mode"), any(riscvf, riscvd)))] -use riscv::register::mstatus as xstatus; - pub use riscv_rt_macros::{entry, pre_init}; #[export_name = "error: riscv-rt appears more than once in the dependency graph"] #[doc(hidden)] pub static __ONCE__: () = (); -/// Rust entry point (_start_rust) -/// -/// Zeros bss section, initializes data section and calls main. This function never returns. -/// -/// # Safety -/// -/// This function must be called only from assembly `_start` function. -/// Do **NOT** call this function directly. -#[link_section = ".init.rust"] -#[export_name = "_start_rust"] -pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! { - #[rustfmt::skip] - extern "Rust" { - // This symbol will be provided by the user via `#[entry]` - fn main(a0: usize, a1: usize, a2: usize) -> !; - - // This symbol will be provided by the user via `#[pre_init]` - fn __pre_init(); - - fn _setup_interrupts(); - - fn _mp_hook(hartid: usize) -> bool; - } - - #[cfg(not(feature = "single-hart"))] - let run_init = { - // sbi passes hartid as first parameter (a0) - #[cfg(feature = "s-mode")] - let hartid = a0; - #[cfg(not(feature = "s-mode"))] - let hartid = mhartid::read(); - - _mp_hook(hartid) - }; - #[cfg(feature = "single-hart")] - let run_init = true; - - if run_init { - __pre_init(); - - // Initialize RAM - // 1. Copy over .data from flash to RAM - // 2. Zero out .bss - - #[cfg(target_arch = "riscv32")] - core::arch::asm!( - " - // Copy over .data - la {start},_sdata - la {end},_edata - la {input},_sidata - - bgeu {start},{end},2f - 1: - lw {a},0({input}) - addi {input},{input},4 - sw {a},0({start}) - addi {start},{start},4 - bltu {start},{end},1b - - 2: - li {a},0 - li {input},0 - - // Zero out .bss - la {start},_sbss - la {end},_ebss - - bgeu {start},{end},3f - 2: - sw zero,0({start}) - addi {start},{start},4 - bltu {start},{end},2b - - 3: - li {start},0 - li {end},0 - ", - start = out(reg) _, - end = out(reg) _, - input = out(reg) _, - a = out(reg) _, - ); - - #[cfg(target_arch = "riscv64")] - core::arch::asm!( - " - // Copy over .data - la {start},_sdata - la {end},_edata - la {input},_sidata - - bgeu {start},{end},2f - - 1: // .data Main Loop - ld {a},0({input}) - addi {input},{input},8 - sd {a},0({start}) - addi {start},{start},8 - bltu {start},{end},1b - - 2: // .data zero registers - li {a},0 - li {input},0 - - la {start},_sbss - la {end},_ebss - - bgeu {start},{end},4f - - 3: // .bss main loop - sd zero,0({start}) - addi {start},{start},8 - bltu {start},{end},3b - - 4: // .bss zero registers - // Zero out used registers - li {start},0 - li {end},0 - ", - start = out(reg) _, - end = out(reg) _, - input = out(reg) _, - a = out(reg) _, - ); - - compiler_fence(Ordering::SeqCst); - } - - #[cfg(any(riscvf, riscvd))] - { - xstatus::set_fs(xstatus::FS::Initial); // Enable fpu in xstatus - core::arch::asm!("fscsr x0"); // Zero out fcsr register csrrw x0, fcsr, x0 - - // Zero out floating point registers - #[cfg(all(target_arch = "riscv32", riscvd))] - riscv_rt_macros::loop_asm!("fcvt.d.w f{}, x0", 32); - - #[cfg(all(target_arch = "riscv64", riscvd))] - riscv_rt_macros::loop_asm!("fmv.d.x f{}, x0", 32); - - #[cfg(not(riscvd))] - riscv_rt_macros::loop_asm!("fmv.w.x f{}, x0", 32); - } - - _setup_interrupts(); - - main(a0, a1, a2); -} - /// Registers saved in trap handler #[allow(missing_docs)] #[repr(C)]