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

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