diff --git a/CHANGELOG.md b/CHANGELOG.md index 133f2eba..76183464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Add `read_csr_as_rv32`, `set_rv32`, and `clear_rv32` macros +- Add `mstatus::uxl` and `mstatus::sxl` +- Add `mstatus::ube`, `mstatus::sbe`, and `mstatus::mbe` endianness bit fields +- Add `mstatush` registers (RISCV-32 only) - Add generic implementation of a PLIC peripheral - Add `asm::fence()`, a wrapper for implementing a `fence` instruction - Add `asm::fence_i()`, a wrapper for implementing a `fence.i` instruction @@ -16,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- `misa::MXL` renamed to `misa::XLEN` - Removed `bit_field` dependency - CI actions updated. They now use `checkout@v3` and `dtolnay/rust-toolchain`. - `mcause::{Interrupt, Exception}` and `scause::{Interrupt, Exception}` now implement `From` trait for `usize` @@ -24,7 +29,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fix `scause::Exception` missing `LoadMisaligned` - Fix `scause::Exception` missing `SupervisorEnvCall` -- Removed user-level interrupts from `mcause::Interrupt` and `scause::Interrupt` +- Removed user-level interrupts from `mcause::Interrupt` and `scause::Interrupt` +- Removed user-level interrupts from `mstatus` ## [v0.10.1] - 2023-01-18 diff --git a/src/register/mod.rs b/src/register.rs similarity index 98% rename from src/register/mod.rs rename to src/register.rs index baf0c8e3..71670f3c 100644 --- a/src/register/mod.rs +++ b/src/register.rs @@ -9,6 +9,7 @@ //! - mcycleh //! - minstreth //! - mhpmcounter<3-31>h +//! - mstatush #[macro_use] mod macros; @@ -69,6 +70,7 @@ pub mod mideleg; pub mod mie; pub mod misa; pub mod mstatus; +pub mod mstatush; pub mod mtvec; // Machine Trap Handling diff --git a/src/register/macros.rs b/src/register/macros.rs index 59089efc..24f880f9 100644 --- a/src/register/macros.rs +++ b/src/register/macros.rs @@ -52,6 +52,20 @@ macro_rules! read_csr_as { }; } +macro_rules! read_csr_as_rv32 { + ($register:ident, $csr_number:literal) => { + read_csr_rv32!($csr_number); + + /// Reads the CSR + #[inline] + pub fn read() -> $register { + $register { + bits: unsafe { _read() }, + } + } + }; +} + macro_rules! read_csr_as_usize { ($csr_number:literal) => { read_csr!($csr_number); @@ -151,6 +165,23 @@ macro_rules! set { }; } +macro_rules! set_rv32 { + ($csr_number:literal) => { + /// Set the CSR + #[inline] + #[allow(unused_variables)] + unsafe fn _set(bits: usize) { + match () { + #[cfg(riscv32)] + () => core::arch::asm!(concat!("csrrs x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + + #[cfg(not(riscv32))] + () => unimplemented!(), + } + } + }; +} + macro_rules! clear { ($csr_number:literal) => { /// Clear the CSR @@ -168,6 +199,23 @@ macro_rules! clear { }; } +macro_rules! clear_rv32 { + ($csr_number:literal) => { + /// Clear the CSR + #[inline] + #[allow(unused_variables)] + unsafe fn _clear(bits: usize) { + match () { + #[cfg(riscv32)] + () => core::arch::asm!(concat!("csrrc x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + + #[cfg(not(riscv32))] + () => unimplemented!(), + } + } + }; +} + macro_rules! set_csr { ($(#[$attr:meta])*, $set_field:ident, $e:expr) => { $(#[$attr])* diff --git a/src/register/misa.rs b/src/register/misa.rs index daf6b8db..53719b6d 100644 --- a/src/register/misa.rs +++ b/src/register/misa.rs @@ -8,14 +8,26 @@ pub struct Misa { bits: NonZeroUsize, } -/// Machine XLEN +/// Base integer ISA width #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum MXL { +pub enum XLEN { XLEN32, XLEN64, XLEN128, } +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] @@ -23,24 +35,21 @@ impl Misa { self.bits.get() } - /// Returns the machine xlen. + /// Effective xlen in M-mode (i.e., `MXLEN`). #[inline] - pub fn mxl(&self) -> MXL { - let value = match () { - #[cfg(target_pointer_width = "32")] - () => (self.bits() >> 30) as u8, - #[cfg(target_pointer_width = "64")] - () => (self.bits() >> 62) as u8, - }; - match value { - 1 => MXL::XLEN32, - 2 => MXL::XLEN64, - 3 => MXL::XLEN128, - _ => unreachable!(), - } + pub fn mxl(&self) -> XLEN { + let value = (self.bits() >> (usize::BITS - 2)) as u8; + XLEN::from(value) } - /// Returns true when the atomic extension is implemented. + /// Returns true when a given extension is implemented. + /// + /// # Example + /// + /// ``` no_run + /// let misa = unsafe { riscv::register::misa::read() }; + /// 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; diff --git a/src/register/mstatus.rs b/src/register/mstatus.rs index 066d1f11..4f225ac2 100644 --- a/src/register/mstatus.rs +++ b/src/register/mstatus.rs @@ -1,11 +1,6 @@ //! mstatus register -// FIXME: in 1.12 spec there will be `SBE` and `MBE` bits. -// They allows to execute supervisor in given big endian, -// they would be in a new register `mstatush` in RV32; we should implement `mstatush` -// at that time. -// FIXME: `SXL` and `UXL` bits require a structure interpreting XLEN, -// which would be the best way we implement this using Rust? +pub use super::misa::XLEN; /// mstatus register #[derive(Clone, Copy, Debug)] @@ -53,13 +48,23 @@ pub enum SPP { User = 0, } -impl Mstatus { - /// User Interrupt Enable - #[inline] - pub fn uie(&self) -> bool { - self.bits & (1 << 0) != 0 +/// Non-instruction-fetch memory endianness +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Endianness { + BigEndian = 1, + LittleEndian = 0, +} + +impl From for Endianness { + fn from(value: bool) -> Self { + match value { + true => Self::BigEndian, + false => Self::LittleEndian, + } } +} +impl Mstatus { /// Supervisor Interrupt Enable #[inline] pub fn sie(&self) -> bool { @@ -72,18 +77,18 @@ impl Mstatus { self.bits & (1 << 3) != 0 } - /// User Previous Interrupt Enable - #[inline] - pub fn upie(&self) -> bool { - self.bits & (1 << 4) != 0 - } - /// Supervisor Previous Interrupt Enable #[inline] pub fn spie(&self) -> bool { self.bits & (1 << 5) != 0 } + /// U-mode non-instruction-fetch memory endianness + #[inline] + pub fn ube(&self) -> Endianness { + Endianness::from(self.bits & (1 << 6) != 0) + } + /// Machine Previous Interrupt Enable #[inline] pub fn mpie(&self) -> bool { @@ -196,13 +201,57 @@ impl Mstatus { self.bits & (1 << 22) != 0 } - /* - FIXME: There are MBE and SBE bits in 1.12; once Privileged Specification version 1.12 - is ratified, there should be read functions of these bits as well. - */ + /// 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 + /// 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((self.bits >> 34) as u8 & 0x3), + } + } + + /// 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(self.bits & (1 << 36) != 0), + } + } + + /// 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(self.bits & (1 << 37) != 0), + } + } + + /// 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 @@ -251,6 +300,15 @@ 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) { @@ -277,3 +335,39 @@ pub unsafe fn set_fs(fs: FS) { value |= (fs as usize) << 13; _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), + }, + } +} diff --git a/src/register/mstatush.rs b/src/register/mstatush.rs new file mode 100644 index 00000000..5fbab3b8 --- /dev/null +++ b/src/register/mstatush.rs @@ -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), + } +}