Skip to content

Commit

Permalink
Add riscv-target-parser crate
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Nov 18, 2024
1 parent d6365e0 commit 289206a
Show file tree
Hide file tree
Showing 14 changed files with 889 additions and 110 deletions.
20 changes: 15 additions & 5 deletions .github/workflows/changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
- 'riscv-rt/**'
riscv-semihosting:
- 'riscv-semihosting/**'
riscv-target-parser:
- 'riscv-target-parser/**'
- name: Check for CHANGELOG.md (riscv)
if: steps.changes.outputs.riscv == 'true'
Expand All @@ -43,7 +45,15 @@ jobs:
changeLogPath: ./riscv-pac/CHANGELOG.md
skipLabels: 'skip changelog'
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-pac/CHANGELOG.md file.'


- name: Check for CHANGELOG.md (riscv-peripheral)
if: steps.changes.outputs.riscv-peripheral == 'true'
uses: dangoslen/changelog-enforcer@v3
with:
changeLogPath: ./riscv-peripheral/CHANGELOG.md
skipLabels: 'skip changelog'
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-peripheral/CHANGELOG.md file.'

- name: Check for CHANGELOG.md (riscv-rt)
if: steps.changes.outputs.riscv-rt == 'true'
uses: dangoslen/changelog-enforcer@v3
Expand All @@ -60,10 +70,10 @@ jobs:
skipLabels: 'skip changelog'
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-semihosting/CHANGELOG.md file.'

- name: Check for CHANGELOG.md (riscv-peripheral)
if: steps.changes.outputs.riscv-peripheral == 'true'
- name: Check for CHANGELOG.md (riscv-target-parser)
if: steps.changes.outputs.riscv-target-parser == 'true'
uses: dangoslen/changelog-enforcer@v3
with:
changeLogPath: ./riscv-peripheral/CHANGELOG.md
changeLogPath: ./riscv-target-parser/CHANGELOG.md
skipLabels: 'skip changelog'
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-peripheral/CHANGELOG.md file.'
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-target-parser/CHANGELOG.md file.'
37 changes: 37 additions & 0 deletions .github/workflows/riscv-target-parser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
on:
push:
branches: [ master ]
pull_request:
merge_group:

name: Run tests (riscv-target-parser)

jobs:
run-tests:
strategy:
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ] # windows shows weird linking errors
toolchain: [ stable, nightly, 1.61.0 ]
include:
# Nightly is only for reference and allowed to fail
- rust: nightly
experimental: true
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental || false }}
steps:
- uses: actions/checkout@v4
- name: Update Rust toolchain
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- name: Build
run: cargo build --package riscv-target-parser
- name: Run tests
run: cargo test --package riscv-target-parser

# Job to check that all the builds succeeded
tests-check:
needs:
- run-tests
runs-on: ubuntu-latest
if: always()
steps:
- run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ members = [
"riscv-peripheral",
"riscv-rt",
"riscv-semihosting",
"riscv-target-parser",
"tests",
]
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This repository contains various crates useful for writing Rust programs on RISC
* [`riscv-peripheral`]: Interfaces for standard RISC-V peripherals
* [`riscv-rt`]: Startup code and interrupt handling
* [`riscv-semihosting`]: Semihosting for RISC-V processors
* [`riscv-target-parser`]: Utility crate for parsing RISC-V targets in build scripts

This project is developed and maintained by the [RISC-V team][team].

Expand All @@ -27,5 +28,6 @@ to intervene to uphold that code of conduct.
[`riscv-peripheral`]: https://crates.io/crates/riscv-peripheral
[`riscv-rt`]: https://crates.io/crates/riscv-rt
[`riscv-semihosting`]: https://crates.io/crates/riscv-semihosting
[`riscv-target-parser`]: https://crates.io/crates/riscv-target-parser
[team]: https://github.com/rust-embedded/wg#the-risc-v-team
[CoC]: CODE_OF_CONDUCT.md
3 changes: 3 additions & 0 deletions riscv-rt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ targets = [
"riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf",
]

[build-dependencies]
riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.0" }

[dependencies]
riscv = { path = "../riscv", version = "0.12.0" }
riscv-pac = { path = "../riscv-pac", version = "0.2.0" }
Expand Down
99 changes: 16 additions & 83 deletions riscv-rt/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// NOTE: Adapted from cortex-m/build.rs

