Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv-rt: move all the assembly code to asm.rs #186

Merged
merged 13 commits into from
Feb 22, 2024
2 changes: 1 addition & 1 deletion .github/workflows/riscv-rt.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
on:
push:
branches: [ master ]
branches: [ master, riscv-rt-asm ]
pull_request:
merge_group:

Expand Down
9 changes: 9 additions & 0 deletions riscv-rt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Changed

- Moved all the assembly code to `asm.rs`

### Removed

- `start_rust` is no longer needed, as it is now written in assembly

## [v0.12.2] - 2024-02-15

### Added
Expand All @@ -33,6 +41,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<fn>`, which is more idiomatic in Rust
- Removed riscv-target dependency for build
Expand Down
2 changes: 1 addition & 1 deletion riscv-rt/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "riscv-rt"
version = "0.12.2"
version = "0.13.0"
rust-version = "1.60"
repository = "https://github.com/rust-embedded/riscv"
authors = ["The RISC-V Team <[email protected]>"]
Expand Down
73 changes: 62 additions & 11 deletions riscv-rt/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,46 +212,59 @@ 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 {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let template: LitStr = input.parse().unwrap();
_ = input.parse::<Token![,]>().unwrap();
let count: LitInt = input.parse().unwrap();

Ok(Self {
asm_template: template.value(),
count: count.base10_parse().unwrap(),
})
if input.parse::<Token![,]>().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
///
/// ```
/// # 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);
Expand All @@ -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::<Vec<String>>()
.join("\n");

let res = format!("core::arch::global_asm!(\n\"{}\"\n);", instructions);
res.parse().unwrap()
}
162 changes: 121 additions & 41 deletions riscv-rt/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,54 +72,34 @@ _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, STACK POINTER, AND FRAME 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",
);
#[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
Expand All @@ -128,15 +108,115 @@ _abs_start:
addi t1, t1, -1
bnez t1, 1b
2: ",
);
cfg_global_asm!(
"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
"andi sp, t1, -16 // align stack to 16-bytes
add s0, sp, zero",
);

jal zero, _start_rust
// 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)",
);

// 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!(
"
li t0, 0x4000 // bit 14 is FS most significant bit
li t2, 0x2000 // bit 13 is FS least significant bit
",
#[cfg(feature = "s-mode")]
"csrrc x0, sstatus, t0
csrrs x0, sstatus, t2",
#[cfg(not(feature = "s-mode"))]
"csrrc x0, mstatus, t0
csrrs x0, mstatus, t2",
"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",
);

Expand Down
Loading
Loading