From fd163b109404372983ef0fa82fec518d9ccac2d4 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 30 May 2024 03:46:00 +0000 Subject: [PATCH] riscv: add CSR-defining macros Adds helper macros for defining CSR types. --- riscv/CHANGELOG.md | 1 + riscv/src/lib.rs | 2 +- riscv/src/register.rs | 3 + riscv/src/register/macros.rs | 443 ++++++++++++++++++++++++++++ riscv/src/register/mcountinhibit.rs | 75 ++--- riscv/src/register/tests.rs | 25 ++ 6 files changed, 493 insertions(+), 56 deletions(-) create mode 100644 riscv/src/register/tests.rs diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 2a56518a..043b7742 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Export `riscv::register::macros` module macros for external use - Add `riscv::register::mcountinhibit` module for `mcountinhibit` CSR - Add `Mcounteren` in-memory update functions +- Add CSR-defining macros to create in-memory types ### Fixed diff --git a/riscv/src/lib.rs b/riscv/src/lib.rs index cb6f6ed8..6cb83259 100644 --- a/riscv/src/lib.rs +++ b/riscv/src/lib.rs @@ -36,7 +36,7 @@ #![allow(clippy::missing_safety_doc)] pub mod asm; -pub(crate) mod bits; +pub mod bits; pub mod delay; pub mod interrupt; pub mod register; diff --git a/riscv/src/register.rs b/riscv/src/register.rs index e99264b5..be788310 100644 --- a/riscv/src/register.rs +++ b/riscv/src/register.rs @@ -107,6 +107,9 @@ pub mod minstreth; mod mhpmeventx; pub use self::mhpmeventx::*; +#[cfg(test)] +mod tests; + // TODO: Debug/Trace Registers (shared with Debug Mode) // TODO: Debug Mode Registers diff --git a/riscv/src/register/macros.rs b/riscv/src/register/macros.rs index d2dfcb49..fb89fe13 100644 --- a/riscv/src/register/macros.rs +++ b/riscv/src/register/macros.rs @@ -397,3 +397,446 @@ macro_rules! clear_pmp { } }; } + +/// Helper macro to create a read-write CSR type. +/// +/// The type allows to read the CSR value into memory, and update the field values in-memory. +/// +/// The user can then write the entire bitfield value back into the CSR with a single write. +#[macro_export] +macro_rules! read_write_csr { + ($doc:expr, + $ty:ident: $csr:tt, + $mask:tt, + $( + $field_doc:expr, + $field:ident: $get_bit:literal, + $set_field_doc:expr, + $set_field:ident: $set_bit:literal$(,)? + )+) => { + $crate::read_write_csr!($doc, $ty: $csr, $mask); + + $( + $crate::read_write_csr_field!( + $ty, + $field_doc, + $field: $get_bit, + $set_field_doc, + $set_field: $set_bit, + ); + )+ + }; + + ($doc:expr, + $ty:ident: $csr:tt, + $mask:tt, + $( + $field_doc:expr, + $field:ident: $get_bit_start:literal ..= $get_bit_end:literal, + $set_field_doc:expr, + $set_field:ident: $set_bit_start:literal ..= $set_bit_end:literal$(,)? + )+) => { + $crate::read_write_csr!($doc, $ty: $csr, $mask); + + $( + $crate::read_write_csr_field!( + $ty, + $field_doc, + $field: $get_bit_start ..= $get_bit_end, + $set_field_doc, + $set_field: $set_bit_start ..= $set_bit_end, + ); + )+ + }; + + ($doc:expr, + $ty:ident: $csr:tt, + $mask:tt, + $( + $field_doc:expr, + $field:ident: [$get_bit_start:literal : $get_bit_end:literal], + $set_field_doc:expr, + $set_field:ident: [$set_bit_start:literal : $set_bit_end:literal]$(,)? + )+) => { + $crate::read_write_csr!($doc, $ty: $csr, $mask); + + $( + $crate::read_write_csr_field!( + $ty, + $field_doc, + $field: [$get_bit_start : $get_bit_end], + $set_field_doc, + $set_field: [$set_bit_start : $set_bit_end], + ); + )+ + }; + + ($doc:expr, + $ty:ident: $csr:expr, + $mask:tt$(,)?) => { + #[repr(C)] + #[doc = $doc] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct $ty { + bits: usize, + } + + impl $ty { + /// Bitmask for legal bitfields of the CSR type. + pub const BITMASK: usize = $mask; + + /// Creates a new CSR type from raw bits value. + /// + /// Only bits in the [BITMASK](Self::BITMASK) will be set. + pub const fn from_bits(bits: usize) -> Self { + Self { bits: bits & $mask } + } + + /// Gets the raw bits value. + pub const fn bits(&self) -> usize { + self.bits & $mask + } + + /// Gets the bitmask for the CSR type's bitfields. + pub const fn bitmask(&self) -> usize { + Self::BITMASK + } + } + + $crate::read_csr_as!($ty, $csr); + $crate::write_csr_as!($ty, $csr); + $crate::set!($csr); + $crate::clear!($csr); + }; +} + +/// Helper macro to create a read-only CSR type. +/// +/// The type allows to read the CSR value into memory. +#[macro_export] +macro_rules! read_only_csr { + ($doc:expr, + $ty:ident: $csr:expr, + $mask:literal, + $( + $field_doc:expr, + $field:ident: [$bit_start:literal : $bit_end:literal]$(,)? + )+) => { + $crate::read_only_csr! { $doc, $ty: $csr, $mask } + + $( + $crate::read_only_csr_field! { + $ty, + $field_doc, + $field: [$bit_start:$bit_end], + } + )+ + }; + + ($doc:expr, $ty:ident: $csr:tt, $mask:tt, $($field_doc:expr, $field:ident, $bit:tt$(,)?)+) => { + $crate::read_only_csr! { $doc, $ty: $csr, $mask } + + $( + $crate::read_only_csr_field! { + $ty, + $field_doc, + $field: $bit..=$bit_end, + } + )+ + }; + + ($doc:expr, + $ty:ident: $csr:expr, + $mask:literal, + $( + $field_doc:expr, + $field:ident: $bit:tt ..= $bit_end:tt$(,)? + )+) => { + $crate::read_only_csr! { $doc, $ty: $csr, $mask } + + $( + $crate::read_only_csr_field! { + $ty, + $field_doc, + $field: $bit..=$bit_end, + } + )+ + }; + + + ($doc:expr, $ty:ident: $csr:literal, $mask:literal) => { + #[repr(C)] + #[doc = $doc] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct $ty { + bits: usize, + } + + impl $ty { + /// Bitmask for legal bitfields of the CSR type. + pub const BITMASK: usize = $mask; + + /// Creates a new CSR type from raw bits value. + /// + /// Only bits in the [BITMASK](Self::BITMASK) will be set. + pub const fn from_bits(bits: usize) -> Self { + Self { bits: bits & $mask } + } + + /// Gets the raw bits value. + pub const fn bits(&self) -> usize { + self.bits & $mask + } + + /// Gets the bitmask for the CSR type's bitfields. + pub const fn bitmask(&self) -> usize { + Self::BITMASK + } + } + + $crate::read_csr_as!($ty, $csr); + }; +} + +/// Helper macro to create a read-only CSR type. +/// +/// The type allows to read the CSR value into memory. +#[macro_export] +macro_rules! write_only_csr { + ($doc:expr, + $ty:ident: $csr:literal, + $mask:literal, + $($field_doc:expr, $field:ident: $bit:literal$(,)?)+) => { + $crate::read_only_csr! { $doc, $ty: $csr, $mask } + + $( + $crate::write_only_csr_field! { + $ty, + $field_doc, + $field: $bit, + } + )+ + }; + + ($doc:expr, + $ty:ident: $csr:literal, + $mask:literal, + $( + $field_doc:expr, + $field:ident: $bit_start:literal ..= $bit_end:literal$(,)? + )+) => { + $crate::read_only_csr! { $doc, $ty: $csr, $mask } + + $( + $crate::write_only_csr_field! { + $ty, + $field_doc, + $field: $bit_start..=$bit_end, + } + )+ + + }; + + ($doc:expr, + $ty:ident: $csr:literal, + $mask:literal, + $( + $field_doc:expr, + $field:ident: [$bit_start:literal : $bit_end:literal]$(,)? + )+) => { + $crate::read_only_csr! { $doc, $ty: $csr, $mask } + + $( + $crate::write_only_csr_field! { + $ty, + $field_doc, + $field: [$bit_start:$bit_end], + } + )+ + }; + + + ($doc:expr, $ty:ident: $csr:literal, $mask:literal) => { + #[repr(C)] + #[doc = $doc] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct $ty { + bits: usize, + } + + impl $ty { + /// Bitmask for legal bitfields of the CSR type. + pub const BITMASK: usize = $mask; + + /// Creates a new CSR type from raw bits value. + /// + /// Only bits in the [BITMASK](Self::BITMASK) will be set. + pub const fn from_bits(bits: usize) -> Self { + Self { bits: bits & $mask } + } + + /// Gets the raw bits value. + pub const fn bits(&self) -> usize { + self.bits & $mask + } + + /// Gets the bitmask for the CSR type's bitfields. + pub const fn bitmask(&self) -> usize { + Self::BITMASK + } + } + + $crate::write_csr_as!($ty, $csr); + }; +} + +/// Defines field accesor functions for a read-write CSR type. +#[macro_export] +macro_rules! read_write_csr_field { + ($ty:ident, + $field_doc:expr, + $field:ident: $get_bit:literal, + $set_field_doc:expr, + $set_field:ident: $set_bit:literal$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&self) -> bool { + $crate::bits::bf_extract(self.bits, $get_bit, 1) != 0 + } + + #[doc = $set_field_doc] + #[inline] + pub fn $set_field(&mut self, $field: bool) { + self.bits = $crate::bits::bf_insert(self.bits, $set_bit, 1, $field as usize); + } + } + }; + + ($ty:ident, + $field_doc:expr, + $field:ident: $get_bit_start:literal ..= $get_bit_end:literal, + $set_field_doc:expr, + $set_field:ident: $set_bit_start:literal ..= $set_bit_end:literal$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&self, index: usize) -> bool { + assert!(($get_bit_start..=$get_bit_end).contains(&index)); + $crate::bits::bf_extract(self.bits, index, 1) != 0 + } + + #[doc = $set_field_doc] + #[inline] + pub fn $set_field(&mut self, index: usize, $field: bool) { + assert!(($set_bit_start..=$set_bit_end).contains(&index)); + self.bits = $crate::bits::bf_insert(self.bits, index, 1, $field as usize); + } + } + }; + + ($ty:ident, + $field_doc:expr, + $field:ident: [$get_bit_start:literal : $get_bit_end:literal], + $set_field_doc:expr, + $set_field:ident: [$set_bit_start:literal : $set_bit_end:literal]$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&self) -> usize { + $crate::bits::bf_extract( + self.bits, + $get_bit_start, + $get_bit_end - $get_bit_start + 1, + ) + } + + #[doc = $set_field_doc] + #[inline] + pub fn $set_field(&mut self, $field: usize) { + self.bits = $crate::bits::bf_insert( + self.bits, + $set_bit_start, + $set_bit_end - $set_bit_start + 1, + $field, + ); + } + } + }; +} + +/// Defines field accesor functions for a read-only CSR type. +#[macro_export] +macro_rules! read_only_csr_field { + ($ty:ident, $field_doc:expr, $field:ident: $bit:literal$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&self) -> bool { + $crate::bits::bf_extract(self.bits, $bit, 1) != 0 + } + } + }; + + ($ty:ident, $field_doc:expr, $field:ident: $bit_start:literal..=$bit_end:literal$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&self, index: usize) -> bool { + assert!(($bit_start..=$bit_end).contains(&index)); + $crate::bits::bf_extract(self.bits, index, 1) != 0 + } + } + }; + + ($ty:ident, $field_doc:expr, $field:ident: [$bit_start:literal : $bit_end:literal]$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&self) -> bool { + $crate::bits::bf_extract(self.bits, $bit_start, $bit_end - $bit_start + 1) != 0 + } + } + }; +} + +/// Defines field accesor functions for a write-only CSR type. +#[macro_export] +macro_rules! write_only_csr_field { + ($ty:ident, $field_doc:expr, $field:ident: $bit:literal$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&self) -> bool { + $crate::bits::bf_insert(self.bits, $bit, 1) != 0 + } + } + }; + + ($ty:ident, $field_doc:expr, $field:ident: $bit_start:literal..=$bit_end:literal$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&mut self, index: usize, $field: bool) { + assert!(($bit_start..=$bit_end).contains(&index)); + self.bits = $crate::bits::bf_insert(self.bits, index, 1, $field as usize); + } + } + }; + + ($ty:ident, $field_doc:expr, $field:ident: [$bit_start:literal : $bit_end:literal]$(,)?) => { + impl $ty { + #[doc = $field_doc] + #[inline] + pub fn $field(&mut self, index: usize, $field: usize) { + assert!(($bit_start..=$bit_end).contains(&index)); + self.bits = $crate::bits::bf_insert( + self.bits, + $bit_start, + $bit_end - $bit_start + 1, + $field, + ); + } + } + }; +} diff --git a/riscv/src/register/mcountinhibit.rs b/riscv/src/register/mcountinhibit.rs index fca8cce7..efd4b89e 100644 --- a/riscv/src/register/mcountinhibit.rs +++ b/riscv/src/register/mcountinhibit.rs @@ -1,64 +1,29 @@ //! `mcountinhibit` register -use crate::bits::{bf_extract, bf_insert}; - -/// `mcountinhibit` register -#[derive(Clone, Copy, Debug)] -pub struct Mcountinhibit { - bits: usize, +use crate::read_write_csr; + +read_write_csr! { + "`mcountinhibit` register", + Mcountinhibit: 0x320, + 0xffff_fffd, + "Gets the `cycle[h]` inhibit field value.", + cy: 0, + "Sets the `cycle[h]` inhibit field value.\n\n**NOTE**: only updates the in-memory value without touching the CSR.", + set_cy: 0, + "Gets the `instret[h]` inhibit field value.", + ir: 2, + "Sets the `instret[h]` inhibit field value.\n\n**NOTE**: only updates the in-memory value without touching the CSR.", + set_ir: 2, } -impl Mcountinhibit { - /// Machine "cycle\[h\]" Disable - #[inline] - pub fn cy(&self) -> bool { - bf_extract(self.bits, 0, 1) != 0 - } - - /// Sets whether to inhibit the "cycle\[h\]" counter. - /// - /// Only updates the in-memory value, does not modify the `mcountinhibit` register. - #[inline] - pub fn set_cy(&mut self, cy: bool) { - self.bits = bf_insert(self.bits, 0, 1, cy as usize); - } - - /// Machine "instret\[h\]" Disable - #[inline] - pub fn ir(&self) -> bool { - bf_extract(self.bits, 2, 1) != 0 - } - - /// Sets whether to inhibit the "instret\[h\]" counter. - /// - /// Only updates the in-memory value, does not modify the `mcountinhibit` register. - #[inline] - pub fn set_ir(&mut self, ir: bool) { - self.bits = bf_insert(self.bits, 2, 1, ir as usize); - } - - /// Machine "hpm\[x\]" Disable (bits 3-31) - #[inline] - pub fn hpm(&self, index: usize) -> bool { - assert!((3..32).contains(&index)); - bf_extract(self.bits, index, 1) != 0 - } - - /// Sets whether to inhibit the "hpm\[X\]" counter. - /// - /// Only updates the in-memory value, does not modify the `mcountinhibit` register. - #[inline] - pub fn set_hpm(&mut self, index: usize, hpm: bool) { - assert!((3..32).contains(&index)); - self.bits = bf_insert(self.bits, index, 1, hpm as usize); - } +read_write_csr_field! { + Mcountinhibit, + "Gets the `mhpmcounterX[h]` inhibit field value.\n\n**WARN**: `index` must be in the range `[31:3]`.", + hpm: 3..=31, + "Sets the `mhpmcounterX[h]` inhibit field value.\n\n**WARN**: `index` must be in the range `[31:3]`.\n\n**NOTE**: only updates the in-memory value without touching the CSR.", + set_hpm: 3..=31, } -read_csr_as!(Mcountinhibit, 0x320); -write_csr_as!(Mcountinhibit, 0x320); -set!(0x320); -clear!(0x320); - set_clear_csr!( /// Machine cycle Disable , set_cy, clear_cy, 1 << 0); diff --git a/riscv/src/register/tests.rs b/riscv/src/register/tests.rs new file mode 100644 index 00000000..3f87598e --- /dev/null +++ b/riscv/src/register/tests.rs @@ -0,0 +1,25 @@ +read_write_csr! { + "test CSR register type", + Mtest: 0x000, + 0b1111_1111, + "test single-bit field", + single: 0, + "setter test single-bit field", + set_single: 0, +} + +read_write_csr_field! { + Mtest, + "multiple single-bit field range", + multi_range: 1..=3, + "setter multiple single-bit field range", + set_multi_range: 1..=3, +} + +read_write_csr_field!( + Mtest, + "multi-bit field", + multi_field: [4:7], + "setter multi-bit field", + set_multi_field: [4:7], +);