use std::{collections::HashSet, env, fs, io, path::PathBuf};
use riscv_target_parser::RiscvTarget;
use std::{env, fs, io, path::PathBuf};

fn add_linker_script(arch_width: u32) -> io::Result<()> {
// Read the file to a string and replace all occurrences of ${ARCH_WIDTH} with the arch width
Expand All @@ -17,96 +18,28 @@ fn add_linker_script(arch_width: u32) -> io::Result<()> {
Ok(())
}

/// Parse the target RISC-V architecture and returns its bit width and the extension set
fn parse_target(target: &str, cargo_flags: &str) -> (u32, HashSet<char>) {
// isolate bit width and extensions from the rest of the target information
let arch = target
.trim_start_matches("riscv")
.split('-')
.next()
.unwrap();

let bits = arch
.chars()
.take_while(|c| c.is_ascii_digit())
.collect::<String>()
.parse::<u32>()
.unwrap();

let mut extensions: HashSet<char> = arch.chars().skip_while(|c| c.is_ascii_digit()).collect();
// expand the 'g' shorthand extension
if extensions.contains(&'g') {
extensions.insert('i');
extensions.insert('m');
extensions.insert('a');
extensions.insert('f');
extensions.insert('d');
}

let cargo_flags = cargo_flags
.split(0x1fu8 as char)
.filter(|arg| !arg.is_empty());

cargo_flags
.filter(|k| k.starts_with("target-feature="))
.flat_map(|str| {
let flags = str.split('=').collect::<Vec<&str>>()[1];
flags.split(',')
})
.for_each(|feature| {
let chars = feature.chars().collect::<Vec<char>>();
match chars[0] {
'+' => {
extensions.insert(chars[1]);
}
'-' => {
extensions.remove(&chars[1]);
}
_ => {
panic!("Unsupported target feature operation");
}
}
});

(bits, extensions)
}

fn main() {
println!("cargo:rustc-check-cfg=cfg(riscv)");
println!("cargo:rustc-check-cfg=cfg(riscv32)");
println!("cargo:rustc-check-cfg=cfg(riscv64)");
// Required until target_feature risc-v is stable and in-use (rust 1.75)
for ext in ['i', 'e', 'm', 'a', 'f', 'd', 'g', 'c'] {
println!("cargo:rustc-check-cfg=cfg(riscv{})", ext);
}

let target = env::var("TARGET").unwrap();
let cargo_flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();
let _name = env::var("CARGO_PKG_NAME").unwrap();

// set configuration flags depending on the target
if target.starts_with("riscv") {
println!("cargo:rustc-cfg=riscv");
// This is required until target_arch & target_feature risc-v work is
// stable and in-use (rust 1.75.0)
let (bits, extensions) = parse_target(&target, &cargo_flags);

// generate the linker script and expose the ISA width
let arch_width = match bits {
32 => {
println!("cargo:rustc-cfg=riscv32");
4
}
64 => {
println!("cargo:rustc-cfg=riscv64");
8
}
_ => panic!("Unsupported bit width"),
};
add_linker_script(arch_width).unwrap();

// expose the ISA extensions
for ext in &extensions {
println!("cargo:rustc-cfg=riscv{}", ext);
if let Ok(target) = RiscvTarget::build(&target, &cargo_flags) {
let width = target.width();
if matches!(width, riscv_target_parser::Width::W128) {
panic!("Unsupported RISC-V target: {width}");
}
if target.base_extension().is_none() {
panic!("Unsupported RISC-V target: no base extension");
}
for flag in target.rustc_flags() {
// Required until target_feature risc-v is stable and in-use
println!("cargo:rustc-check-cfg=cfg({flag})");
println!("cargo:rustc-cfg={flag}");
}
add_linker_script(width.into()).unwrap();
}
}
34 changes: 18 additions & 16 deletions riscv-rt/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ macro_rules! cfg_global_asm {
// - https://github.com/llvm/llvm-project/issues/61991
cfg_global_asm!(
"// Provisional patch to avoid LLVM spurious errors when compiling in release mode.",
#[cfg(all(riscv32, riscvm))]
#[cfg(all(target_arch = "riscv32", riscvm))]
".attribute arch, \"rv32im\"",
#[cfg(all(riscv64, riscvm, not(riscvg)))]
#[cfg(all(target_arch = "riscv64", riscvm, not(riscvg)))]
".attribute arch, \"rv64im\"",
#[cfg(all(riscv64, riscvg))]
#[cfg(all(target_arch = "riscv64", riscvg))]
".attribute arch, \"rv64g\"",
);

Expand All @@ -47,10 +47,10 @@ cfg_global_asm!(
.global _start
_start:",
#[cfg(riscv32)]
#[cfg(target_arch = "riscv32")]
"lui ra, %hi(_abs_start)
jr %lo(_abs_start)(ra)",
#[cfg(riscv64)]
#[cfg(target_arch = "riscv64")]
".option push
.option norelax // to prevent an unsupported R_RISCV_ALIGN relocation from being generated
1:
Expand Down Expand Up @@ -84,7 +84,9 @@ _abs_start:
// 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);
riscv_rt_macros::loop_global_asm!(" li x{}, 0", 13, 16);
#[cfg(not(riscve))]
riscv_rt_macros::loop_global_asm!(" li x{}, 0", 16, 32);

// INITIALIZE GLOBAL POINTER, STACK POINTER, AND FRAME POINTER
cfg_global_asm!(
Expand Down Expand Up @@ -125,12 +127,12 @@ cfg_global_asm!(

// STORE A0..A2 IN THE STACK, AS THEY WILL BE NEEDED LATER BY main
cfg_global_asm!(
#[cfg(riscv32)]
#[cfg(target_arch = "riscv32")]
"addi sp, sp, -4 * 3
sw a0, 4 * 0(sp)
sw a1, 4 * 1(sp)
sw a2, 4 * 2(sp)",
#[cfg(riscv64)]
#[cfg(target_arch = "riscv64")]
"addi sp, sp, -8 * 3
sd a0, 8 * 0(sp)
sd a1, 8 * 1(sp)
Expand Down Expand Up @@ -202,22 +204,22 @@ cfg_global_asm!(
"fscsr x0",
);
// ZERO OUT FLOATING POINT REGISTERS
#[cfg(all(riscv32, riscvd))]
#[cfg(all(target_arch = "riscv32", riscvd))]
riscv_rt_macros::loop_global_asm!(" fcvt.d.w f{}, x0", 32);
#[cfg(all(riscv64, riscvd))]
#[cfg(all(target_arch = "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)]
#[cfg(target_arch = "riscv32")]
"lw a0, 4 * 0(sp)
lw a1, 4 * 1(sp)
lw a2, 4 * 2(sp)
addi sp, sp, 4 * 3",
#[cfg(riscv64)]
#[cfg(target_arch = "riscv64")]
"ld a0, 8 * 0(sp)
ld a1, 8 * 1(sp)
ld a2, 8 * 2(sp)
Expand Down Expand Up @@ -276,14 +278,14 @@ _pre_init_trap:
j _pre_init_trap",
);

#[cfg(riscv32)]
#[cfg(target_arch = "riscv32")]
riscv_rt_macros::weak_start_trap_riscv32!();
#[cfg(riscv64)]
#[cfg(target_arch = "riscv64")]
riscv_rt_macros::weak_start_trap_riscv64!();

#[cfg(all(riscv32, feature = "v-trap"))]
#[cfg(all(target_arch = "riscv32", feature = "v-trap"))]
riscv_rt_macros::vectored_interrupt_trap_riscv32!();
#[cfg(all(riscv64, feature = "v-trap"))]
#[cfg(all(target_arch = "riscv64", feature = "v-trap"))]
riscv_rt_macros::vectored_interrupt_trap_riscv64!();

#[rustfmt::skip]
Expand Down
5 changes: 4 additions & 1 deletion riscv-rt/src/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ pub unsafe extern "C" fn _dispatch_core_interrupt(code: usize) {
}

// In vectored mode, we also must provide a vector table
#[cfg(all(riscv, feature = "v-trap"))]
#[cfg(all(
any(target_arch = "riscv32", target_arch = "riscv64"),
feature = "v-trap"
))]
core::arch::global_asm!(
r#" .section .trap, "ax"
.weak _vector_table
Expand Down
Loading

0 comments on commit 289206a

Please sign in to comment.