Skip to content

Commit 1921b5e

Browse files
authored
Merge pull request #170 from rust-embedded/semihosting
add `riscv-semihosting`
2 parents 0f8305b + 4dc63f6 commit 1921b5e

File tree

14 files changed

+899
-7
lines changed

14 files changed

+899
-7
lines changed

.github/workflows/changelog.yaml

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ jobs:
1919
filters: |
2020
riscv:
2121
- 'riscv/**'
22-
riscv-rt:
23-
- 'riscv-rt/**'
2422
riscv-pac:
2523
- 'riscv-pac/**'
24+
riscv-rt:
25+
- 'riscv-rt/**'
26+
riscv-semihosting:
27+
- 'riscv-semihosting/**'
2628
2729
- name: Check for CHANGELOG.md (riscv)
2830
if: steps.changes.outputs.riscv == 'true'
@@ -32,6 +34,14 @@ jobs:
3234
skipLabels: 'skip changelog'
3335
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv/CHANGELOG.md file.'
3436

37+
- name: Check for CHANGELOG.md (riscv-pac)
38+
if: steps.changes.outputs.riscv-pac == 'true'
39+
uses: dangoslen/changelog-enforcer@v3
40+
with:
41+
changeLogPath: ./riscv-pac/CHANGELOG.md
42+
skipLabels: 'skip changelog'
43+
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-pac/CHANGELOG.md file.'
44+
3545
- name: Check for CHANGELOG.md (riscv-rt)
3646
if: steps.changes.outputs.riscv-rt == 'true'
3747
uses: dangoslen/changelog-enforcer@v3
@@ -40,10 +50,10 @@ jobs:
4050
skipLabels: 'skip changelog'
4151
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-rt/CHANGELOG.md file.'
4252

43-
- name: Check for CHANGELOG.md (riscv-pac)
44-
if: steps.changes.outputs.riscv-pac == 'true'
53+
- name: Check for CHANGELOG.md (riscv-semihosting)
54+
if: steps.changes.outputs.riscv-semihosting == 'true'
4555
uses: dangoslen/changelog-enforcer@v3
4656
with:
47-
changeLogPath: ./riscv-pac/CHANGELOG.md
57+
changeLogPath: ./riscv-semihosting/CHANGELOG.md
4858
skipLabels: 'skip changelog'
49-
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-pac/CHANGELOG.md file.'
59+
missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-semihosting/CHANGELOG.md file.'
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
on:
2+
push:
3+
branches: [ master ]
4+
pull_request:
5+
merge_group:
6+
7+
name: Build check (riscv-semihosting)
8+
9+
jobs:
10+
# We check that the crate builds and links for all the toolchains and targets.
11+
build-riscv:
12+
strategy:
13+
matrix:
14+
# All generated code should be running on stable now, MRSV is 1.60.0
15+
toolchain: [ stable, nightly, 1.60.0 ]
16+
target:
17+
- riscv32i-unknown-none-elf
18+
- riscv32imc-unknown-none-elf
19+
- riscv32imac-unknown-none-elf
20+
- riscv64imac-unknown-none-elf
21+
- riscv64gc-unknown-none-elf
22+
include:
23+
# Nightly is only for reference and allowed to fail
24+
- toolchain: nightly
25+
experimental: true
26+
runs-on: ubuntu-latest
27+
continue-on-error: ${{ matrix.experimental || false }}
28+
steps:
29+
- uses: actions/checkout@v4
30+
- uses: dtolnay/rust-toolchain@master
31+
with:
32+
toolchain: ${{ matrix.toolchain }}
33+
targets: ${{ matrix.target }}
34+
- name: Build (M-mode)
35+
run: cargo build --package riscv-semihosting --target ${{ matrix.target }}
36+
- name: Build (U-mode)
37+
run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=u-mode
38+
- name: Build (no semihosting)
39+
run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=no-semihosting
40+
41+
# On MacOS, Ubuntu, and Windows, we at least make sure that the crate builds and links.
42+
build-others:
43+
strategy:
44+
matrix:
45+
os: [ macos-latest, ubuntu-latest, windows-latest ]
46+
runs-on: ${{ matrix.os }}
47+
steps:
48+
- uses: actions/checkout@v3
49+
- uses: dtolnay/rust-toolchain@stable
50+
- name: Build (no features)
51+
run: cargo build --package riscv-semihosting
52+
- name: Build (all features)
53+
run: cargo build --package riscv-semihosting --all-features
54+
55+
# Job to check that all the builds succeeded
56+
build-check:
57+
needs:
58+
- build-riscv
59+
- build-others
60+
runs-on: ubuntu-latest
61+
if: always()
62+
steps:
63+
- run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ members = [
44
"riscv",
55
"riscv-pac",
66
"riscv-rt",
7+
"riscv-semihosting",
78
]

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This repository contains various crates useful for writing Rust programs on RISC
55
* [`riscv`]: CPU registers access and intrinsics
66
* [`riscv-pac`]: Common traits to be implemented by RISC-V PACs
77
* [`riscv-rt`]: Startup code and interrupt handling
8-
8+
* [`riscv-semihosting`]: Semihosting for RISC-V processors
99

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

