Skip to content

Commit

Permalink
Merge pull request #142 from rust-embedded/add-mstatush
Browse files Browse the repository at this point in the history
Rework on misa and mstatus
  • Loading branch information
almindor authored Sep 20, 2023
2 parents aa56cb4 + 93c1911 commit ee80fd4
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 41 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ 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
- Add `TryFrom` implementation for `mcause::{Interrupt, Exception}` and `scause::{Interrupt, Exception}`

### 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`
Expand All @@ -25,7 +30,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

Expand Down
2 changes: 2 additions & 0 deletions src/register/mod.rs → src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//! - mcycleh
//! - minstreth
//! - mhpmcounter<3-31>h
//! - mstatush
#[macro_use]
mod macros;
Expand Down Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions src/register/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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])*
Expand Down
43 changes: 26 additions & 17 deletions src/register/misa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,48 @@ 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]
pub fn bits(&self) -> usize {
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;
Expand Down
140 changes: 117 additions & 23 deletions src/register/mstatus.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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<bool> 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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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),
},
}
}
Loading

0 comments on commit ee80fd4

Please sign in to comment.