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)
**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;