Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv: define stvec CSR with macro helpers #267

Merged
merged 2 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Use CSR helper macros to define `scounteren` field types
- Use CSR helper macros to define `sip` register
- Use CSR helper macros to define `sstatus` field types
- Use CSR helper macros to define `stvec` field types

## [v0.12.1] - 2024-10-20

Expand Down
100 changes: 77 additions & 23 deletions riscv/src/register/stvec.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,97 @@
//! stvec register

pub use crate::register::mtvec::TrapMode;
use crate::result::{Error, Result};

/// stvec register
#[derive(Clone, Copy, Debug)]
pub struct Stvec {
bits: usize,
const TRAP_MASK: usize = 0b11;

read_write_csr! {
/// stvec register
Stvec: 0x105,
mask: usize::MAX,
}

read_write_csr_field! {
Stvec,
/// Returns the trap-vector mode
trap_mode,
TrapMode: [0:1],
}

impl Stvec {
/// Returns the contents of the register as raw bits
/// Returns the trap-vector base-address
#[inline]
pub fn bits(&self) -> usize {
self.bits
pub const fn address(&self) -> usize {
self.bits & !TRAP_MASK
}

/// Returns the trap-vector base-address
/// Sets the trap-vector base-address.
///
/// # Note
///
/// Panics if the address is not aligned to 4-bytes.
#[inline]
pub fn address(&self) -> usize {
self.bits - (self.bits & 0b11)
pub fn set_address(&mut self, address: usize) {
self.try_set_address(address).unwrap();
}

/// Returns the trap-vector mode
/// Attempts to set the trap-vector base-address.
///
/// # Note
///
/// Returns an error if the address is not aligned to 4-bytes.
#[inline]
pub fn trap_mode(&self) -> Option<TrapMode> {
let mode = self.bits & 0b11;
match mode {
0 => Some(TrapMode::Direct),
1 => Some(TrapMode::Vectored),
_ => None,
pub fn try_set_address(&mut self, address: usize) -> Result<()> {
// check for four-byte alignment
if (address & TRAP_MASK) != 0 {
Err(Error::InvalidFieldVariant {
field: "stvec::address",
value: address,
})
} else {
self.bits = address | (self.bits & TRAP_MASK);
Ok(())
}
}
}

read_csr_as!(Stvec, 0x105);
write_csr!(0x105);
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_stvec() {
let mut stvec = Stvec::from_bits(0);

[TrapMode::Direct, TrapMode::Vectored]
.into_iter()
.for_each(|trap_mode| {
test_csr_field!(stvec, trap_mode: trap_mode);
});

/// Writes the CSR
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
_write(addr + mode as usize);
(1..=usize::BITS)
.map(|r| (((1u128 << r) - 1) as usize) & !TRAP_MASK)
.for_each(|address| {
stvec.set_address(address);
assert_eq!(stvec.address(), address);

assert_eq!(stvec.try_set_address(address), Ok(()));
assert_eq!(stvec.address(), address);
});

(1..=usize::BITS)
.filter_map(|r| match ((1u128 << r) - 1) as usize {
addr if (addr & TRAP_MASK) != 0 => Some(addr),
_ => None,
})
.for_each(|address| {
assert_eq!(
stvec.try_set_address(address),
Err(Error::InvalidFieldVariant {
field: "stvec::address",
value: address,
})
);
});
}
}