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:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+163
@@ -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!(),
|
||||
}
|
||||
}
|
||||
Vendored
+17
@@ -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
|
||||
}
|
||||
Vendored
+29
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+30
@@ -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 {}
|
||||
}
|
||||
}
|
||||
Vendored
+98
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
Vendored
+62
@@ -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;
|
||||
}
|
||||
Vendored
+57
@@ -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()
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
Vendored
+115
@@ -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
|
||||
Vendored
+8
@@ -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());
|
||||
Vendored
+7
@@ -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
@@ -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);
|
||||
Vendored
+8
@@ -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
@@ -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);
|
||||
Vendored
+1030
File diff suppressed because it is too large.
Load diff
Vendored
+28
@@ -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 })
|
||||
}
|
||||
Vendored
+59
@@ -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
@@ -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
@@ -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
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
//! mcycle register
|
||||
|
||||
read_csr_as_usize!(0xB00);
|
||||
read_composite_csr!(super::mcycleh::read(), read());
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
//! mcycleh register
|
||||
|
||||
read_csr_as_usize_rv32!(0xB80);
|
||||
Vendored
+137
@@ -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);
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
//! mepc register
|
||||
|
||||
read_csr_as_usize!(0x341);
|
||||
write_csr_as_usize!(0x341);
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
//! mhartid register
|
||||
|
||||
read_csr_as_usize!(0xf14);
|
||||
+84
@@ -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
@@ -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);
|
||||
Vendored
+47
@@ -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);
|
||||
Vendored
+74
@@ -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);
|
||||
Vendored
+28
@@ -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
@@ -0,0 +1,4 @@
|
||||
//! minstret register
|
||||
|
||||
read_csr_as_usize!(0xB02);
|
||||
read_composite_csr!(super::minstreth::read(), read());
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
//! minstreth register
|
||||
|
||||
read_csr_as_usize_rv32!(0xB82);
|
||||
Vendored
+65
@@ -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);
|
||||
Vendored
+72
@@ -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
@@ -0,0 +1,4 @@
|
||||
//! mscratch register
|
||||
|
||||
read_csr_as_usize!(0x340);
|
||||
write_csr_as_usize!(0x340);
|
||||
Vendored
+653
@@ -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
@@ -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),
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
//! mtval register
|
||||
|
||||
read_csr_as_usize!(0x343);
|
||||
Vendored
+50
@@ -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
@@ -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
@@ -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);
|
||||
Vendored
+179
@@ -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!();
|
||||
}
|
||||
Vendored
+209
@@ -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)
|
||||
}
|
||||
}
|
||||
Vendored
+72
@@ -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
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
//! sepc register
|
||||
|
||||
read_csr_as_usize!(0x141);
|
||||
write_csr_as_usize!(0x141);
|
||||
Vendored
+47
@@ -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);
|
||||
Vendored
+41
@@ -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
@@ -0,0 +1,4 @@
|
||||
//! sscratch register
|
||||
|
||||
read_csr_as_usize!(0x140);
|
||||
write_csr_as_usize!(0x140);
|
||||
Vendored
+141
@@ -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);
|
||||
}
|
||||
Vendored
+10
@@ -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)
|
||||
}
|
||||
Vendored
+43
@@ -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);
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
mod read_only_csr;
|
||||
mod read_write_csr;
|
||||
mod write_only_csr;
|
||||
+121
@@ -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
@@ -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
@@ -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))
|
||||
);
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
//! time register
|
||||
|
||||
read_csr_as_usize!(0xC01);
|
||||
read_composite_csr!(super::timeh::read(), read());
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
//! timeh register
|
||||
|
||||
read_csr_as_usize_rv32!(0xC81);
|
||||
Reference in new issue
Block a user