Skip to content

Commit

Permalink
Merge pull request #186 from rust-embedded/riscv-rt-asm
Browse files Browse the repository at this point in the history
`riscv-rt`: move all the assembly code to `asm.rs`
  • Loading branch information
romancardenas authored Feb 22, 2024
2 parents 527b27d + 09e1b14 commit f5a2da9
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 225 deletions.
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 <risc-v@teams.rust-embedded.org>"]
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

0 comments on commit f5a2da9

Please sign in to comment.