feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+1
View File
@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"d5639e0ccff99ece86647ee799776bc13c772245fc7676e187f17f8946f69bf5","CHANGELOG.md":"5f724774f6a842b7af07821e98d0e6da6e9c3a8f0ee5060f27aa4154e1d59079","Cargo.toml":"1652f14e233879d7990cfec1c7964046b03991aebd7300a2eaceab3349c1f31c","Cargo.toml.orig":"7b7aef90cf980fff522c0954e98c1f81143d0e06972992aaef2a161be4cd66d6","README.md":"ec60f7fb440c8bc0411baedfae0958a025421ea16096b7b7731b3b41c46fc5f1","build.rs":"5598bbfd52d8b56e88aa6a6533707ed8d9c59e3fd40ca4192c619296b3adb4ae","src/asm.rs":"effcd5b93608aa429b481db6b3e4c0e3e67e3f1f769fa5a9f95dcee2821e513a","src/bits.rs":"3f32d500212f869d3f06545416919161d111a1fc845f18fe45548a0820d01b41","src/critical_section.rs":"1769ed6055502d377c5c5f4449e230ae7558ce34acc0a77fe6ed64cf391795c0","src/delay.rs":"8e91f278b667ca7094d3d40ca9e180d18e164eb8c6f6654562b7aa96b5534a8f","src/interrupt.rs":"4f5e555d4ffa6db3cb230ef6163ce63427988431a2367a26152ee2fcc3388a3b","src/interrupt/machine.rs":"d021dba7834f055a4c85614dcf7df06b8acab24068bd78aeae5b00c2a1a31e78","src/interrupt/supervisor.rs":"e936e44a50dcb79eb673784d6018dae4f39a77a52a22fefe4273092b20492a43","src/lib.rs":"5153edc34d970ac5beaf7ffe0cf483ba77a04af70165e08ffb391e0bd08e3683","src/macros.rs":"ea6625a15be6a9ec2f9fa9a7134d1bc93bd1f25b5d3435141325ffb697dcb055","src/register.rs":"7411abd46da09f5661b026b4deaa5f8396c018a8e08eb95dbddd7324d114b243","src/register/cycle.rs":"a2347cd9cbe2d90cc65b4e71b6815d736cf31a8ea22a18e4c843b1722d11eca8","src/register/cycleh.rs":"38625bf272a0b1c2a364e90f1639835ffc40dbfbad6df144a9f2b49573b0a1d5","src/register/hpmcounterx.rs":"d7b1c69d10e5c76427965b4c8f9139dac2aa7a5a1eb8c032d664244ec6ccbdcb","src/register/instret.rs":"a1469afa9876a27f2cb129f174a5d5f25f81529be77a86a534a57cbc00157e60","src/register/instreth.rs":"5f61fd655029ce9cf781225d4fe99c4319f8c7685b2e6b9524845721bf2b48e8","src/register/macros.rs":"18319ea8cfdc97621d5f6901503e18b2037ca8a5fece424d97a0fde6482e826f","src/register/marchid.rs":"2f1ac5d12ba5fe76e2598e4a7a964c6c40eb357fbdc966d436484edac6fa2232","src/register/mcause.rs":"dd755f10361133c05378bd7593d7f434c1a80b47b0e854c7ce60e3980d71db76","src/register/mcounteren.rs":"82cc36f869fef57948bcd7e46f4c2d79327aca2013ddb29729f9f2c4f9077626","src/register/mcountinhibit.rs":"83db95df4249e738583842d65f3c65b30fb55398262539509789e482423ce94d","src/register/mcycle.rs":"f390d208a7b42841f3f1ff73741f822ea3f2808861f127b90a72d18990d30138","src/register/mcycleh.rs":"d519a131218baaab010ebfd35e96372b28f6e87c1135a1bc55dcd3fe90a1ee01","src/register/medeleg.rs":"5407b033c4d169125a768f9171f169ddbc17e0298cad9e3272875ce2c4485316","src/register/mepc.rs":"6579dcb903788605c02e963de8aca3e95678eba526e009e0ef037facb1e15bdb","src/register/mhartid.rs":"20306fee750b115d09539c897dc741c220048e763ca9e7fbd17a56bf362c2935","src/register/mhpmcounterx.rs":"fb08a4bc3a13ac148540bd40b321468e2516d4b8498181b904076c4458bfba65","src/register/mhpmeventx.rs":"0e92dd9a157d8faff8c44b99a4e4225a49f8b721b400b3d091fd5f4238443259","src/register/mideleg.rs":"bf19c0f501703102870e0ecbc8889f603349e0a8c40330dd86e82c03188db3fe","src/register/mie.rs":"c9631af837fdb0cec860b040d40cd2643118c2fa1a20a47fe4496a79f1d88a8d","src/register/mimpid.rs":"5b028ac73ca7ac721d82fac984af3fdf50cf5cef2dd24549ac05d1b04e4b2e7e","src/register/minstret.rs":"530c2ac2fb5fb79a4645ea2c80045148a233d0e69f9a9f441e41113cea634eae","src/register/minstreth.rs":"f623afd2a281175b89670491540d1f2dea0c8baab6742c3ba71865f189c1965e","src/register/mip.rs":"a21c08a83e098408e3c140de291ecb282be9bec95ea6ab6d736ddc0bbdbab976","src/register/misa.rs":"a6cbd1d8188e30b7bd3f8fa6ce08ac8d749abb463036ca8b6e586942bba4a77b","src/register/mscratch.rs":"f94cd3a81d2864b05e7801fbb604d510a0ff1698dffdbebe0f893f7ec648cd27","src/register/mstatus.rs":"78f82ffb13277a33317218f154c312524d2d4f59f89bdd2fdb201f16aa4c1684","src/register/mstatush.rs":"b4e4eab941b4933b45351b5c7db4b4187a2852df46a2a6ebb0d324f21d56a412","src/register/mtval.rs":"f0c68cd85c9af1131850a8b2595abe9e12d07471b51a95d7218f50d894428b42","src/register/mtvec.rs":"0e6f6837d5b672b1f586b2d68fb33d1456b033338c990bf9ec93b56293a27d69","src/register/mvendorid.rs":"961e66a85ea67b6795874f61852e195f96effdeb1c24b060f8a45e2072602e33","src/register/pmpaddrx.rs":"402a915899f770562cbf2dd56a7631fef6da0a16cfe6559ef4b956d40ce7e45d","src/register/pmpcfgx.rs":"d7f52709214e31c757204290d2f7a66d656de9ef823e60d280f69f1f939a288e","src/register/satp.rs":"92d69742a930150d01de73f3e102feae0570ba1460c569437069d14f735f2713","src/register/scause.rs":"73049ce71f15df9a536f31a9cae54a6b78c71832c58b2a4a655fcebb527a7d20","src/register/scounteren.rs":"86fd53bc455f0c691da59910daad7ffa2ad53ed881015c9bc98bd9ec96ed5d82","src/register/sepc.rs":"c70602e54be39cc5322b7521dfe22d0b70dc1ad8f264cae98f9d3dadbc59cd2b","src/register/sie.rs":"30c5add548f48c014647eda99548ecfdb89a89a271abcc4310672f362558791e","src/register/sip.rs":"a93ba2a6457b99192ac57d6cd3fd4fbf7c2ccc49afdac11b73c4a9c37324f380","src/register/sscratch.rs":"35123597ef3fcc765f305eb29f66c65f0e038f8b74ae73e2fecdb1c6d614672a","src/registerLine truncated
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "bc1ef18ee3a16906488270b6bd91a6a9bd6d1bb1"
},
"path_in_vcs": "riscv"
}
+200
View File
@@ -0,0 +1,200 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## [v0.12.1] - 2024-10-20
### Changed
- Update critical-section to 1.2.0
## [v0.12.0] - 2024-10-19
### Added
- `riscv-macros` crate for `riscv-pac` enums.
- Bump MSRV to 1.61.
- Implementation of `riscv-pac` traits for `Interrupt` and `Exception` enums.
- Tests for the `riscv-pac` trait implementations of `Interrupt` and `Exception` enums.
- Add `Mcause::from(usize)` for use in unit tests
- Add `Mstatus::from(usize)` for use in unit tests
- Add `Mstatus.bits()`
- Add `Eq` and `PartialEq` for `pmpcfgx::{Range, Permission}`
- Add `Mstatus::update_*` helpers to manipulate Mstatus values without touching
the CSR
- Export `riscv::register::macros` module macros for external use
- Add `riscv::register::mcountinhibit` module for `mcountinhibit` CSR
- Add `Mcounteren` in-memory update functions
- Add `Mstatus` vector extension support
- Add fallible counterparts to all functions that `panic`
- Add `riscv-pac` as a dependency
- Add CSR-defining macros to create in-memory types
### Fixed
- Fixed `sip::set_ssoft` and `sip::clear_ssoft` using wrong address
- Fixed assignment in `mstatus` unit tests.
- delay implementation does not use binary labels in inline assembly.
## [v0.11.1] - 2024-02-15
### Changed
- Made `asm::wfi`, `fence`, `fence_i` and `sfence` safe (ie, removed `unsafe` from their definitions)
- Made `cfg` variable selection more robust for custom targets
## [v0.11.0] - 2024-01-14
### Added
- Add `asm::ecall()`, a wrapper for implementing an `ecall` instruction
- Add `nested` function for nested ISRs in `interrupt::machine` and `interrupt::supervisor`
- `s-mode` feature for reexporting `interrupt::machine` or `interrupt::supervisor` to `interrupt`
- Support for supervisor-level interrupts in `interrupt::supervisor`
- Add CI workflow to check that CHANGELOG.md file has been modified in PRs
- Add `read_csr_as_rv32`, `set_rv32`, and `clear_rv32` macros
- Add `mstatus::uxl` and `mstatus::sxl`
- Add `mstatus::ube`, `mstatus::sbe`, and `mstatus::mbe` endianness bit fields
- Add `mstatush` registers (RISCV-32 only)
- Add `asm::fence()`, a wrapper for implementing a `fence` instruction
- Add `asm::fence_i()`, a wrapper for implementing a `fence.i` instruction
- Add `TryFrom` implementation for `mcause::{Interrupt, Exception}` and `scause::{Interrupt, Exception}`
### Changed
- Cargo workspace for riscv and riscv-rt
- Update `embedded-hal` dependency to v1.0.0 (bumps MSRV to 1.60)
- `misa::MXL` renamed to `misa::XLEN`
- Removed `bit_field` dependency
- CI actions updated. They now use `checkout@v3` and `dtolnay/rust-toolchain`.
- `mcause::{Interrupt, Exception}` and `scause::{Interrupt, Exception}` now implement `From` trait for `usize`
- Set safety of `asm::nop` and `asm::delay` functions to safe.
### Fixed
- Fix `scause::Exception` missing `LoadMisaligned`
- Fix `scause::Exception` missing `SupervisorEnvCall`
- Removed user-level interrupts from `mcause::Interrupt` and `scause::Interrupt`
- Removed user-level interrupts from `mstatus`
- Removed machine environment call delegation from `medeleg`
- Removed user-level interrupts from machine and supervisor mode interrupt-related registers.
### Removed
- User mode registers removed, as they are no longer supported in RISC-V
- FCSR register operations removed to avoid UB (#148)
## [v0.10.1] - 2023-01-18
### Fixed
- Fix implementation for `SingleHartCriticalSection`
## [v0.10.0] - 2022-11-09
### Added
- `critical-section-single-hart` feature which provides an implementation for the `critical_section` crate for single-hart systems, based on disabling all interrupts.
## [v0.9.0] - 2022-10-06
### Fixed
- Fix `asm::delay()` to ensure count register is always reloaded
- Fix reading marchid and mimpid (#107)
### Removed
- `set_msoft`, `clear_msoft`, `set_mtimer` and `clear_mtimer` removed as part of fixing issue #62
## [v0.8.0] - 2022-04-20
### Added
- Add `#[cfg(riscv32)]` to `pmpcfg1` and `pmpcfg3` modules
- Add enums `Range`, `Permission` for PMP configuration
- Add `set_pmp()` and `clear_pmp()` functions to pmpcfg(x) modules
- Add struct `Pmpcsr` and is returned from `pmpcfgx::read()`
- Add `singleton!` macro
- Add delay structure and methods using embedded-hal traits and `mcycle` register
- Add `asm::delay()` function for assembly-based busy-loops
- Add `asm::nop()`, a wrapper for implementing a `nop` instruction
- Add missing `#[inline]` attribute to register reads, type conversations and `interrupt::free`
### Changed
- Use new `asm!` instead of `llvm_asm!`
- Change `pmpcfgx::read()` macro to `read_csr_as!()` from `read_csr_as_usize!()`
- Inline assembly is now always used
- Update Minimum Supported Rust Version to 1.59
### Fixed
- Fix `sfence.vma` operand order
### Removed
- Remove `inline-asm` feature which is now always enabled
## [v0.7.0] - 2021-07-29
### Added
- Add `medeleg` register
- Add `cycle[h]`, `instret[h]` and `mcounteren`
- Add additional binaries for floating-point ABIs
- Add support for `mxr`
- Add support for `mprv`
### Changed
- Fix `scause::set`
- Various formatting and comment fixes
- Update `bare-metal` to `v1.0.0` removing `Nr` trait
- Build targets on `docs.rs` are now RISC-V targets other than default ones
## [v0.6.0] - 2020-06-20
### Changed
- `Mtvec::trap_mode()`, `Stvec::trap_mode()` and `Utvec::trap_mode()` functions now return `Option<TrapMode>` (breaking change)
- Updated Minimum Supported Rust Version to 1.42.0
- Use `llvm_asm!` instead of `asm!`
### Removed
- vexriscv-specific registers were moved to the `vexriscv` crate
## [v0.5.6] - 2020-03-14
### Added
- Added vexriscv-specific registers
## [v0.5.5] - 2020-02-28
### Added
- Added `riscv32i-unknown-none-elf` target support
- Added user trap setup and handling registers
- Added write methods for the `mip` and `satp` registers
- Added `mideleg` register
- Added Changelog
### Changed
- Fixed MSRV by restricting the upper bound of `bare-metal` version
[Unreleased]: https://github.com/rust-embedded/riscv/compare/v0.10.1...HEAD
[v0.10.1]: https://github.com/rust-embedded/riscv/compare/v0.10.0...v0.10.1
[v0.10.0]: https://github.com/rust-embedded/riscv/compare/v0.9.0...v0.10.0
[v0.9.0]: https://github.com/rust-embedded/riscv/compare/v0.8.0...v0.9.0
[v0.8.0]: https://github.com/rust-embedded/riscv/compare/v0.7.0...v0.8.0
[v0.7.0]: https://github.com/rust-embedded/riscv/compare/v0.6.0...v0.7.0
[v0.6.0]: https://github.com/rust-embedded/riscv/compare/v0.5.6...v0.6.0
[v0.5.6]: https://github.com/rust-embedded/riscv/compare/v0.5.5...v0.5.6
[v0.5.5]: https://github.com/rust-embedded/riscv/compare/v0.5.4...v0.5.5
+73
View File
@@ -0,0 +1,73 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.61"
name = "riscv"
version = "0.12.1"
authors = ["The RISC-V Team <risc-v@teams.rust-embedded.org>"]
build = "build.rs"
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Low level access to RISC-V processors"
documentation = "https://docs.rs/riscv"
readme = "README.md"
keywords = [
"riscv",
"register",
"peripheral",
]
categories = [
"embedded",
"hardware-support",
"no-std",
]
license = "ISC"
repository = "https://github.com/rust-embedded/riscv"
[package.metadata.docs.rs]
all-features = true
default-target = "riscv64imac-unknown-none-elf"
targets = [
"riscv32i-unknown-none-elf",
"riscv32imc-unknown-none-elf",
"riscv32imac-unknown-none-elf",
"riscv64imac-unknown-none-elf",
"riscv64gc-unknown-none-elf",
]
[lib]
name = "riscv"
path = "src/lib.rs"
[dependencies.critical-section]
version = "1.2.0"
[dependencies.embedded-hal]
version = "1.0.0"
[dependencies.paste]
version = "1.0.15"
[dependencies.riscv-macros]
version = "0.1.0"
optional = true
[dependencies.riscv-pac]
version = "0.2.0"
[features]
critical-section-single-hart = ["critical-section/restore-state-bool"]
default = ["riscv-macros"]
s-mode = []
+40
View File
@@ -0,0 +1,40 @@
[![crates.io](https://img.shields.io/crates/d/riscv.svg)](https://crates.io/crates/riscv)
[![crates.io](https://img.shields.io/crates/v/riscv.svg)](https://crates.io/crates/riscv)
# `riscv`
> Low level access to RISC-V processors
This project is developed and maintained by the [RISC-V team][team].
## [Documentation](https://docs.rs/crate/riscv)
## Minimum Supported Rust Version (MSRV)
This crate is guaranteed to compile on stable Rust 1.61 and up. It *might*
compile with older versions but that may change in any new patch release.
## License
Copyright 2019-2022 [RISC-V team][team]
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
## Code of Conduct
Contribution to this crate is organized under the terms of the [Rust Code of
Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises
to intervene to uphold that code of conduct.
[CoC]: CODE_OF_CONDUCT.md
[team]: https://github.com/rust-embedded/wg#the-risc-v-team
+17
View File
@@ -0,0 +1,17 @@
use std::env;
fn main() {
println!("cargo:rustc-check-cfg=cfg(riscv)");
println!("cargo:rustc-check-cfg=cfg(riscv32)");
println!("cargo:rustc-check-cfg=cfg(riscv64)");
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
if target_arch == "riscv32" {
println!("cargo:rustc-cfg=riscv");
println!("cargo:rustc-cfg=riscv32");
} else if target_arch == "riscv64" {
println!("cargo:rustc-cfg=riscv");
println!("cargo:rustc-cfg=riscv64");
}
}
+163
View File
@@ -0,0 +1,163 @@
//! Assembly instructions
macro_rules! instruction {
($(#[$attr:meta])*, unsafe $fnname:ident, $asm:expr) => (
$(#[$attr])*
#[inline]
pub unsafe fn $fnname() {
match () {
#[cfg(riscv)]
() => core::arch::asm!($asm),
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
);
($(#[$attr:meta])*, $fnname:ident, $asm:expr) => (
$(#[$attr])*
#[inline]
pub fn $fnname() {
match () {
#[cfg(riscv)]
() => unsafe { core::arch::asm!($asm) },
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
);
}
instruction!(
/// `nop` instruction wrapper
///
/// The `NOP` instruction does not change any architecturally visible state, except for
/// advancing the pc and incrementing any applicable performance counters.
///
/// This function generates a no-operation; it's useful to prevent delay loops from being
/// optimized away.
, nop, "nop");
instruction!(
/// `EBREAK` instruction wrapper
///
/// Generates a breakpoint exception.
, unsafe ebreak, "ebreak");
instruction!(
/// `WFI` instruction wrapper
///
/// Provides a hint to the implementation that the current hart can be stalled until an interrupt might need servicing.
/// The WFI instruction is just a hint, and a legal implementation is to implement WFI as a NOP.
, wfi, "wfi");
instruction!(
/// `SFENCE.VMA` instruction wrapper (all address spaces and page table levels)
///
/// Synchronizes updates to in-memory memory-management data structures with current execution.
/// Instruction execution causes implicit reads and writes to these data structures; however, these implicit references
/// are ordinarily not ordered with respect to loads and stores in the instruction stream.
/// Executing an `SFENCE.VMA` instruction guarantees that any stores in the instruction stream prior to the
/// `SFENCE.VMA` are ordered before all implicit references subsequent to the `SFENCE.VMA`.
, sfence_vma_all, "sfence.vma");
instruction!(
/// `FENCE` instruction wrapper
///
/// The FENCE instruction is used to order device I/O and memory accesses as viewed by other RISC-V
/// harts and external devices or coprocessors. Any combination of device input (I), device output
/// (O), memory reads (R), and memory writes (W) may be ordered with respect to any combination
/// of the same. Informally, no other RISC-V hart or external device can observe any operation in the
/// successor set following a FENCE before any operation in the predecessor set preceding the FENCE.
/// Chapter 17 provides a precise description of the RISC-V memory consistency model.
///
/// The FENCE instruction also orders memory reads and writes made by the hart as observed by
/// memory reads and writes made by an external device. However, FENCE does not order observations
/// of events made by an external device using any other signaling mechanism.
, fence, "fence");
instruction!(
/// `FENCE.I` instruction wrapper
///
/// Used to synchronize the instruction and data streams. RISC-V does not guarantee that
/// stores to instruction memory will be made visible to instruction fetches on a
/// RISC-V hart until that hart executes a FENCE.I instruction.
///
/// A FENCE.I instruction ensures that a subsequent instruction fetch on a RISC-V hart
/// will see any previous data stores already visible to the same RISC-V hart.
/// FENCE.I does not ensure that other RISC-V harts’ instruction fetches will observe the
/// local hart’s stores in a multiprocessor system. To make a store to instruction memory
/// visible to all RISC-V harts, the writing hart also has to execute a data FENCE before
/// requesting that all remote RISC-V harts execute a FENCE.I.
///
/// The unused fields in the FENCE.I instruction, imm\[11:0\], rs1, and rd, are reserved for
/// finer-grain fences in future extensions. For forward compatibility, base
/// implementations shall ignore these fields, and standard software shall zero these fields.
, fence_i, "fence.i");
/// `SFENCE.VMA` instruction wrapper
///
/// Synchronizes updates to in-memory memory-management data structures with current execution.
/// Instruction execution causes implicit reads and writes to these data structures; however, these implicit references
/// are ordinarily not ordered with respect to loads and stores in the instruction stream.
/// Executing an `SFENCE.VMA` instruction guarantees that any stores in the instruction stream prior to the
/// `SFENCE.VMA` are ordered before all implicit references subsequent to the `SFENCE.VMA`.
#[inline]
#[allow(unused_variables)]
pub unsafe fn sfence_vma(asid: usize, addr: usize) {
match () {
#[cfg(riscv)]
() => core::arch::asm!("sfence.vma {0}, {1}", in(reg) addr, in(reg) asid),
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
/// `ECALL` instruction wrapper
///
/// Generates an exception for a service request to the execution environment.
/// When executed in U-mode, S-mode, or M-mode, it generates an environment-call-from-U-mode
/// exception, environment-call-from-S-mode exception, or environment-call-from-M-mode exception,
/// respectively, and performs no other operation.
///
/// # Note
///
/// The ECALL instruction will **NOT** save and restore the stack pointer, as it triggers an exception.
/// The stack pointer must be saved and restored accordingly by the exception handler.
#[inline]
pub unsafe fn ecall() {
match () {
#[cfg(riscv)]
() => core::arch::asm!("ecall", options(nostack)),
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
/// Blocks the program for *at least* `cycles` CPU cycles.
///
/// This is implemented in assembly so its execution time is independent of the optimization
/// level, however it is dependent on the specific architecture and core configuration.
///
/// NOTE that the delay can take much longer if interrupts are serviced during its execution
/// and the execution time may vary with other factors. This delay is mainly useful for simple
/// timer-less initialization of peripherals if and only if accurate timing is not essential. In
/// any other case please use a more accurate method to produce a delay.
#[inline]
#[allow(unused_variables)]
pub fn delay(cycles: u32) {
match () {
#[cfg(riscv)]
() => unsafe {
let real_cyc = 1 + cycles / 2;
core::arch::asm!(
"2:",
"addi {0}, {0}, -1",
"bne {0}, zero, 2b",
inout(reg) real_cyc => _,
options(nomem, nostack),
)
},
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
+17
View File
@@ -0,0 +1,17 @@
/// Insert a new value into a bitfield
///
/// `value` is masked to `width` bits and inserted into `orig`.`
#[inline]
pub fn bf_insert(orig: usize, bit: usize, width: usize, value: usize) -> usize {
let mask = (1 << width) - 1;
orig & !(mask << bit) | ((value & mask) << bit)
}
/// Extract a value from a bitfield
///
/// Extracts `width` bits from bit offset `bit` and returns it shifted to bit 0.s
#[inline]
pub fn bf_extract(orig: usize, bit: usize, width: usize) -> usize {
let mask = (1 << width) - 1;
(orig >> bit) & mask
}
+29
View File
@@ -0,0 +1,29 @@
use critical_section::{set_impl, Impl, RawRestoreState};
use crate::interrupt;
struct SingleHartCriticalSection;
set_impl!(SingleHartCriticalSection);
unsafe impl Impl for SingleHartCriticalSection {
#[cfg(not(feature = "s-mode"))]
unsafe fn acquire() -> RawRestoreState {
let mut mstatus: usize;
core::arch::asm!("csrrci {}, mstatus, 0b1000", out(reg) mstatus);
core::mem::transmute::<_, crate::register::mstatus::Mstatus>(mstatus).mie()
}
#[cfg(feature = "s-mode")]
unsafe fn acquire() -> RawRestoreState {
let mut sstatus: usize;
core::arch::asm!("csrrci {}, sstatus, 0b0010", out(reg) sstatus);
core::mem::transmute::<_, crate::register::sstatus::Sstatus>(sstatus).sie()
}
unsafe fn release(was_active: RawRestoreState) {
// Only re-enable interrupts if they were enabled before the critical section.
if was_active {
interrupt::enable()
}
}
}
+30
View File
@@ -0,0 +1,30 @@
//! Delay devices and providers
use crate::register::mcycle;
use embedded_hal::delay::DelayNs;
/// Machine mode cycle counter (`mcycle`) as a delay provider
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct McycleDelay {
/// The clock speed of the core, in Hertz
ticks_second: u32,
}
impl McycleDelay {
/// Constructs the delay provider.
/// `ticks_second` should be the clock speed of the core, in Hertz
#[inline]
pub const fn new(ticks_second: u32) -> Self {
Self { ticks_second }
}
}
impl DelayNs for McycleDelay {
#[inline]
fn delay_ns(&mut self, ns: u32) {
let t0 = mcycle::read64();
let ns_64: u64 = ns.into();
let clock = (ns_64 * (self.ticks_second as u64)) / 1_000_000_000u64;
while mcycle::read64().wrapping_sub(t0) <= clock {}
}
}
+98
View File
@@ -0,0 +1,98 @@
//! Interrupts
// NOTE: Adapted from cortex-m/src/interrupt.rs
use crate::result::Result;
// re-export useful riscv-pac traits
pub use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber};
pub mod machine;
pub mod supervisor;
#[cfg(not(feature = "s-mode"))]
pub use machine::*;
#[cfg(feature = "s-mode")]
pub use supervisor::*;
/// Trap Cause.
///
/// This enum represents the cause of a trap. It can be either an interrupt or an exception.
/// The [`mcause`](crate::register::mcause::Mcause::cause) and
/// [`scause`](crate::register::scause::Scause::cause) registers return a value of this type.
/// However, the trap cause is represented as raw numbers. To get a target-specific trap cause,
/// use [`Trap::try_into`] with your target-specific M-Mode or S-Mode trap cause types.
///
/// # Example
///
/// In targets that comply with the RISC-V standard, you can use the standard
/// [`Interrupt`] and [`Exception`] enums to represent the trap cause:
///
/// ```no_run
/// use riscv::interrupt::{Trap, Interrupt, Exception};
/// use riscv::register::mcause;
///
/// let raw_trap: Trap<usize, usize> = mcause::read().cause();
/// let standard_trap: Trap<Interrupt, Exception> = raw_trap.try_into().unwrap();
/// ```
///
/// Targets that do not comply with the RISC-V standard usually have their own interrupt and exceptions.
/// You can find these types in the target-specific PAC. If it has been generated with `svd2rust`,
/// you can use the `pac::interrupt::CoreInterrupt` and `pac::interrupt::Exception` enums:
///
/// ```ignore,no_run
/// use riscv::interrupt::Trap;
/// use pac::interrupt::{CoreInterrupt, Exception}; // pac is the target-specific PAC
///
/// let standard_trap: Trap<CoreInterrupt, Exception> = pac::interrupt::cause();
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Trap<I, E> {
Interrupt(I),
Exception(E),
}
/// Trap Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TrapError {
InvalidInterrupt(usize),
InvalidException(usize),
}
impl Trap<usize, usize> {
/// Converts a target-specific trap cause to a generic trap cause
#[inline]
pub fn from<I: CoreInterruptNumber, E: ExceptionNumber>(trap: Trap<I, E>) -> Self {
match trap {
Trap::Interrupt(interrupt) => Trap::Interrupt(interrupt.number()),
Trap::Exception(exception) => Trap::Exception(exception.number()),
}
}
/// Tries to convert the generic trap cause to a target-specific trap cause
#[inline]
pub fn try_into<I, E>(self) -> Result<Trap<I, E>>
where
I: CoreInterruptNumber,
E: ExceptionNumber,
{
match self {
Trap::Interrupt(code) => Ok(Trap::Interrupt(I::from_number(code)?)),
Trap::Exception(code) => Ok(Trap::Exception(E::from_number(code)?)),
}
}
}
impl<I: CoreInterruptNumber, E: ExceptionNumber> Trap<I, E> {
/// Converts a target-specific trap cause to a generic trap cause
#[inline]
pub fn into(self) -> Trap<usize, usize> {
Trap::from(self)
}
/// Tries to convert the generic trap cause to a target-specific trap cause
#[inline]
pub fn try_from(trap: Trap<usize, usize>) -> Result<Self> {
trap.try_into()
}
}
+274
View File
@@ -0,0 +1,274 @@
use crate::{
interrupt::Trap,
register::{mcause, mepc, mstatus},
};
use riscv_pac::{
result::{Error, Result},
CoreInterruptNumber, ExceptionNumber, InterruptNumber,
};
/// Standard M-mode RISC-V interrupts
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum Interrupt {
SupervisorSoft = 1,
MachineSoft = 3,
SupervisorTimer = 5,
MachineTimer = 7,
SupervisorExternal = 9,
MachineExternal = 11,
}
/// SAFETY: `Interrupt` represents the standard RISC-V interrupts
unsafe impl InterruptNumber for Interrupt {
const MAX_INTERRUPT_NUMBER: usize = Self::MachineExternal as usize;
#[inline]
fn number(self) -> usize {
self as usize
}
#[inline]
fn from_number(value: usize) -> Result<Self> {
match value {
1 => Ok(Self::SupervisorSoft),
3 => Ok(Self::MachineSoft),
5 => Ok(Self::SupervisorTimer),
7 => Ok(Self::MachineTimer),
9 => Ok(Self::SupervisorExternal),
11 => Ok(Self::MachineExternal),
_ => Err(Error::InvalidVariant(value)),
}
}
}
/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts
unsafe impl CoreInterruptNumber for Interrupt {}
/// Standard M-mode RISC-V exceptions
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum Exception {
InstructionMisaligned = 0,
InstructionFault = 1,
IllegalInstruction = 2,
Breakpoint = 3,
LoadMisaligned = 4,
LoadFault = 5,
StoreMisaligned = 6,
StoreFault = 7,
UserEnvCall = 8,
SupervisorEnvCall = 9,
MachineEnvCall = 11,
InstructionPageFault = 12,
LoadPageFault = 13,
StorePageFault = 15,
}
/// SAFETY: `Exception` represents the standard RISC-V exceptions
unsafe impl ExceptionNumber for Exception {
const MAX_EXCEPTION_NUMBER: usize = Self::StorePageFault as usize;
#[inline]
fn number(self) -> usize {
self as usize
}
#[inline]
fn from_number(value: usize) -> Result<Self> {
match value {
0 => Ok(Self::InstructionMisaligned),
1 => Ok(Self::InstructionFault),
2 => Ok(Self::IllegalInstruction),
3 => Ok(Self::Breakpoint),
4 => Ok(Self::LoadMisaligned),
5 => Ok(Self::LoadFault),
6 => Ok(Self::StoreMisaligned),
7 => Ok(Self::StoreFault),
8 => Ok(Self::UserEnvCall),
9 => Ok(Self::SupervisorEnvCall),
11 => Ok(Self::MachineEnvCall),
12 => Ok(Self::InstructionPageFault),
13 => Ok(Self::LoadPageFault),
15 => Ok(Self::StorePageFault),
_ => Err(Error::InvalidVariant(value)),
}
}
}
/// Disables all interrupts in the current hart (machine mode).
#[inline]
pub fn disable() {
// SAFETY: It is safe to disable interrupts
unsafe { mstatus::clear_mie() }
}
/// Enables all the interrupts in the current hart (machine mode).
///
/// # Safety
///
/// Do not call this function inside a critical section.
#[inline]
pub unsafe fn enable() {
mstatus::set_mie()
}
/// Retrieves the cause of a trap in the current hart (machine mode).
///
/// This function expects the target-specific interrupt and exception types.
/// If the raw cause is not a valid interrupt or exception for the target, it returns an error.
#[inline]
pub fn try_cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Result<Trap<I, E>> {
mcause::read().cause().try_into()
}
/// Retrieves the cause of a trap in the current hart (machine mode).
///
/// This function expects the target-specific interrupt and exception types.
/// If the raw cause is not a valid interrupt or exception for the target, it panics.
#[inline]
pub fn cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Trap<I, E> {
try_cause().unwrap()
}
/// Execute closure `f` with interrupts disabled in the current hart (machine mode).
///
/// This method does not synchronise multiple harts, so it is not suitable for
/// using as a critical section. See the `critical-section` crate for a cross-platform
/// way to enter a critical section which provides a `CriticalSection` token.
///
/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
#[inline]
pub fn free<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let mstatus = mstatus::read();
// disable interrupts
disable();
let r = f();
// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
if mstatus.mie() {
unsafe { enable() };
}
r
}
/// Execute closure `f` with interrupts enabled in the current hart (machine mode).
///
/// This method is assumed to be called within an interrupt handler, and allows
/// nested interrupts to occur. After the closure `f` is executed, the [`mstatus`]
/// and [`mepc`] registers are properly restored to their previous values.
///
/// # Safety
///
/// - Do not call this function inside a critical section.
/// - This method is assumed to be called within an interrupt handler.
/// - Make sure to clear the interrupt flag that caused the interrupt before calling
/// this method. Otherwise, the interrupt will be re-triggered before executing `f`.
#[inline]
pub unsafe fn nested<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let mstatus = mstatus::read();
let mepc = mepc::read();
// enable interrupts to allow nested interrupts
enable();
let r = f();
// If the interrupts were inactive before our `enable` call, then re-disable
// them. Otherwise, keep them enabled
if !mstatus.mie() {
disable();
}
// Restore MSTATUS.PIE, MSTATUS.MPP, and SEPC
let mut after_mstatus = mstatus::read();
if mstatus.mpie() {
after_mstatus.set_mpie(mstatus.mpie());
}
after_mstatus.set_mpp(mstatus.mpp());
mstatus::write(after_mstatus);
mepc::write(mepc);
r
}
#[cfg(test)]
mod test {
use super::*;
use Exception::*;
use Interrupt::*;
#[test]
fn test_interrupt() {
assert_eq!(Interrupt::from_number(1), Ok(SupervisorSoft));
assert_eq!(Interrupt::from_number(2), Err(Error::InvalidVariant(2)));
assert_eq!(Interrupt::from_number(3), Ok(MachineSoft));
assert_eq!(Interrupt::from_number(4), Err(Error::InvalidVariant(4)));
assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer));
assert_eq!(Interrupt::from_number(6), Err(Error::InvalidVariant(6)));
assert_eq!(Interrupt::from_number(7), Ok(MachineTimer));
assert_eq!(Interrupt::from_number(8), Err(Error::InvalidVariant(8)));
assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal));
assert_eq!(Interrupt::from_number(10), Err(Error::InvalidVariant(10)));
assert_eq!(Interrupt::from_number(11), Ok(MachineExternal));
assert_eq!(Interrupt::from_number(12), Err(Error::InvalidVariant(12)));
assert_eq!(SupervisorSoft.number(), 1);
assert_eq!(MachineSoft.number(), 3);
assert_eq!(SupervisorTimer.number(), 5);
assert_eq!(MachineTimer.number(), 7);
assert_eq!(SupervisorExternal.number(), 9);
assert_eq!(MachineExternal.number(), 11);
assert_eq!(MachineExternal.number(), Interrupt::MAX_INTERRUPT_NUMBER)
}
#[test]
fn test_exception() {
assert_eq!(Exception::from_number(0), Ok(InstructionMisaligned));
assert_eq!(Exception::from_number(1), Ok(InstructionFault));
assert_eq!(Exception::from_number(2), Ok(IllegalInstruction));
assert_eq!(Exception::from_number(3), Ok(Breakpoint));
assert_eq!(Exception::from_number(4), Ok(LoadMisaligned));
assert_eq!(Exception::from_number(5), Ok(LoadFault));
assert_eq!(Exception::from_number(6), Ok(StoreMisaligned));
assert_eq!(Exception::from_number(7), Ok(StoreFault));
assert_eq!(Exception::from_number(8), Ok(UserEnvCall));
assert_eq!(Exception::from_number(9), Ok(SupervisorEnvCall));
assert_eq!(Exception::from_number(10), Err(Error::InvalidVariant(10)));
assert_eq!(Exception::from_number(11), Ok(MachineEnvCall));
assert_eq!(Exception::from_number(12), Ok(InstructionPageFault));
assert_eq!(Exception::from_number(13), Ok(LoadPageFault));
assert_eq!(Exception::from_number(14), Err(Error::InvalidVariant(14)));
assert_eq!(Exception::from_number(15), Ok(StorePageFault));
assert_eq!(Exception::from_number(16), Err(Error::InvalidVariant(16)));
assert_eq!(InstructionMisaligned.number(), 0);
assert_eq!(InstructionFault.number(), 1);
assert_eq!(IllegalInstruction.number(), 2);
assert_eq!(Breakpoint.number(), 3);
assert_eq!(LoadMisaligned.number(), 4);
assert_eq!(LoadFault.number(), 5);
assert_eq!(StoreMisaligned.number(), 6);
assert_eq!(StoreFault.number(), 7);
assert_eq!(UserEnvCall.number(), 8);
assert_eq!(SupervisorEnvCall.number(), 9);
assert_eq!(MachineEnvCall.number(), 11);
assert_eq!(InstructionPageFault.number(), 12);
assert_eq!(LoadPageFault.number(), 13);
assert_eq!(StorePageFault.number(), 15);
assert_eq!(StorePageFault.number(), Exception::MAX_EXCEPTION_NUMBER)
}
}
+260
View File
@@ -0,0 +1,260 @@
use crate::{
interrupt::Trap,
register::{scause, sepc, sstatus},
};
use riscv_pac::{
result::{Error, Result},
CoreInterruptNumber, ExceptionNumber, InterruptNumber,
};
/// Interrupt
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(usize)]
pub enum Interrupt {
SupervisorSoft = 1,
SupervisorTimer = 5,
SupervisorExternal = 9,
}
/// SAFETY: `Interrupt` represents the standard RISC-V interrupts
unsafe impl InterruptNumber for Interrupt {
const MAX_INTERRUPT_NUMBER: usize = Self::SupervisorExternal as usize;
#[inline]
fn number(self) -> usize {
self as usize
}
#[inline]
fn from_number(value: usize) -> Result<Self> {
match value {
1 => Ok(Self::SupervisorSoft),
5 => Ok(Self::SupervisorTimer),
9 => Ok(Self::SupervisorExternal),
_ => Err(Error::InvalidVariant(value)),
}
}
}
/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts
unsafe impl CoreInterruptNumber for Interrupt {}
/// Exception
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(usize)]
pub enum Exception {
InstructionMisaligned = 0,
InstructionFault = 1,
IllegalInstruction = 2,
Breakpoint = 3,
LoadMisaligned = 4,
LoadFault = 5,
StoreMisaligned = 6,
StoreFault = 7,
UserEnvCall = 8,
SupervisorEnvCall = 9,
InstructionPageFault = 12,
LoadPageFault = 13,
StorePageFault = 15,
}
/// SAFETY: `Exception` represents the standard RISC-V exceptions
unsafe impl ExceptionNumber for Exception {
const MAX_EXCEPTION_NUMBER: usize = Self::StorePageFault as usize;
#[inline]
fn number(self) -> usize {
self as usize
}
#[inline]
fn from_number(value: usize) -> Result<Self> {
match value {
0 => Ok(Self::InstructionMisaligned),
1 => Ok(Self::InstructionFault),
2 => Ok(Self::IllegalInstruction),
3 => Ok(Self::Breakpoint),
4 => Ok(Self::LoadMisaligned),
5 => Ok(Self::LoadFault),
6 => Ok(Self::StoreMisaligned),
7 => Ok(Self::StoreFault),
8 => Ok(Self::UserEnvCall),
9 => Ok(Self::SupervisorEnvCall),
12 => Ok(Self::InstructionPageFault),
13 => Ok(Self::LoadPageFault),
15 => Ok(Self::StorePageFault),
_ => Err(Error::InvalidVariant(value)),
}
}
}
/// Disables all interrupts in the current hart (supervisor mode).
#[inline]
pub fn disable() {
// SAFETY: It is safe to disable interrupts
unsafe { sstatus::clear_sie() }
}
/// Enables all the interrupts in the current hart (supervisor mode).
///
/// # Safety
///
/// Do not call this function inside a critical section.
#[inline]
pub unsafe fn enable() {
sstatus::set_sie()
}
/// Retrieves the cause of a trap in the current hart (supervisor mode).
///
/// This function expects the target-specific interrupt and exception types.
/// If the raw cause is not a valid interrupt or exception for the target, it returns an error.
#[inline]
pub fn try_cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Result<Trap<I, E>> {
scause::read().cause().try_into()
}
/// Retrieves the cause of a trap in the current hart (supervisor mode).
///
/// This function expects the target-specific interrupt and exception types.
/// If the raw cause is not a valid interrupt or exception for the target, it panics.
#[inline]
pub fn cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Trap<I, E> {
try_cause().unwrap()
}
/// Execute closure `f` with interrupts disabled in the current hart (supervisor mode).
///
/// This method does not synchronise multiple harts, so it is not suitable for
/// using as a critical section. See the `critical-section` crate for a cross-platform
/// way to enter a critical section which provides a `CriticalSection` token.
///
/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
#[inline]
pub fn free<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let sstatus = sstatus::read();
// disable interrupts
disable();
let r = f();
// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
if sstatus.sie() {
unsafe { enable() };
}
r
}
/// Execute closure `f` with interrupts enabled in the current hart (supervisor mode).
///
/// This method is assumed to be called within an interrupt handler, and allows
/// nested interrupts to occur. After the closure `f` is executed, the [`sstatus`]
/// and [`sepc`] registers are properly restored to their previous values.
///
/// # Safety
///
/// - Do not call this function inside a critical section.
/// - This method is assumed to be called within an interrupt handler.
/// - Make sure to clear the interrupt flag that caused the interrupt before calling
/// this method. Otherwise, the interrupt will be re-triggered before executing `f`.
#[inline]
pub unsafe fn nested<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let sstatus = sstatus::read();
let sepc = sepc::read();
// enable interrupts to allow nested interrupts
enable();
let r = f();
// If the interrupts were inactive before our `enable` call, then re-disable
// them. Otherwise, keep them enabled
if !sstatus.sie() {
disable();
}
// Restore SSTATUS.SPIE, SSTATUS.SPP, and SEPC
if sstatus.spie() {
sstatus::set_spie();
}
sstatus::set_spp(sstatus.spp());
sepc::write(sepc);
r
}
#[cfg(test)]
mod test {
use super::*;
use Exception::*;
use Interrupt::*;
#[test]
fn test_interrupt() {
assert_eq!(Interrupt::from_number(1), Ok(SupervisorSoft));
assert_eq!(Interrupt::from_number(2), Err(Error::InvalidVariant(2)));
assert_eq!(Interrupt::from_number(3), Err(Error::InvalidVariant(3)));
assert_eq!(Interrupt::from_number(4), Err(Error::InvalidVariant(4)));
assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer));
assert_eq!(Interrupt::from_number(6), Err(Error::InvalidVariant(6)));
assert_eq!(Interrupt::from_number(7), Err(Error::InvalidVariant(7)));
assert_eq!(Interrupt::from_number(8), Err(Error::InvalidVariant(8)));
assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal));
assert_eq!(Interrupt::from_number(10), Err(Error::InvalidVariant(10)));
assert_eq!(Interrupt::from_number(11), Err(Error::InvalidVariant(11)));
assert_eq!(Interrupt::from_number(12), Err(Error::InvalidVariant(12)));
assert_eq!(SupervisorSoft.number(), 1);
assert_eq!(SupervisorTimer.number(), 5);
assert_eq!(SupervisorExternal.number(), 9);
assert_eq!(SupervisorExternal.number(), Interrupt::MAX_INTERRUPT_NUMBER)
}
#[test]
fn test_exception() {
assert_eq!(Exception::from_number(0), Ok(InstructionMisaligned));
assert_eq!(Exception::from_number(1), Ok(InstructionFault));
assert_eq!(Exception::from_number(2), Ok(IllegalInstruction));
assert_eq!(Exception::from_number(3), Ok(Breakpoint));
assert_eq!(Exception::from_number(4), Ok(LoadMisaligned));
assert_eq!(Exception::from_number(5), Ok(LoadFault));
assert_eq!(Exception::from_number(6), Ok(StoreMisaligned));
assert_eq!(Exception::from_number(7), Ok(StoreFault));
assert_eq!(Exception::from_number(8), Ok(UserEnvCall));
assert_eq!(Exception::from_number(9), Ok(SupervisorEnvCall));
assert_eq!(Exception::from_number(10), Err(Error::InvalidVariant(10)));
assert_eq!(Exception::from_number(11), Err(Error::InvalidVariant(11)));
assert_eq!(Exception::from_number(12), Ok(InstructionPageFault));
assert_eq!(Exception::from_number(13), Ok(LoadPageFault));
assert_eq!(Exception::from_number(14), Err(Error::InvalidVariant(14)));
assert_eq!(Exception::from_number(15), Ok(StorePageFault));
assert_eq!(Exception::from_number(16), Err(Error::InvalidVariant(16)));
assert_eq!(InstructionMisaligned.number(), 0);
assert_eq!(InstructionFault.number(), 1);
assert_eq!(IllegalInstruction.number(), 2);
assert_eq!(Breakpoint.number(), 3);
assert_eq!(LoadMisaligned.number(), 4);
assert_eq!(LoadFault.number(), 5);
assert_eq!(StoreMisaligned.number(), 6);
assert_eq!(StoreFault.number(), 7);
assert_eq!(UserEnvCall.number(), 8);
assert_eq!(SupervisorEnvCall.number(), 9);
assert_eq!(InstructionPageFault.number(), 12);
assert_eq!(LoadPageFault.number(), 13);
assert_eq!(StorePageFault.number(), 15);
assert_eq!(StorePageFault.number(), Exception::MAX_EXCEPTION_NUMBER)
}
}
+62
View File
@@ -0,0 +1,62 @@
//! Low level access to RISC-V processors
//!
//! # Minimum Supported Rust Version (MSRV)
//!
//! This crate is guaranteed to compile on stable Rust 1.60 and up. It *might*
//! compile with older versions but that may change in any new patch release.
//!
//! # Features
//!
//! This crate provides:
//!
//! - Access to core registers like `mstatus` or `mcause`.
//! - Interrupt manipulation mechanisms.
//! - Wrappers around assembly instructions like `WFI`.
//!
//! # Optional features
//!
//! ## `s-mode`
//!
//! This feature re-exports in `interrupt` S-mode interrupt functions defined in `interrupt::supervisor`.
//! By default, the crate assumes that the target is running in M-mode.
//! Thus, `interrupt` re-exports the M-mode functions defined in `interrupt::machine`.
//!
//! ## `critical-section-single-hart`
//!
//! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section)
//! implementation suitable for single-hart targets, based on disabling interrupts globally.
//! This feature uses S-mode interrupt handling if the `s-mode` feature is enabled, and M-mode otherwise.
//!
//! It is **unsound** to enable it on multi-hart targets,
//! and may cause functional problems in systems where some interrupts must NOT be disabled
//! or critical sections are managed as part of an RTOS. In these cases, you should use
//! a target-specific implementation instead, typically provided by a HAL or RTOS crate.
#![no_std]
#![allow(clippy::missing_safety_doc)]
pub use paste::paste;
pub mod asm;
pub mod bits;
pub mod delay;
pub mod interrupt;
pub mod register;
// Re-export crates of the RISC-V ecosystem
#[cfg(feature = "riscv-macros")]
pub use riscv_macros::*;
pub use riscv_pac::*;
#[macro_use]
mod macros;
#[cfg(all(riscv, feature = "critical-section-single-hart"))]
mod critical_section;
/// Used to reexport items for use in macros. Do not use directly.
/// Not covered by semver guarantees.
#[doc(hidden)]
pub mod _export {
pub use critical_section;
}
+57
View File
@@ -0,0 +1,57 @@
/// Macro to create a mutable reference to a statically allocated value
///
/// This macro returns a value with type `Option<&'static mut $ty>`. `Some($expr)` will be returned
/// the first time the macro is executed; further calls will return `None`. To avoid `unwrap`ping a
/// `None` variant the caller must ensure that the macro is called from a function that's executed
/// at most once in the whole lifetime of the program.
///
/// # Note
///
/// This macro requires a `critical-section` implementation to be set. For most single-hart systems,
/// you can enable the `critical-section-single-hart` feature for this crate. For other systems, you
/// have to provide one from elsewhere, typically your chip's HAL crate.
///
/// # Example
///
/// ``` no_run
/// use riscv::singleton;
///
/// fn main() {
/// // OK if `main` is executed only once
/// let x: &'static mut bool = singleton!(: bool = false).unwrap();
///
/// let y = alias();
/// // BAD this second call to `alias` will definitively `panic!`
/// let y_alias = alias();
/// }
///
/// fn alias() -> &'static mut bool {
/// singleton!(: bool = false).unwrap()
/// }
/// ```
#[macro_export]
macro_rules! singleton {
(: $ty:ty = $expr:expr) => {
$crate::_export::critical_section::with(|_| {
static mut VAR: Option<$ty> = None;
#[allow(unsafe_code)]
let used = unsafe { VAR.is_some() };
if used {
None
} else {
let expr = $expr;
#[allow(unsafe_code)]
unsafe {
VAR = Some(expr)
}
#[allow(unsafe_code)]
unsafe {
VAR.as_mut()
}
}
})
};
}
+115
View File
@@ -0,0 +1,115 @@
//! RISC-V CSR's
//!
//! The following registers are not available on 64-bit implementations.
//!
//! - cycleh
//! - timeh
//! - instreth
//! - hpmcounter<3-31>h
//! - mcycleh
//! - minstreth
//! - mhpmcounter<3-31>h
//! - mstatush
//!
//! # On Floating-Point CSRs
//!
//! We are deliberately *not* providing instructions that could change the floating-point rounding
//! mode or exception behavior or read the accrued exceptions flags: `frcsr`, `fscsr`, `fsrm`,
//! `frflags`, `fsflags`.
//!
//! Rust makes no guarantees whatsoever about the contents of the accrued exceptions register: Rust
//! floating-point operations may or may not result in this register getting updated with exception
//! state, and the register can change between two invocations of this function even when no
//! floating-point operations appear in the source code (since floating-point operations appearing
//! earlier or later can be reordered).
//!
//! Modifying the rounding mode leads to **immediate Undefined Behavior**: Rust assumes that the
//! default rounding mode is always set and will optimize accordingly. This even applies when the
//! rounding mode is altered and later reset to its original value without any floating-point
//! operations appearing in the source code between those operations (since floating-point
//! operations appearing earlier or later can be reordered).
//!
//! If you need to perform some floating-point operations and check whether they raised an
//! exception, use a single inline assembly block for the entire sequence of operations.
//!
//! If you need to perform some floating-point operations under a different rounding mode, use a
//! single inline assembly block and make sure to restore the original rounding mode before the end
//! of the block.
#[macro_use]
mod macros;
// User Counter/Timers
pub mod cycle;
pub mod cycleh;
mod hpmcounterx;
pub use self::hpmcounterx::*;
pub mod instret;
pub mod instreth;
pub mod time;
pub mod timeh;
// Supervisor Trap Setup
pub mod scounteren;
pub mod sie;
pub mod sstatus;
pub mod stvec;
// Supervisor Trap Handling
pub mod scause;
pub mod sepc;
pub mod sip;
pub mod sscratch;
pub mod stval;
// Supervisor Protection and Translation
pub mod satp;
// Machine Information Registers
pub mod marchid;
pub mod mhartid;
pub mod mimpid;
pub mod mvendorid;
// Machine Trap Setup
pub mod mcounteren;
pub mod medeleg;
pub mod mideleg;
pub mod mie;
pub mod misa;
pub mod mstatus;
pub mod mstatush;
pub mod mtvec;
// Machine Trap Handling
pub mod mcause;
pub mod mepc;
pub mod mip;
pub mod mscratch;
pub mod mtval;
// Machine Protection and Translation
mod pmpcfgx;
pub use self::pmpcfgx::*;
mod pmpaddrx;
pub use self::pmpaddrx::*;
// Machine Counter/Timers
pub mod mcountinhibit;
pub mod mcycle;
pub mod mcycleh;
mod mhpmcounterx;
pub use self::mhpmcounterx::*;
pub mod minstret;
pub mod minstreth;
// Machine Counter Setup
mod mhpmeventx;
pub use self::mhpmeventx::*;
#[cfg(test)]
mod tests;
// TODO: Debug/Trace Registers (shared with Debug Mode)
// TODO: Debug Mode Registers
+8
View File
@@ -0,0 +1,8 @@
//! cycle register
//!
//! Shadow of mcycle register
//! must have `scounteren::cy` or `mcounteren::cy` bit enabled depending on whether
//! S-mode is implemented or not
read_csr_as_usize!(0xC00);
read_composite_csr!(super::cycleh::read(), read());
+7
View File
@@ -0,0 +1,7 @@
//! cycleh register
//!
//! Shadow of mcycleh register (rv32)
//! must have `scounteren::cy` or `mcounteren::cy` bit enabled depending on whether
//! S-mode is implemented or not
read_csr_as_usize_rv32!(0xC80);
+82
View File
@@ -0,0 +1,82 @@
macro_rules! reg {
(
$addr:expr, $csrl:ident, $csrh:ident
) => {
/// Performance-monitoring counter
pub mod $csrl {
read_csr_as_usize!($addr);
read_composite_csr!(super::$csrh::read(), read());
}
};
}
macro_rules! regh {
(
$addr:expr, $csrh:ident
) => {
/// Upper 32 bits of performance-monitoring counter (RV32I only)
pub mod $csrh {
read_csr_as_usize_rv32!($addr);
}
};
}
reg!(0xC03, hpmcounter3, hpmcounter3h);
reg!(0xC04, hpmcounter4, hpmcounter4h);
reg!(0xC05, hpmcounter5, hpmcounter5h);
reg!(0xC06, hpmcounter6, hpmcounter6h);
reg!(0xC07, hpmcounter7, hpmcounter7h);
reg!(0xC08, hpmcounter8, hpmcounter8h);
reg!(0xC09, hpmcounter9, hpmcounter9h);
reg!(0xC0A, hpmcounter10, hpmcounter10h);
reg!(0xC0B, hpmcounter11, hpmcounter11h);
reg!(0xC0C, hpmcounter12, hpmcounter12h);
reg!(0xC0D, hpmcounter13, hpmcounter13h);
reg!(0xC0E, hpmcounter14, hpmcounter14h);
reg!(0xC0F, hpmcounter15, hpmcounter15h);
reg!(0xC10, hpmcounter16, hpmcounter16h);
reg!(0xC11, hpmcounter17, hpmcounter17h);
reg!(0xC12, hpmcounter18, hpmcounter18h);
reg!(0xC13, hpmcounter19, hpmcounter19h);
reg!(0xC14, hpmcounter20, hpmcounter20h);
reg!(0xC15, hpmcounter21, hpmcounter21h);
reg!(0xC16, hpmcounter22, hpmcounter22h);
reg!(0xC17, hpmcounter23, hpmcounter23h);
reg!(0xC18, hpmcounter24, hpmcounter24h);
reg!(0xC19, hpmcounter25, hpmcounter25h);
reg!(0xC1A, hpmcounter26, hpmcounter26h);
reg!(0xC1B, hpmcounter27, hpmcounter27h);
reg!(0xC1C, hpmcounter28, hpmcounter28h);
reg!(0xC1D, hpmcounter29, hpmcounter29h);
reg!(0xC1E, hpmcounter30, hpmcounter30h);
reg!(0xC1F, hpmcounter31, hpmcounter31h);
regh!(0xC83, hpmcounter3h);
regh!(0xC84, hpmcounter4h);
regh!(0xC85, hpmcounter5h);
regh!(0xC86, hpmcounter6h);
regh!(0xC87, hpmcounter7h);
regh!(0xC88, hpmcounter8h);
regh!(0xC89, hpmcounter9h);
regh!(0xC8A, hpmcounter10h);
regh!(0xC8B, hpmcounter11h);
regh!(0xC8C, hpmcounter12h);
regh!(0xC8D, hpmcounter13h);
regh!(0xC8E, hpmcounter14h);
regh!(0xC8F, hpmcounter15h);
regh!(0xC90, hpmcounter16h);
regh!(0xC91, hpmcounter17h);
regh!(0xC92, hpmcounter18h);
regh!(0xC93, hpmcounter19h);
regh!(0xC94, hpmcounter20h);
regh!(0xC95, hpmcounter21h);
regh!(0xC96, hpmcounter22h);
regh!(0xC97, hpmcounter23h);
regh!(0xC98, hpmcounter24h);
regh!(0xC99, hpmcounter25h);
regh!(0xC9A, hpmcounter26h);
regh!(0xC9B, hpmcounter27h);
regh!(0xC9C, hpmcounter28h);
regh!(0xC9D, hpmcounter29h);
regh!(0xC9E, hpmcounter30h);
regh!(0xC9F, hpmcounter31h);
+8
View File
@@ -0,0 +1,8 @@
//! instret register
//!
//! Shadow of minstret register
//! must have `scounteren::ir` or `mcounteren::ir` bit enabled depending on whether
//! S-mode is implemented or not
read_csr_as_usize!(0xC02);
read_composite_csr!(super::instreth::read(), read());
+7
View File
@@ -0,0 +1,7 @@
//! instreth register
//!
//! Shadow of minstreth register (rv32)
//! must have `scounteren::ir` or `mcounteren::ir` bit enabled depending on whether
//! S-mode is implemented or not
read_csr_as_usize!(0xC82);
File diff suppressed because it is too large. Load diff
+28
View File
@@ -0,0 +1,28 @@
//! marchid register
use core::num::NonZeroUsize;
/// marchid register
#[derive(Clone, Copy, Debug)]
pub struct Marchid {
bits: NonZeroUsize,
}
impl Marchid {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits.get()
}
}
read_csr!(0xF12);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Marchid> {
let r = unsafe { _read() };
// When marchid is hardwired to zero it means that the marchid
// csr isn't implemented.
NonZeroUsize::new(r).map(|bits| Marchid { bits })
}
+59
View File
@@ -0,0 +1,59 @@
//! mcause register
pub use crate::interrupt::Trap;
/// mcause register
#[derive(Clone, Copy, Debug)]
pub struct Mcause {
bits: usize,
}
impl From<usize> for Mcause {
#[inline]
fn from(bits: usize) -> Self {
Self { bits }
}
}
impl Mcause {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the code field
#[inline]
pub fn code(&self) -> usize {
self.bits & !(1 << (usize::BITS as usize - 1))
}
/// Returns the trap cause represented by this register.
///
/// # Note
///
/// This method returns a **raw trap cause**, which means that values are represented as `usize`.
/// To get a target-specific trap cause, use [`Trap::try_into`] with your target-specific M-Mode trap cause types.
#[inline]
pub fn cause(&self) -> Trap<usize, usize> {
if self.is_interrupt() {
Trap::Interrupt(self.code())
} else {
Trap::Exception(self.code())
}
}
/// Is trap cause an interrupt.
#[inline]
pub fn is_interrupt(&self) -> bool {
self.bits & (1 << (usize::BITS as usize - 1)) != 0
}
/// Is trap cause an exception.
#[inline]
pub fn is_exception(&self) -> bool {
!self.is_interrupt()
}
}
read_csr_as!(Mcause, 0x342);
+248
View File
@@ -0,0 +1,248 @@
//! mcounteren register
use crate::bits::{bf_extract, bf_insert};
use crate::result::{Error, Result};
/// mcounteren register
#[derive(Clone, Copy, Debug)]
pub struct Mcounteren {
bits: usize,
}
impl Mcounteren {
/// Supervisor "cycle\[h\]" Enable
#[inline]
pub fn cy(&self) -> bool {
bf_extract(self.bits, 0, 1) != 0
}
/// Sets whether to enable the "cycle\[h\]" counter.
///
/// Only updates the in-memory value, does not modify the `mcounteren` register.
#[inline]
pub fn set_cy(&mut self, cy: bool) {
self.bits = bf_insert(self.bits, 0, 1, cy as usize);
}
/// Supervisor "time\[h\]" Enable
#[inline]
pub fn tm(&self) -> bool {
bf_extract(self.bits, 1, 1) != 0
}
/// Sets whether to enable "time\[h\]".
///
/// Only updates the in-memory value, does not modify the `mcounteren` register.
#[inline]
pub fn set_tm(&mut self, tm: bool) {
self.bits = bf_insert(self.bits, 1, 1, tm as usize);
}
/// Supervisor "instret\[h\]" Enable
#[inline]
pub fn ir(&self) -> bool {
bf_extract(self.bits, 2, 1) != 0
}
/// Sets whether to enable the "instret\[h\]" counter.
///
/// Only updates the in-memory value, does not modify the `mcounteren` register.
#[inline]
pub fn set_ir(&mut self, ir: bool) {
self.bits = bf_insert(self.bits, 2, 1, ir as usize);
}
/// Supervisor "hpm\[x\]" Enable (bits 3-31)
///
/// **WARNING**: panics on `index` out-of-bounds
#[inline]
pub fn hpm(&self, index: usize) -> bool {
self.try_hpm(index).unwrap()
}
/// Fallible Supervisor "hpm\[x\]" Enable (bits 3-31).
///
/// Attempts to read the "hpm\[x\]" value, and returns an error if the `index` is invalid.
#[inline]
pub fn try_hpm(&self, index: usize) -> Result<bool> {
if (3..32).contains(&index) {
Ok(bf_extract(self.bits, index, 1) != 0)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
/// Sets whether to enable the "hpm\[X\]" counter.
///
/// Only updates the in-memory value, does not modify the `mcounteren` register.
///
/// **WARNING**: panics on `index` out-of-bounds
#[inline]
pub fn set_hpm(&mut self, index: usize, hpm: bool) {
self.try_set_hpm(index, hpm).unwrap()
}
/// Sets whether to enable the "hpm\[X\]" counter.
///
/// Only updates the in-memory value, does not modify the `mcounteren` register.
///
/// Attempts to update the "hpm\[x\]" value, and returns an error if the `index` is invalid.
#[inline]
pub fn try_set_hpm(&mut self, index: usize, hpm: bool) -> Result<()> {
if (3..32).contains(&index) {
self.bits = bf_insert(self.bits, index, 1, hpm as usize);
Ok(())
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
}
read_csr_as!(Mcounteren, 0x306);
write_csr_as!(Mcounteren, 0x306);
set!(0x306);
clear!(0x306);
set_clear_csr!(
/// Supervisor cycle Enable
, set_cy, clear_cy, 1 << 0);
set_clear_csr!(
/// Supervisor time Enable
, set_tm, clear_tm, 1 << 1);
set_clear_csr!(
/// Supervisor instret Enable
, set_ir, clear_ir, 1 << 2);
/// Enables the "hpm\[X\]" counter.
///
/// Updates the `mcounteren` register.
///
/// **WARNING**: panics on:
///
/// - non-`riscv` targets
/// - `index` out-of-bounds
#[inline]
pub unsafe fn set_hpm(index: usize) {
try_set_hpm(index).unwrap();
}
/// Attempts to enable the "hpm\[X\]" counter.
///
/// Updates the `mcounteren` register.
#[inline]
pub unsafe fn try_set_hpm(index: usize) -> Result<()> {
if (3..32).contains(&index) {
_try_set(1 << index)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
/// Disables the "hpm\[X\]" counter.
///
/// Updates the `mcounteren` register.
///
/// **WARNING**: panics on:
///
/// - non-`riscv` targets
/// - `index` out-of-bounds
#[inline]
pub unsafe fn clear_hpm(index: usize) {
try_clear_hpm(index).unwrap();
}
/// Attempts to disable the "hpm\[X\]" counter.
///
/// Updates the `mcounteren` register.
#[inline]
pub unsafe fn try_clear_hpm(index: usize) -> Result<()> {
if (3..32).contains(&index) {
_try_clear(1 << index)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mcounteren() {
let mut m = Mcounteren { bits: 0 };
assert!(!m.cy());
m.set_cy(true);
assert!(m.cy());
m.set_cy(false);
assert!(!m.cy());
assert!(!m.tm());
m.set_tm(true);
assert!(m.tm());
m.set_tm(false);
assert!(!m.tm());
assert!(!m.ir());
m.set_ir(true);
assert!(m.ir());
m.set_ir(false);
assert!(!m.ir());
(3..32).for_each(|i| {
assert!(!m.hpm(i));
assert_eq!(m.try_hpm(i), Ok(false));
m.set_hpm(i, true);
assert!(m.hpm(i));
assert_eq!(m.try_set_hpm(i, false), Ok(()));
assert_eq!(m.try_hpm(i), Ok(false));
assert!(!m.hpm(i));
});
(0..3).chain(32..64).for_each(|index| {
assert_eq!(
m.try_hpm(index),
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31
})
);
assert_eq!(
m.try_set_hpm(index, false),
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31
})
);
})
}
}
+135
View File
@@ -0,0 +1,135 @@
//! `mcountinhibit` register
use crate::result::{Error, Result};
read_write_csr! {
/// `mcountinhibit` register
Mcountinhibit: 0x320,
mask: 0xffff_fffd,
}
set!(0x320);
clear!(0x320);
read_write_csr_field! {
Mcountinhibit,
/// Gets the `cycle[h]` inhibit field value.
cy: 0,
}
read_write_csr_field! {
Mcountinhibit,
/// Gets the `instret[h]` inhibit field value.
ir: 2,
}
read_write_csr_field! {
Mcountinhibit,
/// Gets the `mhpmcounterX[h]` inhibit field value.
///
/// **WARN**: `index` must be in the range `[31:3]`.
hpm: 3..=31,
}
set_clear_csr!(
/// Machine cycle Disable
, set_cy, clear_cy, 1 << 0);
set_clear_csr!(
/// Machine instret Disable
, set_ir, clear_ir, 1 << 2);
#[inline]
pub unsafe fn set_hpm(index: usize) {
assert!((3..32).contains(&index));
_set(1 << index);
}
#[inline]
pub unsafe fn try_set_hpm(index: usize) -> Result<()> {
if (3..32).contains(&index) {
_try_set(1 << index)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
#[inline]
pub unsafe fn clear_hpm(index: usize) {
assert!((3..32).contains(&index));
_clear(1 << index);
}
#[inline]
pub unsafe fn try_clear_hpm(index: usize) -> Result<()> {
if (3..32).contains(&index) {
_try_clear(1 << index)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mcountinhibit() {
let mut m = Mcountinhibit { bits: 0 };
assert!(!m.cy());
m.set_cy(true);
assert!(m.cy());
m.set_cy(false);
assert!(!m.cy());
assert!(!m.ir());
m.set_ir(true);
assert!(m.ir());
m.set_ir(false);
assert!(!m.ir());
(3..32).for_each(|i| {
assert!(!m.hpm(i));
assert_eq!(m.try_hpm(i), Ok(false));
m.set_hpm(i, true);
assert!(m.hpm(i));
assert_eq!(m.try_hpm(i), Ok(true));
assert_eq!(m.try_set_hpm(i, false), Ok(()));
assert_eq!(m.try_hpm(i), Ok(false));
});
(0..2).chain(32..64).for_each(|index| {
assert_eq!(
m.try_hpm(index),
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31
})
);
assert_eq!(
m.try_set_hpm(index, false),
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31
})
);
});
}
}
+4
View File
@@ -0,0 +1,4 @@
//! mcycle register
read_csr_as_usize!(0xB00);
read_composite_csr!(super::mcycleh::read(), read());
+3
View File
@@ -0,0 +1,3 @@
//! mcycleh register
read_csr_as_usize_rv32!(0xB80);
+137
View File
@@ -0,0 +1,137 @@
//! medeleg register
/// medeleg register
#[derive(Clone, Copy, Debug)]
pub struct Medeleg {
bits: usize,
}
impl Medeleg {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Instruction Address Misaligned Delegate
#[inline]
pub fn instruction_misaligned(&self) -> bool {
self.bits & (1 << 0) != 0
}
/// Instruction Access Fault Delegate
#[inline]
pub fn instruction_fault(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// Illegal Instruction Delegate
#[inline]
pub fn illegal_instruction(&self) -> bool {
self.bits & (1 << 2) != 0
}
/// Breakpoint Delegate
#[inline]
pub fn breakpoint(&self) -> bool {
self.bits & (1 << 3) != 0
}
/// Load Address Misaligned Delegate
#[inline]
pub fn load_misaligned(&self) -> bool {
self.bits & (1 << 4) != 0
}
/// Load Access Fault Delegate
#[inline]
pub fn load_fault(&self) -> bool {
self.bits & (1 << 5) != 0
}
/// Store/AMO Address Misaligned Delegate
#[inline]
pub fn store_misaligned(&self) -> bool {
self.bits & (1 << 6) != 0
}
/// Store/AMO Access Fault Delegate
#[inline]
pub fn store_fault(&self) -> bool {
self.bits & (1 << 7) != 0
}
/// Environment Call from U-mode Delegate
#[inline]
pub fn user_env_call(&self) -> bool {
self.bits & (1 << 8) != 0
}
/// Environment Call from S-mode Delegate
#[inline]
pub fn supervisor_env_call(&self) -> bool {
self.bits & (1 << 9) != 0
}
/// Instruction Page Fault Delegate
#[inline]
pub fn instruction_page_fault(&self) -> bool {
self.bits & (1 << 12) != 0
}
/// Load Page Fault Delegate
#[inline]
pub fn load_page_fault(&self) -> bool {
self.bits & (1 << 13) != 0
}
/// Store/AMO Page Fault Delegate
#[inline]
pub fn store_page_fault(&self) -> bool {
self.bits & (1 << 15) != 0
}
}
read_csr_as!(Medeleg, 0x302);
set!(0x302);
clear!(0x302);
set_clear_csr!(
/// Instruction Address Misaligned Delegate
, set_instruction_misaligned, clear_instruction_misaligned, 1 << 0);
set_clear_csr!(
/// Instruction Access Fault Delegate
, set_instruction_fault, clear_instruction_fault, 1 << 1);
set_clear_csr!(
/// Illegal Instruction Delegate
, set_illegal_instruction, clear_illegal_instruction, 1 << 2);
set_clear_csr!(
/// Breakpoint Delegate
, set_breakpoint, clear_breakpoint, 1 << 3);
set_clear_csr!(
/// Load Address Misaligned Delegate
, set_load_misaligned, clear_load_misaligned, 1 << 4);
set_clear_csr!(
/// Load Access Fault Delegate
, set_load_fault, clear_load_fault, 1 << 5);
set_clear_csr!(
/// Store/AMO Address Misaligned Delegate
, set_store_misaligned, clear_store_misaligned, 1 << 6);
set_clear_csr!(
/// Store/AMO Access fault
, set_store_fault, clear_store_fault, 1 << 7);
set_clear_csr!(
/// Environment Call from U-mode Delegate
, set_user_env_call, clear_user_env_call, 1 << 8);
set_clear_csr!(
/// Environment Call from S-mode Delegate
, set_supervisor_env_call, clear_supervisor_env_call, 1 << 9);
set_clear_csr!(
/// Instruction Page Fault Delegate
, set_instruction_page_fault, clear_instruction_page_fault, 1 << 12);
set_clear_csr!(
/// Load Page Fault Delegate
, set_load_page_fault, clear_load_page_fault, 1 << 13);
set_clear_csr!(
/// Store/AMO Page Fault Delegate
, set_store_page_fault, clear_store_page_fault, 1 << 15);
+4
View File
@@ -0,0 +1,4 @@
//! mepc register
read_csr_as_usize!(0x341);
write_csr_as_usize!(0x341);
+3
View File
@@ -0,0 +1,3 @@
//! mhartid register
read_csr_as_usize!(0xf14);
+84
View File
@@ -0,0 +1,84 @@
macro_rules! reg {
(
$addr:expr, $csrl:ident, $csrh:ident
) => {
/// Machine performance-monitoring counter
pub mod $csrl {
read_csr_as_usize!($addr);
write_csr_as_usize!($addr);
read_composite_csr!(super::$csrh::read(), read());
}
};
}
macro_rules! regh {
(
$addr:expr, $csrh:ident
) => {
/// Upper 32 bits of machine performance-monitoring counter (RV32I only)
pub mod $csrh {
read_csr_as_usize_rv32!($addr);
write_csr_as_usize_rv32!($addr);
}
};
}
reg!(0xB03, mhpmcounter3, mhpmcounter3h);
reg!(0xB04, mhpmcounter4, mhpmcounter4h);
reg!(0xB05, mhpmcounter5, mhpmcounter5h);
reg!(0xB06, mhpmcounter6, mhpmcounter6h);
reg!(0xB07, mhpmcounter7, mhpmcounter7h);
reg!(0xB08, mhpmcounter8, mhpmcounter8h);
reg!(0xB09, mhpmcounter9, mhpmcounter9h);
reg!(0xB0A, mhpmcounter10, mhpmcounter10h);
reg!(0xB0B, mhpmcounter11, mhpmcounter11h);
reg!(0xB0C, mhpmcounter12, mhpmcounter12h);
reg!(0xB0D, mhpmcounter13, mhpmcounter13h);
reg!(0xB0E, mhpmcounter14, mhpmcounter14h);
reg!(0xB0F, mhpmcounter15, mhpmcounter15h);
reg!(0xB10, mhpmcounter16, mhpmcounter16h);
reg!(0xB11, mhpmcounter17, mhpmcounter17h);
reg!(0xB12, mhpmcounter18, mhpmcounter18h);
reg!(0xB13, mhpmcounter19, mhpmcounter19h);
reg!(0xB14, mhpmcounter20, mhpmcounter20h);
reg!(0xB15, mhpmcounter21, mhpmcounter21h);
reg!(0xB16, mhpmcounter22, mhpmcounter22h);
reg!(0xB17, mhpmcounter23, mhpmcounter23h);
reg!(0xB18, mhpmcounter24, mhpmcounter24h);
reg!(0xB19, mhpmcounter25, mhpmcounter25h);
reg!(0xB1A, mhpmcounter26, mhpmcounter26h);
reg!(0xB1B, mhpmcounter27, mhpmcounter27h);
reg!(0xB1C, mhpmcounter28, mhpmcounter28h);
reg!(0xB1D, mhpmcounter29, mhpmcounter29h);
reg!(0xB1E, mhpmcounter30, mhpmcounter30h);
reg!(0xB1F, mhpmcounter31, mhpmcounter31h);
regh!(0xB83, mhpmcounter3h);
regh!(0xB84, mhpmcounter4h);
regh!(0xB85, mhpmcounter5h);
regh!(0xB86, mhpmcounter6h);
regh!(0xB87, mhpmcounter7h);
regh!(0xB88, mhpmcounter8h);
regh!(0xB89, mhpmcounter9h);
regh!(0xB8A, mhpmcounter10h);
regh!(0xB8B, mhpmcounter11h);
regh!(0xB8C, mhpmcounter12h);
regh!(0xB8D, mhpmcounter13h);
regh!(0xB8E, mhpmcounter14h);
regh!(0xB8F, mhpmcounter15h);
regh!(0xB90, mhpmcounter16h);
regh!(0xB91, mhpmcounter17h);
regh!(0xB92, mhpmcounter18h);
regh!(0xB93, mhpmcounter19h);
regh!(0xB94, mhpmcounter20h);
regh!(0xB95, mhpmcounter21h);
regh!(0xB96, mhpmcounter22h);
regh!(0xB97, mhpmcounter23h);
regh!(0xB98, mhpmcounter24h);
regh!(0xB99, mhpmcounter25h);
regh!(0xB9A, mhpmcounter26h);
regh!(0xB9B, mhpmcounter27h);
regh!(0xB9C, mhpmcounter28h);
regh!(0xB9D, mhpmcounter29h);
regh!(0xB9E, mhpmcounter30h);
regh!(0xB9F, mhpmcounter31h);
+41
View File
@@ -0,0 +1,41 @@
macro_rules! reg {
(
$addr:expr, $csr:ident
) => {
/// Machine performance-monitoring event selector
pub mod $csr {
read_csr_as_usize!($addr);
write_csr_as_usize!($addr);
}
};
}
reg!(0x323, mhpmevent3);
reg!(0x324, mhpmevent4);
reg!(0x325, mhpmevent5);
reg!(0x326, mhpmevent6);
reg!(0x327, mhpmevent7);
reg!(0x328, mhpmevent8);
reg!(0x329, mhpmevent9);
reg!(0x32A, mhpmevent10);
reg!(0x32B, mhpmevent11);
reg!(0x32C, mhpmevent12);
reg!(0x32D, mhpmevent13);
reg!(0x32E, mhpmevent14);
reg!(0x32F, mhpmevent15);
reg!(0x330, mhpmevent16);
reg!(0x331, mhpmevent17);
reg!(0x332, mhpmevent18);
reg!(0x333, mhpmevent19);
reg!(0x334, mhpmevent20);
reg!(0x335, mhpmevent21);
reg!(0x336, mhpmevent22);
reg!(0x337, mhpmevent23);
reg!(0x338, mhpmevent24);
reg!(0x339, mhpmevent25);
reg!(0x33A, mhpmevent26);
reg!(0x33B, mhpmevent27);
reg!(0x33C, mhpmevent28);
reg!(0x33D, mhpmevent29);
reg!(0x33E, mhpmevent30);
reg!(0x33F, mhpmevent31);
+47
View File
@@ -0,0 +1,47 @@
//! mideleg register
/// mideleg register
#[derive(Clone, Copy, Debug)]
pub struct Mideleg {
bits: usize,
}
impl Mideleg {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Supervisor Software Interrupt Delegate
#[inline]
pub fn ssoft(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// Supervisor Timer Interrupt Delegate
#[inline]
pub fn stimer(&self) -> bool {
self.bits & (1 << 5) != 0
}
/// Supervisor External Interrupt Delegate
#[inline]
pub fn sext(&self) -> bool {
self.bits & (1 << 9) != 0
}
}
read_csr_as!(Mideleg, 0x303);
set!(0x303);
clear!(0x303);
set_clear_csr!(
/// Supervisor Software Interrupt Delegate
, set_ssoft, clear_ssoft, 1 << 1);
set_clear_csr!(
/// Supervisor Timer Interrupt Delegate
, set_stimer, clear_stimer, 1 << 5);
set_clear_csr!(
/// Supervisor External Interrupt Delegate
, set_sext, clear_sext, 1 << 9);
+74
View File
@@ -0,0 +1,74 @@
//! mie register
/// mie register
#[derive(Clone, Copy, Debug)]
pub struct Mie {
bits: usize,
}
impl Mie {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Supervisor Software Interrupt Enable
#[inline]
pub fn ssoft(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// Machine Software Interrupt Enable
#[inline]
pub fn msoft(&self) -> bool {
self.bits & (1 << 3) != 0
}
/// Supervisor Timer Interrupt Enable
#[inline]
pub fn stimer(&self) -> bool {
self.bits & (1 << 5) != 0
}
/// Machine Timer Interrupt Enable
#[inline]
pub fn mtimer(&self) -> bool {
self.bits & (1 << 7) != 0
}
/// Supervisor External Interrupt Enable
#[inline]
pub fn sext(&self) -> bool {
self.bits & (1 << 9) != 0
}
/// Machine External Interrupt Enable
#[inline]
pub fn mext(&self) -> bool {
self.bits & (1 << 11) != 0
}
}
read_csr_as!(Mie, 0x304);
set!(0x304);
clear!(0x304);
set_clear_csr!(
/// Supervisor Software Interrupt Enable
, set_ssoft, clear_ssoft, 1 << 1);
set_clear_csr!(
/// Machine Software Interrupt Enable
, set_msoft, clear_msoft, 1 << 3);
set_clear_csr!(
/// Supervisor Timer Interrupt Enable
, set_stimer, clear_stimer, 1 << 5);
set_clear_csr!(
/// Machine Timer Interrupt Enable
, set_mtimer, clear_mtimer, 1 << 7);
set_clear_csr!(
/// Supervisor External Interrupt Enable
, set_sext, clear_sext, 1 << 9);
set_clear_csr!(
/// Machine External Interrupt Enable
, set_mext, clear_mext, 1 << 11);
+28
View File
@@ -0,0 +1,28 @@
//! mimpid register
use core::num::NonZeroUsize;
/// mimpid register
#[derive(Clone, Copy, Debug)]
pub struct Mimpid {
bits: NonZeroUsize,
}
impl Mimpid {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits.get()
}
}
read_csr!(0xF13);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Mimpid> {
let r = unsafe { _read() };
// When mimpid is hardwired to zero it means that the mimpid
// csr isn't implemented.
NonZeroUsize::new(r).map(|bits| Mimpid { bits })
}
+4
View File
@@ -0,0 +1,4 @@
//! minstret register
read_csr_as_usize!(0xB02);
read_composite_csr!(super::minstreth::read(), read());
+3
View File
@@ -0,0 +1,3 @@
//! minstreth register
read_csr_as_usize_rv32!(0xB82);
+65
View File
@@ -0,0 +1,65 @@
//! mip register
/// mip register
#[derive(Clone, Copy, Debug)]
pub struct Mip {
bits: usize,
}
impl Mip {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Supervisor Software Interrupt Pending
#[inline]
pub fn ssoft(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// Machine Software Interrupt Pending
#[inline]
pub fn msoft(&self) -> bool {
self.bits & (1 << 3) != 0
}
/// Supervisor Timer Interrupt Pending
#[inline]
pub fn stimer(&self) -> bool {
self.bits & (1 << 5) != 0
}
/// Machine Timer Interrupt Pending
#[inline]
pub fn mtimer(&self) -> bool {
self.bits & (1 << 7) != 0
}
/// Supervisor External Interrupt Pending
#[inline]
pub fn sext(&self) -> bool {
self.bits & (1 << 9) != 0
}
/// Machine External Interrupt Pending
#[inline]
pub fn mext(&self) -> bool {
self.bits & (1 << 11) != 0
}
}
read_csr_as!(Mip, 0x344);
set!(0x344);
clear!(0x344);
set_clear_csr!(
/// Supervisor Software Interrupt Pending
, set_ssoft, clear_ssoft, 1 << 1);
set_clear_csr!(
/// Supervisor Timer Interrupt Pending
, set_stimer, clear_stimer, 1 << 5);
set_clear_csr!(
/// Supervisor External Interrupt Pending
, set_sext, clear_sext, 1 << 9);
+72
View File
@@ -0,0 +1,72 @@
//! misa register
use core::num::NonZeroUsize;
/// misa register
#[derive(Clone, Copy, Debug)]
pub struct Misa {
bits: NonZeroUsize,
}
/// Base integer ISA width
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum XLEN {
XLEN32 = 1,
XLEN64 = 2,
XLEN128 = 3,
}
impl XLEN {
/// Converts a number into an ISA width
pub(crate) fn from(value: u8) -> Self {
match value {
1 => XLEN::XLEN32,
2 => XLEN::XLEN64,
3 => XLEN::XLEN128,
_ => unreachable!(),
}
}
}
impl Misa {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits.get()
}
/// Effective xlen in M-mode (i.e., `MXLEN`).
#[inline]
pub fn mxl(&self) -> XLEN {
let value = (self.bits() >> (usize::BITS - 2)) as u8;
XLEN::from(value)
}
/// Returns true when a given extension is implemented.
///
/// # Example
///
/// ```no_run
/// let misa = unsafe { riscv::register::misa::read() }.unwrap();
/// assert!(misa.has_extension('A')); // panics if atomic extension is not implemented
/// ```
#[inline]
pub fn has_extension(&self, extension: char) -> bool {
let bit = extension as u8 - 65;
if bit > 25 {
return false;
}
self.bits() & (1 << bit) == (1 << bit)
}
}
read_csr!(0x301);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Misa> {
let r = unsafe { _read() };
// When misa is hardwired to zero it means that the misa csr
// isn't implemented.
NonZeroUsize::new(r).map(|bits| Misa { bits })
}
+4
View File
@@ -0,0 +1,4 @@
//! mscratch register
read_csr_as_usize!(0x340);
write_csr_as_usize!(0x340);
+653
View File
@@ -0,0 +1,653 @@
//! mstatus register
pub use super::misa::XLEN;
use crate::bits::{bf_extract, bf_insert};
/// mstatus register
#[derive(Clone, Copy, Debug)]
pub struct Mstatus {
bits: usize,
}
impl From<usize> for Mstatus {
#[inline]
fn from(bits: usize) -> Self {
Self { bits }
}
}
/// Additional extension state
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum XS {
/// All off
AllOff = 0,
/// None dirty or clean, some on
NoneDirtyOrClean = 1,
/// None dirty, some clean
NoneDirtySomeClean = 2,
/// Some dirty
SomeDirty = 3,
}
/// Floating-point extension state
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FS {
Off = 0,
Initial = 1,
Clean = 2,
Dirty = 3,
}
/// Vector extension state
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum VS {
Off = 0,
Initial = 1,
Clean = 2,
Dirty = 3,
}
/// Machine Previous Privilege Mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MPP {
Machine = 3,
Supervisor = 1,
User = 0,
}
/// Supervisor Previous Privilege Mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SPP {
Supervisor = 1,
User = 0,
}
/// Non-instruction-fetch memory endianness
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Endianness {
BigEndian = 1,
LittleEndian = 0,
}
impl From<bool> for Endianness {
fn from(value: bool) -> Self {
match value {
true => Self::BigEndian,
false => Self::LittleEndian,
}
}
}
impl Mstatus {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Supervisor Interrupt Enable
#[inline]
pub fn sie(&self) -> bool {
bf_extract(self.bits, 1, 1) != 0
}
/// Update Supervisor Interrupt Enable
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_sie`]/[`clear_sie`] to directly
/// update the CSR.
#[inline]
pub fn set_sie(&mut self, sie: bool) {
self.bits = bf_insert(self.bits, 1, 1, sie as usize);
}
/// Machine Interrupt Enable
#[inline]
pub fn mie(&self) -> bool {
bf_extract(self.bits, 3, 1) != 0
}
/// Update Machine Interrupt Enable
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_mie`]/[`clear_mie`] to directly
/// update the CSR.
#[inline]
pub fn set_mie(&mut self, mie: bool) {
self.bits = bf_insert(self.bits, 3, 1, mie as usize);
}
/// Supervisor Previous Interrupt Enable
#[inline]
pub fn spie(&self) -> bool {
bf_extract(self.bits, 5, 1) != 0
}
/// Update Supervisor Previous Interrupt Enable
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_spie`]` to directly update the
/// CSR.
#[inline]
pub fn set_spie(&mut self, spie: bool) {
self.bits = bf_insert(self.bits, 5, 1, spie as usize);
}
/// U-mode non-instruction-fetch memory endianness
#[inline]
pub fn ube(&self) -> Endianness {
Endianness::from(bf_extract(self.bits, 6, 1) != 0)
}
/// Update U-mode non-instruction-fetch memory endianness
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_ube`] to directly update the
/// CSR.
#[inline]
pub fn set_ube(&mut self, endianness: Endianness) {
self.bits = bf_insert(self.bits, 6, 1, endianness as usize);
}
/// Machine Previous Interrupt Enable
#[inline]
pub fn mpie(&self) -> bool {
bf_extract(self.bits, 7, 1) != 0
}
/// Update Machine Previous Interrupt Enable
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_mpie`] to directly update the
/// CSR.
#[inline]
pub fn set_mpie(&mut self, mpie: bool) {
self.bits = bf_insert(self.bits, 7, 1, mpie as usize);
}
/// Supervisor Previous Privilege Mode
#[inline]
pub fn spp(&self) -> SPP {
match bf_extract(self.bits, 8, 1) != 0 {
true => SPP::Supervisor,
false => SPP::User,
}
}
/// Update Supervisor Previous Privilege Mode
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_spp`] to directly update the
/// CSR.
#[inline]
pub fn set_spp(&mut self, spp: SPP) {
self.bits = bf_insert(self.bits, 8, 1, spp as usize);
}
/// Machine Previous Privilege Mode
#[inline]
pub fn mpp(&self) -> MPP {
let mpp = bf_extract(self.bits, 11, 2); // bits 11-12
match mpp {
0b00 => MPP::User,
0b01 => MPP::Supervisor,
0b11 => MPP::Machine,
_ => unreachable!(),
}
}
/// Update Machine Previous Privilege Mode
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_mpp`] to directly update the
/// CSR.
#[inline]
pub fn set_mpp(&mut self, mpp: MPP) {
self.bits = bf_insert(self.bits, 11, 2, mpp as usize);
}
/// Floating-point extension state
///
/// Encodes the status of the floating-point unit, including the CSR `fcsr`
/// and floating-point data registers `f0–f31`.
#[inline]
pub fn fs(&self) -> FS {
let fs = bf_extract(self.bits, 13, 2); // bits 13-14
match fs {
0b00 => FS::Off,
0b01 => FS::Initial,
0b10 => FS::Clean,
0b11 => FS::Dirty,
_ => unreachable!(),
}
}
/// Vector extension state
#[inline]
pub fn vs(&self) -> VS {
let fs = bf_extract(self.bits, 9, 2); // bits 9-10
match fs {
0b00 => VS::Off,
0b01 => VS::Initial,
0b10 => VS::Clean,
0b11 => VS::Dirty,
_ => unreachable!(),
}
}
/// Update Floating-point extension state
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_fs`] to directly update the
/// CSR.
#[inline]
pub fn set_fs(&mut self, fs: FS) {
self.bits = bf_insert(self.bits, 13, 2, fs as usize);
}
/// Update vector extension state
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_vs`] to directly update the
/// CSR.
#[inline]
pub fn set_vs(&mut self, vs: VS) {
self.bits = bf_insert(self.bits, 9, 2, vs as usize);
}
/// Additional extension state
///
/// Encodes the status of additional user-mode extensions and associated
/// state.
#[inline]
pub fn xs(&self) -> XS {
let xs = bf_extract(self.bits, 15, 2); // bits 15-16
match xs {
0b00 => XS::AllOff,
0b01 => XS::NoneDirtyOrClean,
0b10 => XS::NoneDirtySomeClean,
0b11 => XS::SomeDirty,
_ => unreachable!(),
}
}
/// Update Additional extension state
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself.
#[inline]
pub fn set_xs(&mut self, xs: XS) {
self.bits = bf_insert(self.bits, 15, 2, xs as usize);
}
/// Modify Memory PRiVilege
#[inline]
pub fn mprv(&self) -> bool {
bf_extract(self.bits, 17, 1) != 0
}
/// Update Modify Memory PRiVilege
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_mprv`]/[`clear_mprv`] to
/// directly update the CSR.
#[inline]
pub fn set_mprv(&mut self, mprv: bool) {
self.bits = bf_insert(self.bits, 17, 1, mprv as usize);
}
/// Permit Supervisor User Memory access
#[inline]
pub fn sum(&self) -> bool {
bf_extract(self.bits, 18, 1) != 0
}
/// Update Permit Supervisor User Memory access
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_sum`]/[`clear_sum`] to directly
/// update the CSR.
#[inline]
pub fn set_sum(&mut self, sum: bool) {
self.bits = bf_insert(self.bits, 18, 1, sum as usize);
}
/// Make eXecutable Readable
#[inline]
pub fn mxr(&self) -> bool {
bf_extract(self.bits, 19, 1) != 0
}
/// Update Make eXecutable Readable
///
/// Note this updates a previously read [`Mstatus`] value, but does not affect
/// the mstatus CSR itself. See [`set_mxr`]/[`clear_mxr`] to directly update
/// the CSR.
#[inline]
pub fn set_mxr(&mut self, mxr: bool) {
self.bits = bf_insert(self.bits, 19, 1, mxr as usize);
}
/// Trap Virtual Memory
///
/// If this bit is set, reads or writes to `satp` CSR or execute `sfence.vma`
/// instruction when in S-mode will raise an illegal instruction exception.
///
/// TVM is hard-wired to 0 when S-mode is not supported.
#[inline]
pub fn tvm(&self) -> bool {
bf_extract(self.bits, 20, 1) != 0
}
/// Update Trap Virtual Memory
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_tvm`]/[`clear_tvm`] to directly
/// update the CSR.
#[inline]
pub fn set_tvm(&mut self, tvm: bool) {
self.bits = bf_insert(self.bits, 20, 1, tvm as usize);
}
/// Timeout Wait
///
/// Indicates that if WFI instruction should be intercepted.
///
/// If this bit is set, when WFI is executed in S-mode, and it does not complete
/// within an implementation specific, bounded time limit, the WFI instruction will cause
/// an illegal instruction trap; or could always cause trap then the time limit is zero.
///
/// TW is hard-wired to 0 when S-mode is not supported.
#[inline]
pub fn tw(&self) -> bool {
bf_extract(self.bits, 21, 1) != 0
}
/// Update Timeout Wait
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_tw`]/[`clear_tw`] to directly
/// update the CSR.
#[inline]
pub fn set_tw(&mut self, tw: bool) {
self.bits = bf_insert(self.bits, 21, 1, tw as usize);
}
/// Trap SRET
///
/// Indicates that if SRET instruction should be trapped to raise illegal
/// instruction exception.
///
/// If S-mode is not supported, TSR bit is hard-wired to 0.
#[inline]
pub fn tsr(&self) -> bool {
bf_extract(self.bits, 22, 1) != 0
}
/// Update Trap SRET
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_tsr`]/[`clear_tsr`] to directly
/// update the CSR.
#[inline]
pub fn set_tsr(&mut self, tsr: bool) {
self.bits = bf_insert(self.bits, 22, 1, tsr as usize);
}
/// Effective xlen in U-mode (i.e., `UXLEN`).
///
/// In RISCV-32, UXL does not exist, and `UXLEN` is always [`XLEN::XLEN32`].
#[inline]
pub fn uxl(&self) -> XLEN {
match () {
#[cfg(riscv32)]
() => XLEN::XLEN32,
#[cfg(not(riscv32))]
() => XLEN::from(bf_extract(self.bits, 32, 2) as u8),
}
}
/// Update Effective xlen in U-mode (i.e., `UXLEN`).
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself.
#[inline]
pub fn set_uxl(&mut self, uxl: XLEN) {
#[cfg(not(riscv32))]
{
self.bits = bf_insert(self.bits, 32, 2, uxl as usize);
}
}
/// Effective xlen in S-mode (i.e., `SXLEN`).
///
/// In RISCV-32, SXL does not exist, and SXLEN is always [`XLEN::XLEN32`].
#[inline]
pub fn sxl(&self) -> XLEN {
match () {
#[cfg(riscv32)]
() => XLEN::XLEN32,
#[cfg(not(riscv32))]
() => XLEN::from(bf_extract(self.bits, 34, 2) as u8),
}
}
/// Update Effective xlen in S-mode (i.e., `SXLEN`).
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself.
#[inline]
pub fn set_sxl(&mut self, sxl: XLEN) {
#[cfg(not(riscv32))]
{
self.bits = bf_insert(self.bits, 34, 2, sxl as usize);
}
}
/// S-mode non-instruction-fetch memory endianness.
///
/// In RISCV-32, this field is read from the [`crate::register::mstatush`] register.
pub fn sbe(&self) -> Endianness {
match () {
#[cfg(riscv32)]
() => super::mstatush::read().sbe(),
#[cfg(not(riscv32))]
() => Endianness::from(bf_extract(self.bits, 36, 1) != 0),
}
}
/// Update S-mode non-instruction-fetch memory endianness
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_sbe`] to directly update the
/// CSR.
#[inline]
pub fn set_sbe(&mut self, endianness: Endianness) {
#[cfg(not(riscv32))]
{
self.bits = bf_insert(self.bits, 36, 1, endianness as usize);
}
}
/// M-mode non-instruction-fetch memory endianness
///
/// In RISCV-32, this field is read from the [`crate::register::mstatush`] register
pub fn mbe(&self) -> Endianness {
match () {
#[cfg(riscv32)]
() => super::mstatush::read().mbe(),
#[cfg(not(riscv32))]
() => Endianness::from(bf_extract(self.bits, 37, 1) != 0),
}
}
/// Update M-mode non-instruction-fetch memory endianness
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself. See [`set_mbe`] to directly update the
/// CSR.
#[inline]
pub fn set_mbe(&mut self, endianness: Endianness) {
#[cfg(not(riscv32))]
{
self.bits = bf_insert(self.bits, 37, 1, endianness as usize);
}
}
/// Whether either the FS field or XS field signals the presence of some dirty state
#[inline]
pub fn sd(&self) -> bool {
bf_extract(self.bits, usize::BITS as usize - 1, 1) != 0
}
/// Update whether either the FS field or XS field signals the presence of
/// some dirty state
///
/// Note this updates a previously read [`Mstatus`] value, but does not
/// affect the mstatus CSR itself.
#[inline]
pub fn set_sd(&mut self, sd: bool) {
self.bits = bf_insert(self.bits, usize::BITS as usize - 1, 1, sd as usize);
}
}
read_csr_as!(Mstatus, 0x300);
write_csr_as!(Mstatus, 0x300);
set!(0x300);
clear!(0x300);
set_clear_csr!(
/// User Interrupt Enable
, set_uie, clear_uie, 1 << 0);
set_clear_csr!(
/// Supervisor Interrupt Enable
, set_sie, clear_sie, 1 << 1);
set_clear_csr!(
/// Machine Interrupt Enable
, set_mie, clear_mie, 1 << 3);
set_csr!(
/// User Previous Interrupt Enable
, set_upie, 1 << 4);
set_csr!(
/// Supervisor Previous Interrupt Enable
, set_spie, 1 << 5);
set_csr!(
/// Machine Previous Interrupt Enable
, set_mpie, 1 << 7);
set_clear_csr!(
/// Modify Memory PRiVilege
, set_mprv, clear_mprv, 1 << 17);
set_clear_csr!(
/// Permit Supervisor User Memory access
, set_sum, clear_sum, 1 << 18);
set_clear_csr!(
/// Make eXecutable Readable
, set_mxr, clear_mxr, 1 << 19);
set_clear_csr!(
/// Trap Virtual Memory
, set_tvm, clear_tvm, 1 << 20);
set_clear_csr!(
/// Timeout Wait
, set_tw, clear_tw, 1 << 21);
set_clear_csr!(
/// Trap SRET
, set_tsr, clear_tsr, 1 << 22);
/// Set U-mode non-instruction-fetch memory endianness
#[inline]
pub unsafe fn set_ube(endianness: Endianness) {
match endianness {
Endianness::BigEndian => _set(1 << 6),
Endianness::LittleEndian => _clear(1 << 6),
}
}
/// Supervisor Previous Privilege Mode
#[inline]
pub unsafe fn set_spp(spp: SPP) {
match spp {
SPP::Supervisor => _set(1 << 8),
SPP::User => _clear(1 << 8),
}
}
/// Machine Previous Privilege Mode
#[inline]
pub unsafe fn set_mpp(mpp: MPP) {
let mut value = _read();
value &= !(0x3 << 11); // clear previous value
value |= (mpp as usize) << 11;
_write(value);
}
/// Floating-point extension state
#[inline]
pub unsafe fn set_fs(fs: FS) {
let mut value = _read();
value &= !(0x3 << 13); // clear previous value
value |= (fs as usize) << 13;
_write(value);
}
/// Vector extension state
#[inline]
pub unsafe fn set_vs(vs: VS) {
let mut value = _read();
value &= !(0x3 << 9); // clear previous value
value |= (vs as usize) << 9;
_write(value);
}
/// Set S-mode non-instruction-fetch memory endianness
///
/// # Note
///
/// In RISCV-32, this function calls [`crate::register::mstatush::set_sbe`]
#[inline]
pub unsafe fn set_sbe(endianness: Endianness) {
match () {
#[cfg(riscv32)]
() => super::mstatush::set_sbe(endianness),
#[cfg(not(riscv32))]
() => match endianness {
Endianness::BigEndian => _set(1 << 36),
Endianness::LittleEndian => _clear(1 << 36),
},
}
}
/// Set M-mode non-instruction-fetch memory endianness
///
/// # Note
///
/// In RISCV-32, this function calls [`crate::register::mstatush::set_mbe`]
#[inline]
pub unsafe fn set_mbe(endianness: Endianness) {
match () {
#[cfg(riscv32)]
() => super::mstatush::set_mbe(endianness),
#[cfg(not(riscv32))]
() => match endianness {
Endianness::BigEndian => _set(1 << 37),
Endianness::LittleEndian => _clear(1 << 37),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_mpp() {
let mut mstatus = Mstatus { bits: 0 };
mstatus.set_mpp(MPP::User);
assert_eq!(mstatus.mpp(), MPP::User);
mstatus.set_mpp(MPP::Machine);
assert_eq!(mstatus.mpp(), MPP::Machine);
mstatus.set_mpp(MPP::Supervisor);
assert_eq!(mstatus.mpp(), MPP::Supervisor);
}
}
+46
View File
@@ -0,0 +1,46 @@
//! mstatush register (RISCV-32 only)
pub use super::mstatus::Endianness;
/// mstatus register
#[derive(Clone, Copy, Debug)]
pub struct Mstatush {
bits: usize,
}
impl Mstatush {
/// S-mode non-instruction-fetch memory endianness
#[inline]
pub fn sbe(&self) -> Endianness {
Endianness::from(self.bits & (1 << 4) != 0)
}
/// M-mode non-instruction-fetch memory endianness
#[inline]
pub fn mbe(&self) -> Endianness {
Endianness::from(self.bits & (1 << 5) != 0)
}
}
read_csr_as_rv32!(Mstatush, 0x310);
write_csr_rv32!(0x310);
set_rv32!(0x310);
clear_rv32!(0x310);
/// Set S-mode non-instruction-fetch memory endianness
#[inline]
pub unsafe fn set_sbe(endianness: Endianness) {
match endianness {
Endianness::BigEndian => _set(1 << 4),
Endianness::LittleEndian => _clear(1 << 4),
}
}
/// Set M-mode non-instruction-fetch memory endianness
#[inline]
pub unsafe fn set_mbe(endianness: Endianness) {
match endianness {
Endianness::BigEndian => _set(1 << 5),
Endianness::LittleEndian => _clear(1 << 5),
}
}
+3
View File
@@ -0,0 +1,3 @@
//! mtval register
read_csr_as_usize!(0x343);
+50
View File
@@ -0,0 +1,50 @@
//! mtvec register
/// mtvec register
#[derive(Clone, Copy, Debug)]
pub struct Mtvec {
bits: usize,
}
/// Trap mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TrapMode {
Direct = 0,
Vectored = 1,
}
impl Mtvec {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the trap-vector base-address
#[inline]
pub fn address(&self) -> usize {
self.bits - (self.bits & 0b11)
}
/// Returns the trap-vector mode
#[inline]
pub fn trap_mode(&self) -> Option<TrapMode> {
let mode = self.bits & 0b11;
match mode {
0 => Some(TrapMode::Direct),
1 => Some(TrapMode::Vectored),
_ => None,
}
}
}
read_csr_as!(Mtvec, 0x305);
write_csr!(0x305);
/// Writes the CSR
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
let bits = addr + mode as usize;
_write(bits);
}
+34
View File
@@ -0,0 +1,34 @@
//! mvendorid register
use core::num::NonZeroUsize;
/// mvendorid register
#[derive(Clone, Copy, Debug)]
pub struct Mvendorid {
bits: NonZeroUsize,
}
impl Mvendorid {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits.get()
}
/// Returns the JEDEC manufacturer ID
#[inline]
pub fn jedec_manufacturer(&self) -> usize {
self.bits() >> 7
}
}
read_csr!(0xF11);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Mvendorid> {
let r = unsafe { _read() };
// When mvendorid is hardwired to zero it means that the mvendorid
// csr isn't implemented.
NonZeroUsize::new(r).map(|bits| Mvendorid { bits })
}
+28
View File
@@ -0,0 +1,28 @@
macro_rules! reg {
(
$addr:expr, $csr:ident
) => {
/// Physical memory protection address register
pub mod $csr {
read_csr_as_usize!($addr);
write_csr_as_usize!($addr);
}
};
}
reg!(0x3B0, pmpaddr0);
reg!(0x3B1, pmpaddr1);
reg!(0x3B2, pmpaddr2);
reg!(0x3B3, pmpaddr3);
reg!(0x3B4, pmpaddr4);
reg!(0x3B5, pmpaddr5);
reg!(0x3B6, pmpaddr6);
reg!(0x3B7, pmpaddr7);
reg!(0x3B8, pmpaddr8);
reg!(0x3B9, pmpaddr9);
reg!(0x3BA, pmpaddr10);
reg!(0x3BB, pmpaddr11);
reg!(0x3BC, pmpaddr12);
reg!(0x3BD, pmpaddr13);
reg!(0x3BE, pmpaddr14);
reg!(0x3BF, pmpaddr15);
+179
View File
@@ -0,0 +1,179 @@
//! Physical memory protection configuration
use crate::result::{Error, Result};
/// Permission enum contains all possible permission modes for pmp registers
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Permission {
NONE = 0b000,
R = 0b001,
W = 0b010,
RW = 0b011,
X = 0b100,
RX = 0b101,
WX = 0b110,
RWX = 0b111,
}
impl TryFrom<u8> for Permission {
type Error = Error;
fn try_from(val: u8) -> Result<Self> {
match val {
0b000 => Ok(Self::NONE),
0b001 => Ok(Self::R),
0b010 => Ok(Self::W),
0b011 => Ok(Self::RW),
0b100 => Ok(Self::X),
0b101 => Ok(Self::RX),
0b110 => Ok(Self::WX),
0b111 => Ok(Self::RWX),
_ => Err(Error::InvalidFieldValue {
field: "permission",
value: val as usize,
bitmask: 0b111,
}),
}
}
}
/// Range enum contains all possible addressing modes for pmp registers
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Range {
OFF = 0b00,
TOR = 0b01,
NA4 = 0b10,
NAPOT = 0b11,
}
impl TryFrom<u8> for Range {
type Error = Error;
fn try_from(val: u8) -> Result<Self> {
match val {
0b00 => Ok(Self::OFF),
0b01 => Ok(Self::TOR),
0b10 => Ok(Self::NA4),
0b11 => Ok(Self::NAPOT),
_ => Err(Error::InvalidFieldValue {
field: "range",
value: val as usize,
bitmask: 0b11,
}),
}
}
}
/// Pmp struct holds a high-level representation of a single pmp configuration
#[derive(Clone, Copy, Debug)]
pub struct Pmp {
/// raw bits
pub byte: u8,
/// Current PMP Permission
pub permission: Permission,
/// Current PMP Range
pub range: Range,
/// Is PMP locked?
pub locked: bool,
}
pub struct Pmpcsr {
/// Holds the raw contents of a PMP CSR Register
pub bits: usize,
}
impl Pmpcsr {
/// Take the register contents and translate into a Pmp configuration struct
///
/// **WARNING**: panics on:
///
/// - non-`riscv` targets
/// - `index` is out of bounds
/// - register fields contain invalid values
#[inline]
pub fn into_config(&self, index: usize) -> Pmp {
self.try_into_config(index).unwrap()
}
/// Attempts to take the register contents, and translate into a Pmp configuration struct.
#[inline]
pub fn try_into_config(&self, index: usize) -> Result<Pmp> {
let max = match () {
#[cfg(riscv32)]
() => Ok(4usize),
#[cfg(riscv64)]
() => Ok(8usize),
#[cfg(not(any(riscv32, riscv64)))]
() => Err(Error::Unimplemented),
}?;
if index < max {
let byte = (self.bits >> (8 * index)) as u8; // move config to LSB and drop the rest
let permission = byte & 0x7; // bits 0-2
let range = (byte >> 3) & 0x3; // bits 3-4
Ok(Pmp {
byte,
permission: permission.try_into()?,
range: range.try_into()?,
locked: (byte & (1 << 7)) != 0,
})
} else {
Err(Error::IndexOutOfBounds {
index,
min: 0,
max: max - 1,
})
}
}
}
/// Physical memory protection configuration
/// pmpcfg0 struct contains pmp0cfg - pmp3cfg for RV32, and pmp0cfg - pmp7cfg for RV64
pub mod pmpcfg0 {
use super::{Permission, Pmpcsr, Range};
read_csr_as!(Pmpcsr, 0x3A0);
write_csr_as_usize!(0x3A0);
set_pmp!();
clear_pmp!();
}
/// Physical memory protection configuration
/// pmpcfg1 struct contains pmp4cfg - pmp7cfg for RV32 only
#[cfg(riscv32)]
pub mod pmpcfg1 {
use super::{Permission, Pmpcsr, Range};
read_csr_as!(Pmpcsr, 0x3A1);
write_csr_as_usize_rv32!(0x3A1);
set_pmp!();
clear_pmp!();
}
/// Physical memory protection configuration
/// pmpcfg2 struct contains pmp8cfg - pmp11cfg for RV32, or pmp8cfg - pmp15cfg for RV64
pub mod pmpcfg2 {
use super::{Permission, Pmpcsr, Range};
read_csr_as!(Pmpcsr, 0x3A2);
write_csr_as_usize!(0x3A2);
set_pmp!();
clear_pmp!();
}
/// Physical memory protection configuration
/// pmpcfg3 struct contains pmp12cfg - pmp15cfg for RV32 only
#[cfg(riscv32)]
pub mod pmpcfg3 {
use super::{Permission, Pmpcsr, Range};
read_csr_as!(Pmpcsr, 0x3A3);
write_csr_as_usize_rv32!(0x3A3);
set_pmp!();
clear_pmp!();
}
+209
View File
@@ -0,0 +1,209 @@
//! satp register
use crate::result::{Error, Result};
/// satp register
#[derive(Clone, Copy, Debug)]
pub struct Satp {
bits: usize,
}
impl Satp {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Current address-translation scheme
///
/// **WARNING**: panics if the field has an invalid variant.
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn mode(&self) -> Mode {
self.try_mode().unwrap()
}
/// Attempts to get the current address-translation scheme.
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn try_mode(&self) -> Result<Mode> {
((self.bits >> 31) as u8).try_into()
}
/// Current address-translation scheme
///
/// **WARNING**: panics if the field has an invalid variant.
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn mode(&self) -> Mode {
self.try_mode().unwrap()
}
/// Attempts to get the current address-translation scheme.
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn try_mode(&self) -> Result<Mode> {
((self.bits >> 60) as u8).try_into()
}
/// Address space identifier
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn asid(&self) -> usize {
(self.bits >> 22) & 0x1FF // bits 22-30
}
/// Address space identifier
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn asid(&self) -> usize {
self.bits >> 44 & 0xFFFF // bits 44-59
}
/// Physical page number
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn ppn(&self) -> usize {
self.bits & 0x3F_FFFF // bits 0-21
}
/// Physical page number
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn ppn(&self) -> usize {
self.bits & 0xFFF_FFFF_FFFF // bits 0-43
}
}
/// 32-bit satp mode
#[cfg(target_pointer_width = "32")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Mode {
/// No translation or protection
Bare = 0,
/// Page-based 32-bit virtual addressing
Sv32 = 1,
}
/// 64-bit satp mode
#[cfg(target_pointer_width = "64")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Mode {
/// No translation or protection
Bare = 0,
/// Page-based 39-bit virtual addressing
Sv39 = 8,
/// Page-based 48-bit virtual addressing
Sv48 = 9,
/// Page-based 57-bit virtual addressing
Sv57 = 10,
/// Page-based 64-bit virtual addressing
Sv64 = 11,
}
#[cfg(target_pointer_width = "32")]
impl TryFrom<u8> for Mode {
type Error = Error;
fn try_from(val: u8) -> Result<Self> {
match val {
0 => Ok(Mode::Bare),
1 => Ok(Mode::Sv32),
_ => Err(Error::InvalidFieldVariant {
field: "mode",
value: val as usize,
}),
}
}
}
#[cfg(target_pointer_width = "64")]
impl TryFrom<u8> for Mode {
type Error = Error;
fn try_from(val: u8) -> Result<Self> {
match val {
0 => Ok(Mode::Bare),
8 => Ok(Mode::Sv39),
9 => Ok(Mode::Sv48),
10 => Ok(Mode::Sv57),
11 => Ok(Mode::Sv64),
_ => Err(Error::InvalidFieldVariant {
field: "mode",
value: val as usize,
}),
}
}
}
read_csr_as!(Satp, 0x180);
write_csr_as_usize!(0x180);
/// Sets the register to corresponding page table mode, physical page number and address space id.
///
/// **WARNING**: panics on:
///
/// - non-`riscv` targets
/// - invalid field values
#[inline]
#[cfg(target_pointer_width = "32")]
pub unsafe fn set(mode: Mode, asid: usize, ppn: usize) {
try_set(mode, asid, ppn).unwrap();
}
/// Attempts to set the register to corresponding page table mode, physical page number and address space id.
#[inline]
#[cfg(target_pointer_width = "32")]
pub unsafe fn try_set(mode: Mode, asid: usize, ppn: usize) -> Result<()> {
if asid != asid & 0x1FF {
Err(Error::InvalidFieldValue {
field: "asid",
value: asid,
bitmask: 0x1FF,
})
} else if ppn != ppn & 0x3F_FFFF {
Err(Error::InvalidFieldValue {
field: "ppn",
value: ppn,
bitmask: 0x3F_FFFF,
})
} else {
let bits = (mode as usize) << 31 | (asid << 22) | ppn;
_try_write(bits)
}
}
/// Sets the register to corresponding page table mode, physical page number and address space id.
///
/// **WARNING**: panics on:
///
/// - non-`riscv` targets
/// - invalid field values
#[inline]
#[cfg(target_pointer_width = "64")]
pub unsafe fn set(mode: Mode, asid: usize, ppn: usize) {
try_set(mode, asid, ppn).unwrap()
}
/// Attempts to set the register to corresponding page table mode, physical page number and address space id.
#[inline]
#[cfg(target_pointer_width = "64")]
pub unsafe fn try_set(mode: Mode, asid: usize, ppn: usize) -> Result<()> {
if asid != asid & 0xFFFF {
Err(Error::InvalidFieldValue {
field: "asid",
value: asid,
bitmask: 0xFFFF,
})
} else if ppn != ppn & 0xFFF_FFFF_FFFF {
Err(Error::InvalidFieldValue {
field: "ppn",
value: ppn,
bitmask: 0xFFF_FFFF_FFFF,
})
} else {
let bits = (mode as usize) << 60 | (asid << 44) | ppn;
_try_write(bits)
}
}
+72
View File
@@ -0,0 +1,72 @@
//! scause register
pub use crate::interrupt::Trap;
pub use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits
/// scause register
#[derive(Clone, Copy)]
pub struct Scause {
bits: usize,
}
impl Scause {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the code field
#[inline]
pub fn code(&self) -> usize {
self.bits & !(1 << (usize::BITS as usize - 1))
}
/// Returns the trap cause represented by this register.
///
/// # Note
///
/// This method returns a **raw trap cause**, which means that values are represented as `usize`.
/// To get a target-specific trap cause, use [`Trap::try_into`] with your target-specific S-Mode trap cause types.
#[inline]
pub fn cause(&self) -> Trap<usize, usize> {
if self.is_interrupt() {
Trap::Interrupt(self.code())
} else {
Trap::Exception(self.code())
}
}
/// Is trap cause an interrupt.
#[inline]
pub fn is_interrupt(&self) -> bool {
self.bits & (1 << (usize::BITS as usize - 1)) != 0
}
/// Is trap cause an exception.
#[inline]
pub fn is_exception(&self) -> bool {
!self.is_interrupt()
}
}
read_csr_as!(Scause, 0x142);
write_csr!(0x142);
/// Writes the CSR
#[inline]
pub unsafe fn write(bits: usize) {
_write(bits)
}
/// Set supervisor cause register to corresponding cause.
#[inline]
pub unsafe fn set<I: CoreInterruptNumber, E: ExceptionNumber>(cause: Trap<I, E>) {
let bits = match cause {
Trap::Interrupt(i) => {
i.number() | (1 << (usize::BITS as usize - 1)) // interrupt bit is 1
}
Trap::Exception(e) => e.number(),
};
_write(bits);
}
+107
View File
@@ -0,0 +1,107 @@
//! scounteren register
use crate::result::{Error, Result};
/// scounteren register
#[derive(Clone, Copy, Debug)]
pub struct Scounteren {
bits: usize,
}
impl Scounteren {
/// User "cycle\[h\]" Enable
#[inline]
pub fn cy(&self) -> bool {
self.bits & (1 << 0) != 0
}
/// User "time\[h\]" Enable
#[inline]
pub fn tm(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// User "instret\[h]\" Enable
#[inline]
pub fn ir(&self) -> bool {
self.bits & (1 << 2) != 0
}
/// User "hpm\[x\]" Enable (bits 3-31)
#[inline]
pub fn hpm(&self, index: usize) -> bool {
assert!((3..32).contains(&index));
self.bits & (1 << index) != 0
}
/// User "hpm\[x\]" Enable (bits 3-31)
///
/// Attempts to read the "hpm\[x\]" value, and returns an error if the index is invalid.
#[inline]
pub fn try_hpm(&self, index: usize) -> Result<bool> {
if (3..32).contains(&index) {
Ok(self.bits & (1 << index) != 0)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
}
read_csr_as!(Scounteren, 0x106);
write_csr!(0x106);
set!(0x106);
clear!(0x106);
set_clear_csr!(
/// User cycle Enable
, set_cy, clear_cy, 1 << 0);
set_clear_csr!(
/// User time Enable
, set_tm, clear_tm, 1 << 1);
set_clear_csr!(
/// User instret Enable
, set_ir, clear_ir, 1 << 2);
#[inline]
pub unsafe fn set_hpm(index: usize) {
assert!((3..32).contains(&index));
_set(1 << index);
}
#[inline]
pub unsafe fn try_set_hpm(index: usize) -> Result<()> {
if (3..32).contains(&index) {
_try_set(1 << index)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
#[inline]
pub unsafe fn clear_hpm(index: usize) {
assert!((3..32).contains(&index));
_clear(1 << index);
}
#[inline]
pub unsafe fn try_clear_hpm(index: usize) -> Result<()> {
if (3..32).contains(&index) {
_try_clear(1 << index)
} else {
Err(Error::IndexOutOfBounds {
index,
min: 3,
max: 31,
})
}
}
+4
View File
@@ -0,0 +1,4 @@
//! sepc register
read_csr_as_usize!(0x141);
write_csr_as_usize!(0x141);
+47
View File
@@ -0,0 +1,47 @@
//! sie register
/// sie register
#[derive(Clone, Copy, Debug)]
pub struct Sie {
bits: usize,
}
impl Sie {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Supervisor Software Interrupt Enable
#[inline]
pub fn ssoft(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// Supervisor Timer Interrupt Enable
#[inline]
pub fn stimer(&self) -> bool {
self.bits & (1 << 5) != 0
}
/// Supervisor External Interrupt Enable
#[inline]
pub fn sext(&self) -> bool {
self.bits & (1 << 9) != 0
}
}
read_csr_as!(Sie, 0x104);
set!(0x104);
clear!(0x104);
set_clear_csr!(
/// Supervisor Software Interrupt Enable
, set_ssoft, clear_ssoft, 1 << 1);
set_clear_csr!(
/// Supervisor Timer Interrupt Enable
, set_stimer, clear_stimer, 1 << 5);
set_clear_csr!(
/// Supervisor External Interrupt Enable
, set_sext, clear_sext, 1 << 9);
+41
View File
@@ -0,0 +1,41 @@
//! sip register
/// sip register
#[derive(Clone, Copy, Debug)]
pub struct Sip {
bits: usize,
}
impl Sip {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Supervisor Software Interrupt Pending
#[inline]
pub fn ssoft(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// Supervisor Timer Interrupt Pending
#[inline]
pub fn stimer(&self) -> bool {
self.bits & (1 << 5) != 0
}
/// Supervisor External Interrupt Pending
#[inline]
pub fn sext(&self) -> bool {
self.bits & (1 << 9) != 0
}
}
read_csr_as!(Sip, 0x144);
set!(0x144);
clear!(0x144);
set_clear_csr!(
/// Supervisor Software Interrupt Pending
, set_ssoft, clear_ssoft, 1 << 1);
+4
View File
@@ -0,0 +1,4 @@
//! sscratch register
read_csr_as_usize!(0x140);
write_csr_as_usize!(0x140);
+141
View File
@@ -0,0 +1,141 @@
//! sstatus register
pub use super::misa::XLEN;
pub use super::mstatus::FS;
/// Supervisor Status Register
#[derive(Clone, Copy, Debug)]
pub struct Sstatus {
bits: usize,
}
/// Supervisor Previous Privilege Mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SPP {
Supervisor = 1,
User = 0,
}
impl Sstatus {
/// Supervisor Interrupt Enable
#[inline]
pub fn sie(&self) -> bool {
self.bits & (1 << 1) != 0
}
/// Supervisor Previous Interrupt Enable
#[inline]
pub fn spie(&self) -> bool {
self.bits & (1 << 5) != 0
}
/// Supervisor Previous Privilege Mode
#[inline]
pub fn spp(&self) -> SPP {
match self.bits & (1 << 8) != 0 {
true => SPP::Supervisor,
false => SPP::User,
}
}
/// The status of the floating-point unit
#[inline]
pub fn fs(&self) -> FS {
let fs = (self.bits >> 13) & 0x3; // bits 13-14
match fs {
0 => FS::Off,
1 => FS::Initial,
2 => FS::Clean,
3 => FS::Dirty,
_ => unreachable!(),
}
}
/// The status of additional user-mode extensions
/// and associated state
#[inline]
pub fn xs(&self) -> FS {
let xs = (self.bits >> 15) & 0x3; // bits 15-16
match xs {
0 => FS::Off,
1 => FS::Initial,
2 => FS::Clean,
3 => FS::Dirty,
_ => unreachable!(),
}
}
/// Permit Supervisor User Memory access
#[inline]
pub fn sum(&self) -> bool {
self.bits & (1 << 18) != 0
}
/// Make eXecutable Readable
#[inline]
pub fn mxr(&self) -> bool {
self.bits & (1 << 19) != 0
}
/// Effective xlen in U-mode (i.e., `UXLEN`).
///
/// In RISCV-32, UXL does not exist, and `UXLEN` is always [`XLEN::XLEN32`].
#[inline]
pub fn uxl(&self) -> XLEN {
match () {
#[cfg(riscv32)]
() => XLEN::XLEN32,
#[cfg(not(riscv32))]
() => XLEN::from((self.bits >> 32) as u8 & 0x3),
}
}
/// Whether either the FS field or XS field
/// signals the presence of some dirty state
#[inline]
pub fn sd(&self) -> bool {
self.bits & (1 << (usize::BITS as usize - 1)) != 0
}
}
read_csr_as!(Sstatus, 0x100);
write_csr!(0x100);
set!(0x100);
clear!(0x100);
set_clear_csr!(
/// User Interrupt Enable
, set_uie, clear_uie, 1 << 0);
set_clear_csr!(
/// Supervisor Interrupt Enable
, set_sie, clear_sie, 1 << 1);
set_csr!(
/// User Previous Interrupt Enable
, set_upie, 1 << 4);
set_csr!(
/// Supervisor Previous Interrupt Enable
, set_spie, 1 << 5);
set_clear_csr!(
/// Permit Supervisor User Memory access
, set_sum, clear_sum, 1 << 18);
set_clear_csr!(
/// Make eXecutable Readable
, set_mxr, clear_mxr, 1 << 19);
/// Supervisor Previous Privilege Mode
#[inline]
pub unsafe fn set_spp(spp: SPP) {
match spp {
SPP::Supervisor => _set(1 << 8),
SPP::User => _clear(1 << 8),
}
}
/// The status of the floating-point unit
#[inline]
pub unsafe fn set_fs(fs: FS) {
let mut value = _read();
value &= !(0x3 << 13); // clear previous value
value |= (fs as usize) << 13;
_write(value);
}
+10
View File
@@ -0,0 +1,10 @@
//! stval register
read_csr_as_usize!(0x143);
write_csr!(0x143);
/// Writes the CSR
#[inline]
pub unsafe fn write(bits: usize) {
_write(bits)
}
+43
View File
@@ -0,0 +1,43 @@
//! stvec register
pub use crate::register::mtvec::TrapMode;
/// stvec register
#[derive(Clone, Copy, Debug)]
pub struct Stvec {
bits: usize,
}
impl Stvec {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the trap-vector base-address
#[inline]
pub fn address(&self) -> usize {
self.bits - (self.bits & 0b11)
}
/// Returns the trap-vector mode
#[inline]
pub fn trap_mode(&self) -> Option<TrapMode> {
let mode = self.bits & 0b11;
match mode {
0 => Some(TrapMode::Direct),
1 => Some(TrapMode::Vectored),
_ => None,
}
}
}
read_csr_as!(Stvec, 0x105);
write_csr!(0x105);
/// Writes the CSR
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
_write(addr + mode as usize);
}
+3
View File
@@ -0,0 +1,3 @@
mod read_only_csr;
mod read_write_csr;
mod write_only_csr;
+121
View File
@@ -0,0 +1,121 @@
use crate::result::{Error, Result};
read_only_csr! {
/// test CSR register type
Mtest: 0x000,
mask: 0b1111_1111_1111,
}
read_only_csr_field! {
Mtest,
/// test single-bit field
single: 0,
}
read_only_csr_field! {
Mtest,
/// multiple single-bit field range
multi_range: 1..=3,
}
read_only_csr_field! {
Mtest,
/// multi-bit field
multi_field: [4:7],
}
csr_field_enum! {
/// field enum type with valid field variants
MtestFieldEnum {
range: [8:11],
default: Field1,
Field1 = 1,
Field2 = 2,
Field3 = 3,
Field4 = 15,
}
}
read_only_csr_field! {
Mtest,
/// multi-bit field
field_enum,
MtestFieldEnum: [8:11],
}
// we don't test the `read` function, we are only testing in-memory functions.
#[allow(unused)]
pub fn _read_csr() -> Mtest {
read()
}
#[allow(unused)]
pub fn _try_read_csr() -> Result<Mtest> {
try_read()
}
#[test]
fn test_mtest_read_only() {
let mut mtest = Mtest::from_bits(0);
assert_eq!(mtest.bitmask(), Mtest::BITMASK);
assert_eq!(mtest.bits(), 0);
// check that single bit field getter/setters work.
assert!(!mtest.single());
mtest = Mtest::from_bits(1);
assert!(mtest.single());
mtest = Mtest::from_bits(0);
// check that single bit range field getter/setters work.
for i in 1..=3 {
assert!(!mtest.multi_range(i));
assert_eq!(mtest.try_multi_range(i), Ok(false));
mtest = Mtest::from_bits(1 << i);
assert!(mtest.multi_range(i));
assert_eq!(mtest.try_multi_range(i), Ok(true));
mtest = Mtest::from_bits(0 << i);
assert!(!mtest.multi_range(i));
assert_eq!(mtest.try_multi_range(i), Ok(false));
}
// check that multi-bit field getter/setters work.
assert_eq!(mtest.multi_field(), 0);
mtest = Mtest::from_bits(0xf << 4);
assert_eq!(mtest.multi_field(), 0xf);
mtest = Mtest::from_bits(0x3 << 4);
assert_eq!(mtest.multi_field(), 0x3);
// check that only bits in the field are set.
mtest = Mtest::from_bits(0xff << 4);
assert_eq!(mtest.multi_field(), 0xf);
assert_eq!(mtest.bits(), 0xff << 4);
mtest = Mtest::from_bits(0x0 << 4);
assert_eq!(mtest.multi_field(), 0x0);
assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(0)),);
[
MtestFieldEnum::Field1,
MtestFieldEnum::Field2,
MtestFieldEnum::Field3,
MtestFieldEnum::Field4,
]
.into_iter()
.for_each(|variant| {
mtest = Mtest::from_bits(variant.into_usize() << 8);
assert_eq!(mtest.field_enum(), variant);
assert_eq!(mtest.try_field_enum(), Ok(variant));
});
// check that setting an invalid variant returns `None`
mtest = Mtest::from_bits(0xbad << 8);
assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(13)),);
}
+129
View File
@@ -0,0 +1,129 @@
use crate::result::{Error, Result};
read_write_csr! {
/// test CSR register type
Mtest: 0x000,
mask: 0b1111_1111_1111,
}
read_write_csr_field! {
Mtest,
/// test single-bit field
single: 0,
}
read_write_csr_field! {
Mtest,
/// multiple single-bit field range
multi_range: 1..=3,
}
read_write_csr_field! {
Mtest,
/// multi-bit field
multi_field: [4:7],
}
csr_field_enum! {
/// field enum type with valid field variants
MtestFieldEnum {
range: [8:11],
default: Field1,
Field1 = 1,
Field2 = 2,
Field3 = 3,
Field4 = 15,
}
}
read_write_csr_field! {
Mtest,
/// multi-bit field
field_enum,
MtestFieldEnum: [8:11],
}
// we don't test the `read` and `write` functions, we are only testing in-memory functions.
#[allow(unused)]
pub fn _read_csr() -> Mtest {
read()
}
#[allow(unused)]
pub fn _try_read_csr() -> Result<Mtest> {
try_read()
}
#[allow(unused)]
pub fn _write_csr(csr: Mtest) {
write(csr);
}
#[allow(unused)]
pub fn _try_write_csr(csr: Mtest) {
try_write(csr);
}
#[test]
fn test_mtest_read_write() {
let mut mtest = Mtest::from_bits(0);
assert_eq!(mtest.bitmask(), Mtest::BITMASK);
assert_eq!(mtest.bits(), 0);
// check that single bit field getter/setters work.
assert!(!mtest.single());
mtest.set_single(true);
assert!(mtest.single());
mtest.set_single(false);
assert!(!mtest.single());
// check that single bit range field getter/setters work.
for i in 1..=3 {
assert!(!mtest.multi_range(i));
mtest.set_multi_range(i, true);
assert!(mtest.multi_range(i));
mtest.set_multi_range(i, false);
assert!(!mtest.multi_range(i));
}
// check that multi-bit field getter/setters work.
assert_eq!(mtest.multi_field(), 0);
mtest.set_multi_field(0xf);
assert_eq!(mtest.multi_field(), 0xf);
mtest.set_multi_field(0x3);
assert_eq!(mtest.multi_field(), 0x3);
// check that only bits in the field are set.
mtest.set_multi_field(0xff);
assert_eq!(mtest.multi_field(), 0xf);
assert_eq!(mtest.bits(), 0xf << 4);
mtest.set_multi_field(0x0);
assert_eq!(mtest.multi_field(), 0x0);
assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(0)));
[
MtestFieldEnum::Field1,
MtestFieldEnum::Field2,
MtestFieldEnum::Field3,
MtestFieldEnum::Field4,
]
.into_iter()
.for_each(|variant| {
mtest.set_field_enum(variant);
assert_eq!(mtest.field_enum(), variant);
assert_eq!(mtest.try_field_enum(), Ok(variant));
});
// check that setting an invalid variant returns `None`
mtest = Mtest::from_bits(0xbad << 8);
assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(13)));
}
+119
View File
@@ -0,0 +1,119 @@
use crate::result::{Error, Result};
write_only_csr! {
/// test CSR register type
Mtest: 0x000,
mask: 0b1111_1111_1111,
}
write_only_csr_field! {
Mtest,
/// setter test single-bit field
set_single: 0,
}
write_only_csr_field! {
Mtest,
/// setter multiple single-bit field range
set_multi_range: 1..=3,
}
write_only_csr_field! {
Mtest,
/// setter multi-bit field
set_multi_field: [4:7],
}
csr_field_enum! {
/// field enum type with valid field variants
MtestFieldEnum {
range: [8:11],
default: Field1,
Field1 = 1,
Field2 = 2,
Field3 = 3,
Field4 = 15,
}
}
write_only_csr_field! {
Mtest,
/// setter multi-bit field
set_field_enum,
MtestFieldEnum: [8:11],
}
// we don't test the `write` function, we are only testing in-memory functions.
#[allow(unused)]
pub fn _write_csr(csr: Mtest) {
write(csr);
}
#[allow(unused)]
pub fn _try_write_csr(csr: Mtest) -> Result<()> {
try_write(csr)
}
#[test]
fn test_mtest_write_only() {
let mut mtest = Mtest::from_bits(0);
assert_eq!(mtest.bitmask(), Mtest::BITMASK);
assert_eq!(mtest.bits(), 0);
// check that single bit field getter/setters work.
mtest.set_single(true);
assert_eq!(mtest.bits(), 1);
mtest.set_single(false);
assert_eq!(mtest.bits(), 0);
// check that single bit range field getter/setters work.
for i in 1..=3 {
mtest.set_multi_range(i, true);
assert_ne!(mtest.bits() & (1 << i), 0);
mtest.set_multi_range(i, false);
assert_eq!(mtest.bits() & (1 << i), 0);
}
// check that multi-bit field getter/setters work.
mtest.set_multi_field(0xf);
assert_eq!(mtest.bits() >> 4, 0xf);
mtest.set_multi_field(0x3);
assert_eq!(mtest.bits() >> 4, 0x3);
// check that only bits in the field are set.
mtest.set_multi_field(0xff);
assert_eq!(mtest.bits() >> 4, 0xf);
mtest.set_multi_field(0x0);
assert_eq!(mtest.bits() >> 4, 0x0);
mtest = Mtest::from_bits(0);
assert_eq!(
MtestFieldEnum::from_usize(mtest.bits() >> 8),
Err(Error::InvalidVariant(0))
);
[
MtestFieldEnum::Field1,
MtestFieldEnum::Field2,
MtestFieldEnum::Field3,
MtestFieldEnum::Field4,
]
.into_iter()
.for_each(|variant| {
mtest.set_field_enum(variant);
assert_eq!(MtestFieldEnum::from_usize(mtest.bits() >> 8), Ok(variant));
});
// check that setting an invalid variant returns `None`
mtest = Mtest::from_bits(0xbad << 8);
assert_eq!(
MtestFieldEnum::from_usize(mtest.bits() >> 8),
Err(Error::InvalidVariant(13))
);
}
+4
View File
@@ -0,0 +1,4 @@
//! time register
read_csr_as_usize!(0xC01);
read_composite_csr!(super::timeh::read(), read());
+3
View File
@@ -0,0 +1,3 @@
//! timeh register
read_csr_as_usize_rv32!(0xC81);