@@ -22,6 +22,8 @@ Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises
2222
to intervene to uphold that code of conduct.
2323

2424
[`riscv`]: https://crates.io/crates/riscv
25+
[`riscv-pac`]: https://crates.io/crates/riscv-pac
2526
[`riscv-rt`]: https://crates.io/crates/riscv-rt
27+
[`riscv-semihosting`]: https://crates.io/crates/riscv-semihosting
2628
[team]: https://github.com/rust-embedded/wg#the-risc-v-team
2729
[CoC]: CODE_OF_CONDUCT.md

riscv-semihosting/CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file.
4+
This project adheres to [Semantic Versioning](http://semver.org/).
5+
6+
## [Unreleased]
7+
8+
- Moved to the `riscv` Cargo workspace
9+
- Bring in API changes from
10+
[cortex-m-semihosting](https://github.com/rust-embedded/cortex-m/tree/master/cortex-m-semihosting),
11+
including:
12+
- Addition of the `hprint`, `hprintln`, `heprint`, `heprintln`, and `dbg`
13+
macros.
14+
- `hprint` and `heprintln` print to host stdout without and with a
15+
newline, respectively.
16+
- `heprint` and `heprintln` do the same, except to host stderr.
17+
- `dbg` works exactly like
18+
[`std::dbg`](https://doc.rust-lang.org/std/macro.dbg.html).
19+
- `HStdout` and `HStderr` have been combined into `HostStream`.
20+
- `inline-asm` feature removed, switched to stabilized inline asm and MSRV
21+
bumped to 1.59.0
22+
- Clean up documentation, removing unnecessary references to
23+
cortex-m-semihosting and improving clarity.
24+
- Added GitHub Actions CI
25+
- Add features to select the privilege level the semihosting operations will be
26+
started from
27+
28+
## [v0.0.1] - 2018-02-27
29+
30+
- Initial release
31+
32+
[Unreleased]: https://github.com/riscv-rust/riscv-semihosting/compare/cb1afe4002d576b87bfd4c199f42a43815984ce4..HEAD
33+
[v0.0.1]: https://github.com/riscv-rust/riscv-semihosting/tree/cb1afe4002d576b87bfd4c199f42a43815984ce4

riscv-semihosting/Cargo.toml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
authors = [
3+
"The Cortex-M Team <[email protected]>",
4+
"Jorge Aparicio <[email protected]>",
5+
"The RISC-V Team <[email protected]>",
6+
]
7+
description = "Semihosting for RISCV processors"
8+
documentation = "https://docs.rs/riscv-semihosting"
9+
keywords = ["semihosting", "riscv"]
10+
categories = ["no-std", "embedded"]
11+
license = "MIT OR Apache-2.0"
12+
name = "riscv-semihosting"
13+
readme = "README.md"
14+
repository = "https://github.com/riscv-rust/riscv"
15+
version = "0.0.1"
16+
edition = "2021"
17+
rust-version = "1.60.0"
18+
19+
[features]
20+
u-mode = []
21+
jlink-quirks = []
22+
no-semihosting = []
23+
default = ["jlink-quirks"]
24+
25+
[dependencies]
26+
critical-section = "1.0.0"
27+
riscv = {path = "../riscv", version = "0.10.1"}

riscv-semihosting/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
[![crates.io](https://img.shields.io/crates/d/riscv-semihosting.svg)](https://crates.io/crates/riscv-semihosting)
2+
[![crates.io](https://img.shields.io/crates/v/riscv-semihosting.svg)](https://crates.io/crates/riscv-semihosting)
3+
4+
# `riscv-semihosting`
5+
6+
> Semihosting for RISC-V processors
7+
8+
This is a fork of the
9+
[cortex-m-semihosting](https://docs.rs/cortex-m-semihosting) crate with changes
10+
to support the RISC-V Semihosting Specification as documented
11+
[here](https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc)
12+
13+
This crate can (almost) be used in exactly the same way as cortex-m-semihosting,
14+
simply by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`.
15+
Given this, the
16+
[`cortex-m-semihosting documentation`](https://docs.rs/cortex-m-semihosting) is
17+
generally sufficient for using this library.
18+
19+
A major difference between this library and `cortex-m-semihosting` is that there
20+
are mandatory features to choose the privilege level at which the semihosting
21+
calls are executed. The *machine-mode (M-mode)* feature will cause the macros in `export`
22+
to execute the semihosting operation in an interrupt-free context, while
23+
*user-mode (U-mode)* causes them to just execute the operation.
24+
By default, M-mode is used. You can activate the U-mode via the `u-mode` feature.
25+
26+
27+
# Minimum Supported Rust Version (MSRV)
28+
29+
This crate is guaranteed to compile on stable Rust 1.60.0 and up. It **won't**
30+
compile with older versions.
31+
32+
## License
33+
34+
Copyright 2018-2023 [RISC-V team][team]
35+
36+
Permission to use, copy, modify, and/or distribute this software for any purpose
37+
with or without fee is hereby granted, provided that the above copyright notice
38+
and this permission notice appear in all copies.
39+
40+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
41+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
42+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
43+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
44+
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
45+
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
46+
THIS SOFTWARE.
47+
48+
### Contribution
49+
50+
Unless you explicitly state otherwise, any contribution intentionally submitted
51+
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
52+
dual licensed as above, without any additional terms or conditions.
53+
54+
## Code of Conduct
55+
56+
Contribution to this crate is organized under the terms of the [Rust Code of
57+
Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises
58+
to intervene to uphold that code of conduct.
59+
60+
[CoC]: ../CODE_OF_CONDUCT.md
61+
[team]: https://github.com/rust-embedded/wg#the-risc-v-team

riscv-semihosting/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use std::env;
2+
3+
fn main() {
4+
let target = env::var("TARGET").unwrap();
5+
6+
if target.starts_with("riscv") {
7+
println!("cargo:rustc-cfg=riscv");
8+
}
9+
}

riscv-semihosting/src/debug.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//! Interacting with debugging agent
2+
//!
3+
//! # Example
4+
//!
5+
//! This example will show how to terminate the QEMU session. The program
6+
//! should be running under QEMU with semihosting enabled
7+
//! (use `-semihosting` flag).
8+
//!
9+
//! Target program:
10+
//!
11+
//! ```no_run
12+
//! use riscv_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE};
13+
//!
14+
//! if 2 == 2 {
15+
//! // report success
16+
//! debug::exit(EXIT_SUCCESS);
17+
//! } else {
18+
//! // report failure
19+
//! debug::exit(EXIT_FAILURE);
20+
//! }
21+
//!```
22+
23+
/// This values are taken from section 5.5.2 of
24+
/// ADS Debug Target Guide (DUI0058).
25+
// TODO document
26+
#[allow(missing_docs)]
27+
pub enum Exception {
28+
// Hardware reason codes
29+
BranchThroughZero = 0x20000,
30+
UndefinedInstr = 0x20001,
31+
SoftwareInterrupt = 0x20002,
32+
PrefetchAbort = 0x20003,
33+
DataAbort = 0x20004,
34+
AddressException = 0x20005,
35+
IRQ = 0x20006,
36+
FIQ = 0x20007,
37+
// Software reason codes
38+
BreakPoint = 0x20020,
39+
WatchPoint = 0x20021,
40+
StepComplete = 0x20022,
41+
RunTimeErrorUnknown = 0x20023,
42+
InternalError = 0x20024,
43+
UserInterruption = 0x20025,
44+
ApplicationExit = 0x20026,
45+
StackOverflow = 0x20027,
46+
DivisionByZero = 0x20028,
47+
OSSpecific = 0x20029,
48+
}
49+
50+
/// Status enum for `exit` syscall.
51+
pub type ExitStatus = Result<(), ()>;
52+
53+
/// Successful execution of a program.
54+
pub const EXIT_SUCCESS: ExitStatus = Ok(());
55+
56+
/// Unsuccessful execution of a program.
57+
pub const EXIT_FAILURE: ExitStatus = Err(());
58+
59+
/// Reports to the debugger that the execution has completed.
60+
///
61+
/// This call can be used to terminate QEMU session and report back success
62+
/// or failure. If you need to pass more than one type of error, consider
63+
/// using `report_exception` syscall instead.
64+
///
65+
/// This call should not return. However, it is possible for the debugger
66+
/// to request that the application continue. In that case this call
67+
/// returns normally.
68+
///
69+
pub fn exit(status: ExitStatus) {
70+
match status {
71+
EXIT_SUCCESS => report_exception(Exception::ApplicationExit),
72+
EXIT_FAILURE => report_exception(Exception::RunTimeErrorUnknown),
73+
}
74+
}
75+
76+
/// Report an exception to the debugger directly.
77+
///
78+
/// Exception handlers can use this SWI at the end of handler chains
79+
/// as the default action, to indicate that the exception has not been handled.
80+
///
81+
/// This call should not return. However, it is possible for the debugger
82+
/// to request that the application continue. In that case this call
83+
/// returns normally.
84+
///
85+
/// # Arguments
86+
///
87+
/// * `reason` - A reason code reported back to the debugger.
88+
///
89+
pub fn report_exception(reason: Exception) {
90+
let code = reason as usize;
91+
unsafe {
92+
syscall1!(REPORT_EXCEPTION, code);
93+
}
94+
}

0 commit comments

Comments
 (0)