diff --git a/.cargo/config.toml b/.cargo/config.toml index 7d0f7b5..195a1a5 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -10,6 +10,7 @@ # thumbv6m-none-eabi -> Cortex-M0, microbit/nRF51 (flash @ 0x0, 16K RAM); a real # M0 machine, so the canary catches both RAM overflow and # out-of-ISA (ARMv7E-M) contamination that faults on silicon. +# riscv32imc-unknown-none-elf -> QEMU virt DRAM @ 0x80000000 (-bios none) [target.thumbv7em-none-eabi] # soft-float runner = "qemu-system-arm -machine netduinoplus2 -nographic -semihosting-config enable=on,target=native -kernel" @@ -32,8 +33,17 @@ rustflags = [ "-C", "link-arg=--nmagic", ] +[target.riscv32imc-unknown-none-elf] +runner = "qemu-system-riscv32 -machine virt -nographic -semihosting-config enable=on,target=native -bios none -kernel" +rustflags = [ + "-C", "link-arg=-Tmemory.x", # must come before link x for riscv-rt + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=--nmagic", +] + [alias] fit = "run -p multicalc-demos --example curve_fit" smoke-eabi = "run -p embedded-smoke --release --target thumbv7em-none-eabi" smoke-eabihf = "run -p embedded-smoke --release --target thumbv7em-none-eabihf" smoke-m0 = "run -p embedded-smoke --release --no-default-features --target thumbv6m-none-eabi" +smoke-riscv = "run -p embedded-smoke --release --target riscv32imc-unknown-none-elf" diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml index 7d3453b..b3c47a6 100644 --- a/.github/workflows/matrix.yml +++ b/.github/workflows/matrix.yml @@ -3,7 +3,7 @@ name: Target matrix # Reusable: called by CI on pull requests and by Release on a version bump. # # `full` selects the target set. false (default) = the per-PR representative set -# (x86_64 host + all three bare-metal ABIs, thumbv6m as a reduced canary); true = +# (x86_64 host + all four bare-metal ABIs, thumbv6m as a reduced canary); true = # the complete matrix, adding the aarch64 host. The `setup` job turns that flag into # the JSON matrices the build jobs consume, so representative and full share one set # of steps and differ only in the host list. @@ -33,9 +33,9 @@ jobs: shell: bash run: | # thumbv6m runs the reduced canary (--no-default-features) on every PR; the - # thumbv7em ABIs run the full set. The bare set is the same for representative - # and full runs — full only adds the aarch64 host. - bare='[{"target":"thumbv7em-none-eabi","args":""},{"target":"thumbv7em-none-eabihf","args":""},{"target":"thumbv6m-none-eabi","args":"--no-default-features"}]' + # thumbv7em ABIs and riscv32 run the full set. The bare set is the same for + # representative and full runs — full only adds the aarch64 host. + bare='[{"target":"thumbv7em-none-eabi","args":""},{"target":"thumbv7em-none-eabihf","args":""},{"target":"thumbv6m-none-eabi","args":"--no-default-features"},{"target":"riscv32imc-unknown-none-elf","args":""}]' if [ "${{ inputs.full }}" = "true" ]; then hosts='[{"target":"x86_64-unknown-linux-gnu","runner":"ubuntu-latest"},{"target":"aarch64-unknown-linux-gnu","runner":"ubuntu-24.04-arm"}]' else @@ -92,6 +92,11 @@ jobs: with: packages: qemu-system-arm version: 1.0 + - name: Install QEMU (RISC-V) + run: | + sudo apt-get update + sudo apt-get install -y qemu-system-misc + command -v qemu-system-riscv32 - name: Run smoke tests + budget checks under QEMU run: | for bin in embedded-smoke; do diff --git a/CHANGELOG.md b/CHANGELOG.md index 237e1d0..e45695b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 polynomial compositions. @rtmongold (#129) - Opt-in Kahan compensated summation for iterative integration and approximation metrics via `.with_kahan_summation()`; pairwise remains the default. @rtmongold (#134) +- **RISC-V embedded smoke.** `embedded-smoke` runs on `riscv32imc-unknown-none-elf` + under QEMU `virt`, gated with its own flash/stack budgets. @rtmongold (#140) ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a61d862..8414d67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,8 +8,8 @@ is CI-enforced, so run it before pushing. 4. `cargo fmt`. -That's it. **You do not need QEMU or ARM toolchains** for most contributions — CI runs -the five-target embedded matrix on your PR for you. If you want the bare-metal targets +That's it. **You do not need QEMU or bare-metal toolchains** for most contributions — CI runs +the multi-target embedded matrix on your PR for you. If you want the bare-metal targets locally anyway, the setup lives in [ci/README.md](ci/README.md) (optional). ## Workflow @@ -19,7 +19,7 @@ locally anyway, the setup lives in [ci/README.md](ci/README.md) (optional). - Every behavior-facing change adds a line under `## [Unreleased]` in [CHANGELOG.md](CHANGELOG.md) (grouped `Added`/`Changed`/`Fixed`/`Removed`), in the same PR. -- CI must be green: host tests (x86_64 + aarch64), clippy, the three bare-metal QEMU +- CI must be green: host tests (x86_64 + aarch64), clippy, the four bare-metal QEMU smoke runs, and the flash/stack budget gates. If a budget gate trips, see the raise protocol in [ci/README.md](ci/README.md) — never raise `tolerance_pct`. diff --git a/README.md b/README.md index e85f80e..6605398 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) **Scientific computing for single- and multi-variable calculus in pure, safe Rust. Numerical -derivatives, integrals, curve fitting and linear algebra; built and tested on five +derivatives, integrals, curve fitting and linear algebra; built and tested on six hardware targets. Exercise the same code from a 64-bit server CPU down to a bare-metal microcontroller.**

@@ -22,9 +22,9 @@ core — every number measured live.* ## Highlights - **Pure, safe Rust**: `#![forbid(unsafe_code)]`, no C dependencies. -- **Tested against multiple platforms**: Every commit is built and tested across **five targets**: the +- **Tested against multiple platforms**: Every commit is built and tested across **six targets**: the `x86_64` and `aarch64` Linux hosts and three ARM Cortex-M bare-metal ABIs (`thumbv7em` - soft-float, `thumbv7em` hardware-FPU, and `thumbv6m`). `no_std`, no heap, and no panics rules apply to every platform build, and the transcendental functions come from + soft-float, `thumbv7em` hardware-FPU, and `thumbv6m`), and `riscv32imc-unknown-none-elf`. `no_std`, no heap, and no panics rules apply to every platform build, and the transcendental functions come from [`libm`](https://crates.io/crates/libm). - **Fast, and measured**: a derivative in **~1 ns**, a full Levenberg-Marquardt curve fit in **microseconds**, and solvers that land on the answer to the **last few bits** (objectives near @@ -114,7 +114,7 @@ below. The published library crate lives in [`crates/multicalc`](crates/multicalc); the repository root is a Cargo workspace. Runnable demos live in the dev-only [`demos/`](demos) crate (basics and live Rerun showcases), and [`tools/embedded-smoke`](tools/embedded-smoke) runs `multicalc` on the -three bare-metal Cortex-M targets under QEMU every PR. +four bare-metal targets (three Cortex-M targets + `riscv32imc`) under QEMU every PR. ## Contributing diff --git a/ci/README.md b/ci/README.md index 058ecc9..2a4e6f0 100644 --- a/ci/README.md +++ b/ci/README.md @@ -21,18 +21,19 @@ another's headroom; keep them tight and per-row. ## Smoke-test harness -The on-target smoke binary is a hand-rolled `cortex-m-rt` `#[entry]` runner. It runs every check -in sequence in a single QEMU invocation, prints machine-readable result lines over semihosting -(`hprintln!`, i.e. `SYS_WRITE0`), and exits QEMU with `debug::exit`. The budget gate and the -cross-ABI guard parse those lines straight from QEMU stdout. +The on-target smoke binary is a hand-rolled `#[entry]` runner (`cortex-m-rt` on ARM, +`riscv-rt` on RISC-V). It runs every check in sequence in a single QEMU invocation, prints +machine-readable result lines over semihosting (`hprintln!`, i.e. `SYS_WRITE0`), and exits +QEMU with `debug::exit`. The budget gate and the cross-ABI guard parse those lines straight +from QEMU stdout. `embedded-test`/`defmt-test` are not used. Their harness is driven by the `probe-rs` host runner: it enumerates test cases through a probe-rs-specific semihosting operation and resets the target to -run each `#[test]` in its own invocation. Under the plain `qemu-system-arm -kernel` runner these -smoke tests use, nothing drives that protocol, so the harness aborts before any test runs. The -per-test reset model is also incompatible with the single-invocation stack high-watermark -measurement the budget gate reads. `defmt` output is avoided for the same parsing reason: its RTT -transport needs a host-side decoder, while the plain-text semihosting lines need none. +run each `#[test]` in its own invocation. Under the plain `qemu-system-arm` / +`qemu-system-riscv32 -kernel` runners these smoke tests use, nothing drives that protocol, so +the harness aborts before any test runs. The per-test reset model is also incompatible with the single-invocation stack high-watermark measurement the budget gate reads. `defmt` output is avoided +for the same parsing reason: its RTT transport needs a host-side decoder, while the plain-text +semihosting lines need none. ## Smoke fixtures (shared with the oracle) @@ -52,10 +53,11 @@ a stale checked-in copy fails the build. Change a fixture and rerun the generato ## Test-set tiering The `embedded-smoke` `full-smoke` feature (on by default) selects the check set. Both `thumbv7em` -ABIs build with default features and run the full set; `thumbv6m` builds with `--no-default-features` -and runs only the canary — the portable (no-atomics) path, one golden, and the no-panic negative -path — which keeps the M0 image small while still smoke-running every PR. The target matrix threads -`--no-default-features` into the `thumbv6m` run (see `.github/workflows/matrix.yml`). +ABIs and `riscv32imc-unknown-none-elf` build with default features and run the full set; +`thumbv6m` builds with `--no-default-features` and runs only the canary — the portable +(no-atomics) path, one golden, and the no-panic negative path — which keeps the M0 image small +while still smoke-running every PR. The target matrix threads `--no-default-features` into the +`thumbv6m` run (see `.github/workflows/matrix.yml`). ## Cross-ABI divergence guard diff --git a/ci/budgets.toml b/ci/budgets.toml index 330ec04..dade5cc 100644 --- a/ci/budgets.toml +++ b/ci/budgets.toml @@ -2,7 +2,7 @@ # # Sections are [.], where is the cargo package name of a # smoke crate (embedded-smoke today; per-domain smoke binaries are added at v0.9+, -# each its own section) and is one of the three bare-metal triples below. +# each its own section) and is one of the four bare-metal triples below. # Splitting budgets per binary keeps the gate fair: a heavy new domain grows its own # binary's budget and never spends another domain's headroom. # @@ -37,3 +37,7 @@ stack_bytes_f64 = 1548 [embedded-smoke.thumbv6m-none-eabi] text_bytes = 43424 stack_bytes_f64 = 296 + +[embedded-smoke.riscv32imc-unknown-none-elf] +text_bytes = 78914 +stack_bytes_f64 = 1220 diff --git a/ci/check_budgets.sh b/ci/check_budgets.sh index c1bc35e..8eed815 100644 --- a/ci/check_budgets.sh +++ b/ci/check_budgets.sh @@ -12,6 +12,7 @@ # `cargo size`), e.g. embedded-smoke. Must match the "." # prefix of a [.] section in ci/budgets.toml. # thumbv7em-none-eabi | thumbv7em-none-eabihf | thumbv6m-none-eabi +# | riscv32imc-unknown-none-elf # captured stdout of the run; contains a STACK_HWM_BYTES[...]= # line. Produce it with: # cargo run -p --release --target | tee @@ -20,7 +21,7 @@ set -euo pipefail budgets="ci/budgets.toml" -valid_targets="thumbv7em-none-eabi thumbv7em-none-eabihf thumbv6m-none-eabi" +valid_targets="thumbv7em-none-eabi thumbv7em-none-eabihf thumbv6m-none-eabi riscv32imc-unknown-none-elf" die() { echo ">>> ERROR: $1" >&2 @@ -120,7 +121,7 @@ if ! size_out=$(cargo size --release $size_features -p "$binary" --target "$targ "Add the target: rustup target add $target" \ "Build it at least once: cargo build -p $binary --release --target $target" fi -text_now=$(printf '%s\n' "$size_out" | awk '/^\.text/ { print $2 }') +text_now=$(printf '%s\n' "$size_out" | awk '$1 == ".text" { print $2; exit }') printf '%s' "$text_now" | grep -qE '^[0-9]+$' || { echo "$size_out" >&2; die \ "could not parse a numeric .text size for $binary/$target (got '$text_now')." \ "Look for a '.text' row in the 'cargo size ... -- -A' output above."; } diff --git a/crates/multicalc/README.md b/crates/multicalc/README.md index bf36f50..ebc8993 100644 --- a/crates/multicalc/README.md +++ b/crates/multicalc/README.md @@ -11,12 +11,12 @@ Jacobians and Hessians, vector-field operators, and Taylor approximation in a `n ## Why use it -- **Tested across five hardware targets**: Every commit is built and tested on the `x86_64` and - `aarch64` Linux hosts and on three ARM Cortex-M bare-metal ABIs (`thumbv7em` soft-float, - `thumbv7em` hardware-FPU, and `thumbv6m`). The Cortex-M builds run the real math under QEMU on - each ABI and check the answers against known values, so the same code is exercised from a 64-bit - server CPU down to a microcontroller with no operating system. `no_std`, no-heap, and - no-panic rules hold on every one. +- **Tested across six hardware targets**: Every commit is built and tested on the `x86_64` and + `aarch64` Linux hosts and on four bare-metal ABIs (three ARM Cortex-M: `thumbv7em` soft-float, + `thumbv7em` hardware-FPU, and `thumbv6m`; plus `riscv32imc-unknown-none-elf`). The bare-metal builds + run the real math under QEMU on each ABI and check the answers against known values, so the same + code is exercised from a 64-bit server CPU down to a microcontroller with no operating system. + `no_std`, no-heap, and no-panic rules hold on every one. - **Accurate to the last few bits**: differentiation, Jacobians, Hessians, and Newton steps use forward-mode automatic differentiation, so derivatives are exact rather than finite-difference estimates. The least-squares and root-finding solvers drive objectives down near `1e-30` and land diff --git a/tools/embedded-smoke/Cargo.toml b/tools/embedded-smoke/Cargo.toml index aa4da7d..95d18d8 100644 --- a/tools/embedded-smoke/Cargo.toml +++ b/tools/embedded-smoke/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" rust-version = "1.85" publish = false -# thumbv7em runs the full set (default). thumbv6m runs the reduced canary via -# --no-default-features, keeping the M0 image small. +# thumbv7em / riscv32 runs the full set (default). thumbv6m runs the reduced +# canary via --no-default-features, keeping the M0 image small. [features] default = ["full-smoke"] full-smoke = [] @@ -14,11 +14,18 @@ full-smoke = [] [dependencies] multicalc = { path = "../../crates/multicalc", default-features = false } multicalc-testkit = { path = "../testkit" } + +[target.'cfg(target_arch = "arm")'.dependencies] cortex-m-rt = "0.7" cortex-m-semihosting = "0.5" # With the "exit" feature a panic prints its message and exits QEMU with a failure code. panic-semihosting = { version = "0.6", features = ["exit"] } +[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies] +riscv-rt = "0.13" +riscv-semihosting = "0.2" +riscv = { version = "0.12", features = ["critical-section-single-hart"] } + # This dev-only crate needs one small unsafe block to measure stack use, so it does # not inherit the core crate's "no unsafe" rule. It sets its own lints instead of # using `[lints] workspace = true`. diff --git a/tools/embedded-smoke/README.md b/tools/embedded-smoke/README.md index 0cfe7cf..3e909fc 100644 --- a/tools/embedded-smoke/README.md +++ b/tools/embedded-smoke/README.md @@ -1,45 +1,48 @@ # embedded-smoke -Runs smoke tests for all five supported platforms for `multicalc`. Must return identical numbers on -every target it supports: x86_64 and -aarch64 Linux hosts, plus three bare-metal Cortex-M ABIs (Cortex-M4 soft-float, -Cortex-M4 hard-float, and Cortex-M0). Host tests cover the first two. Nothing but -real on-target execution can cover the last three, and that is this crate. It -exercises the `multicalc` code on every Cortex-M ABI and holds each to the same known -answers, so a divergence on any supported architecture fails the build. - -It hosts `no_std` / `no_main` smoke test that runs `multicalc` on bare-metal Cortex-M -ABIs under QEMU. It is a dev-only crate (`publish = false`, not in -`default-members`) and is never built for a host target, and `cortex-m-rt` only -links for the `thumb*` triples. +Runs smoke tests for all supported platforms for `multicalc`. Must return identical +numbers on every target it supports: x86_64 and aarch64 Linux hosts, plus four +bare-metal ABIs (Cortex-M4 soft-float, Cortex-M4 hard-float, Cortex-M0, and +`riscv32imc`). Host tests cover the first two. Nothing but real on-target execution +can cover the bare-metal set, and that is this crate. It exercises `multicalc` on +each ABI and holds each to the same known answers, so a divergence on any supported +architecture fails the build. + +It hosts a `no_std` / `no_main` smoke test that runs `multicalc` on bare-metal targets +under QEMU. It is a dev-only crate (`publish = false`, not in `default-members`) and +is never built for a host target. `cortex-m-rt` links for `thumb*` triples; `riscv-rt` +links for `riscv32*`. ## Running ```sh -rustup target add thumbv7em-none-eabi thumbv7em-none-eabihf thumbv6m-none-eabi -sudo apt-get install -y qemu-system-arm # provides qemu-system-arm -cargo install cargo-binutils # provides cargo size, for the gate +rustup target add thumbv7em-none-eabi thumbv7em-none-eabihf thumbv6m-none-eabi riscv32imc-unknown-none-elf +sudo apt-get install -y qemu-system-arm qemu-system-misc # provides qemu-system-arm +cargo install cargo-binutils # provides cargo size, for the gate cargo run -p embedded-smoke --release --target thumbv7em-none-eabi cargo run -p embedded-smoke --release --target thumbv7em-none-eabihf -cargo run -p embedded-smoke --release --target thumbv6m-none-eabi +cargo run -p embedded-smoke --release --target thumbv6m-none-eabi --no-default-features +cargo run -p embedded-smoke --release --target riscv32imc-unknown-none-elf ``` -Aliases: `cargo smoke-eabi`, `cargo smoke-eabihf`, `cargo smoke-m0`. +Aliases: `cargo smoke-eabi`, `cargo smoke-eabihf`, `cargo smoke-m0`, `cargo smoke-riscv`. ## Targets and QEMU machine -| Target | Codegen | QEMU machine | RAM | -|-------------------------|------------------------|----------------|------| -| `thumbv7em-none-eabi` | Cortex-M4, soft-float | `netduinoplus2`| 64K | -| `thumbv7em-none-eabihf` | Cortex-M4, hard-float | `netduinoplus2`| 64K | -| `thumbv6m-none-eabi` | Cortex-M0, CAS-free | `microbit` | 16K | +| Target | Codegen | QEMU machine | RAM | +|-------------------------------|------------------------|-----------------------|------| +| `thumbv7em-none-eabi` | Cortex-M4, soft-float | `netduinoplus2` | 64K | +| `thumbv7em-none-eabihf` | Cortex-M4, hard-float | `netduinoplus2` | 64K | +| `thumbv6m-none-eabi` | Cortex-M0, CAS-free | `microbit` | 16K | +| `riscv32imc-unknown-none-elf` | RV32IMC, soft-float | `virt` (`-bios none`) | 256K | The `thumbv7em` ABIs run on `netduinoplus2` (Cortex-M4, FPU, 64K RAM, flash at `0x08000000`). `thumbv6m` runs on `microbit` (Cortex-M0, nRF51, 16K RAM, flash at `0x0`) — a real M0 core, so the run now asserts both RAM-size and ISA fidelity: an oversized image or an out-of-ISA (ARMv7E-M) instruction faults just -as it would on silicon. `build.rs` picks each target's memory map. +as it would on silicon. `riscv32imc` runs on QEMU `virt` with `-bios none` +(DRAM at `0x80000000`). `build.rs` picks each target's memory map. The runners and `rustflags` (`-Tlink.x`, `--nmagic`) live in `.cargo/config.toml`; the per-target memory map is supplied by `build.rs`. @@ -64,7 +67,7 @@ checks assert a mathematical identity that needs no fixture. The set is tiered by the `full-smoke` feature (on by default): the canary runs on every target including the Cortex-M0; the full set adds the heavier checks on the -`thumbv7em` ABIs only. `thumbv6m` builds with `--no-default-features`. +`thumbv7em` ABIs and `riscv32imc`. `thumbv6m` builds with `--no-default-features`. | Test | Targets | Details | |------|---------|---------| @@ -72,16 +75,17 @@ every target including the Cortex-M0; the full set adds the heavier checks on th | `svd_golden` | all (incl. `thumbv6m`) | Golden: singular values of a 3×3 fixture matrix vs the `svd_3x3` oracle golden. Also emits `SMOKE_VAL_svd_s*` for the cross-ABI guard. | | `error_path_returns_err` | all (incl. `thumbv6m`) | Negative path: a singular matrix's `lu()` and an indefinite matrix's `cholesky()` return a typed `Err`, not a panic. | | `lm_fit` | `thumbv7em` only | Golden: Levenberg-Marquardt Rosenbrock least-squares minimizer vs the `rosenbrock` oracle golden. | -| `autodiff_derivative` | `thumbv7em` only | Identity: forward-mode autodiff of `x³` at `x = 2`, expects 12. | -| `ode_identity` | `thumbv7em` only | Identity: RK4 harmonic oscillator round trip and RK45 `y' = -y` to `e^{-1}`. | +| `autodiff_derivative` | `thumbv7em` + `riscv32imc` | Identity: forward-mode autodiff of `x³` at `x = 2`, expects 12. | +| `ode_identity` | `thumbv7em` + `riscv32imc` | Identity: RK4 harmonic oscillator round trip and RK45 `y' = -y` to `e^{-1}`. | ## Pass/fail contract The binary runs under QEMU semihosting: - Clean finish → `debug::exit(EXIT_SUCCESS)` → QEMU exits 0. -- Panic (failed `assert!`/`expect`) → `panic-semihosting` (with the `exit` - feature) prints the message and exits QEMU non-zero. +- Panic (failed `assert!`/`expect`) → on ARM, `panic-semihosting` (with the `exit` + feature) prints and exits QEMU non-zero; on RISC-V, the crate's `#[panic_handler]` + does the same via `riscv-semihosting`. > Note: `cargo run … | tee` masks the exit code unless `pipefail` is set. CI runs steps under `bash -eo pipefail`, so a panic still fails the job. @@ -112,7 +116,7 @@ parses `STACK_HWM_BYTES` from the run output, and fails if either exceeds ```sh cargo run -p embedded-smoke --release --target thumbv6m-none-eabi | tee smoke.txt -bash ci/check_budgets.sh thumbv6m-none-eabi smoke.txt +bash ci/check_budgets.sh embedded-smoke thumbv6m-none-eabi smoke.txt ``` Intentional growth must bump `ci/budgets.toml` in the same PR so it is visible diff --git a/tools/embedded-smoke/build.rs b/tools/embedded-smoke/build.rs index 40eebf8..deabb29 100644 --- a/tools/embedded-smoke/build.rs +++ b/tools/embedded-smoke/build.rs @@ -1,8 +1,9 @@ //! Select the linker memory map for the target chip and put it where cortex-m-rt's //! `link.x` (which does `INCLUDE memory.x`) will find it. //! -//! thumbv6m (Cortex-M0) -> memory/cortex-m0.x (nRF51/micro:bit: 256K flash @ 0x0, 16K RAM) -//! everything else -> memory/cortex-m4.x (netduinoplus2: 256K flash @ 0x08000000, 64K RAM) +//! thumbv6m (Cortex-M0) -> memory/cortex-m0.x (nRF51/micro:bit: 256K flash @ 0x0, 16K RAM) +//! everything else -> memory/cortex-m4.x (netduinoplus2: 256K flash @ 0x08000000, 64K RAM) +//! riscv32* -> memory/riscv32-virt.x (QEMU virt, -bios none) //! //! The chosen file is copied to OUT_DIR/memory.x and OUT_DIR is added to the linker //! search path. There is deliberately NO memory.x at the repo root: a CWD memory.x @@ -15,10 +16,10 @@ use std::path::PathBuf; fn main() { // TARGET is always set by cargo for build scripts; default to the M4 map otherwise. let target = env::var("TARGET").unwrap_or_default(); - let map = if target.starts_with("thumbv6m") { - "memory/cortex-m0.x" - } else { - "memory/cortex-m4.x" + let map = match target.as_str() { + t if t.starts_with("riscv32") => "memory/riscv32-virt.x", + t if t.starts_with("thumbv6m") => "memory/cortex-m0.x", + _other => "memory/cortex-m4.x", }; let out = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is set by cargo")); @@ -28,5 +29,6 @@ fn main() { println!("cargo:rustc-link-search={}", out.display()); println!("cargo:rerun-if-changed=memory/cortex-m4.x"); println!("cargo:rerun-if-changed=memory/cortex-m0.x"); + println!("cargo:rerun-if-changed=memory/riscv32-virt.x"); println!("cargo:rerun-if-changed=build.rs"); } diff --git a/tools/embedded-smoke/memory/riscv32-virt.x b/tools/embedded-smoke/memory/riscv32-virt.x new file mode 100644 index 0000000..31d6430 --- /dev/null +++ b/tools/embedded-smoke/memory/riscv32-virt.x @@ -0,0 +1,12 @@ +/* QEMU virt DRAM; -bios none loads the kernel here. */ +MEMORY +{ + RAM : ORIGIN = 0x80000000, LENGTH = 256K +} + +REGION_ALIAS("REGION_TEXT", RAM); +REGION_ALIAS("REGION_RODATA", RAM); +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); diff --git a/tools/embedded-smoke/src/main.rs b/tools/embedded-smoke/src/main.rs index c200bc9..411c97a 100644 --- a/tools/embedded-smoke/src/main.rs +++ b/tools/embedded-smoke/src/main.rs @@ -26,9 +26,26 @@ #![no_std] #![no_main] -use cortex_m_rt::entry; -use cortex_m_semihosting::{debug, hprintln}; -use panic_semihosting as _; +#[cfg(target_arch = "arm")] +use { + cortex_m_rt::entry, + cortex_m_semihosting::{debug, hprintln}, + panic_semihosting as _, +}; + +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +use { + riscv_rt::entry, + riscv_semihosting::{debug, hprintln}, +}; + +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + let _ = hprintln!("{}", info); + debug::exit(debug::EXIT_FAILURE); + loop {} +} mod checks; mod fixtures;