diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 094d83fd..bb216b85 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Add `tselect`, `tdata2` and `tdata3` Sdtrig CSRs as raw `read_write_csr_as_usize!` accessors, and `tdata1` and `tcontrol` as typed CSRs with field accessors. - Add `mireg` and `sireg` CSRs - Add `stopei` and `vstopei` CSRs (AIA supervisor/virtual-supervisor top external interrupt), completing the `*topei` set alongside `mtopei`. diff --git a/riscv/src/register.rs b/riscv/src/register.rs index 0f21dbbf..6d185814 100644 --- a/riscv/src/register.rs +++ b/riscv/src/register.rs @@ -140,7 +140,12 @@ pub mod vsiselect; #[cfg(test)] mod tests; -// TODO: Debug/Trace Registers (shared with Debug Mode) +// Debug/Trace Registers +pub mod tcontrol; +pub mod tdata1; +pub mod tdata2; +pub mod tdata3; +pub mod tselect; // TODO: Debug Mode Registers pub mod dcsr; diff --git a/riscv/src/register/tcontrol.rs b/riscv/src/register/tcontrol.rs new file mode 100644 index 00000000..bef149fd --- /dev/null +++ b/riscv/src/register/tcontrol.rs @@ -0,0 +1,37 @@ +//! tcontrol register — Trigger Control (0x7a5) +//! +//! Global control for machine-mode triggers (Sdtrig). + +read_write_csr! { + /// Trigger Control Register + Tcontrol: 0x7a5, + mask: 0x88, +} + +read_write_csr_field! { + Tcontrol, + /// M-mode trigger enable (bit 3). + /// + /// When a trap into M-mode is taken, `mte` is set to 0. `mret` restores it + /// from `mpte`. + mte: 3, +} + +read_write_csr_field! { + Tcontrol, + /// M-mode previous trigger enable (bit 7). + mpte: 7, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tcontrol() { + let mut tcontrol = Tcontrol::from_bits(0); + + test_csr_field!(tcontrol, mte); + test_csr_field!(tcontrol, mpte); + } +} diff --git a/riscv/src/register/tdata1.rs b/riscv/src/register/tdata1.rs new file mode 100644 index 00000000..7583a1c6 --- /dev/null +++ b/riscv/src/register/tdata1.rs @@ -0,0 +1,125 @@ +//! tdata1 register — Trigger Data 1 (0x7a1) +//! +//! First data register of the trigger selected by [`tselect`](crate::register::tselect). +//! Layout of the lower bits depends on the trigger type. + +read_write_csr! { + /// Trigger Data 1 Register + Tdata1: 0x7a1, + mask: usize::MAX, +} + +csr_field_enum! { + /// Trigger type encoded in `tdata1.type` (Sdtrig). + TriggerType { + default: None, + /// No trigger at this `tselect`. + None = 0, + /// Legacy SiFive address match trigger. + Legacy = 1, + /// Address/data match trigger (`mcontrol`). + Mcontrol = 2, + /// Instruction count trigger (`icount`). + Icount = 3, + /// Interrupt trigger (`itrigger`). + Itrigger = 4, + /// Exception trigger (`etrigger`). + Etrigger = 5, + /// Address/data match trigger (`mcontrol6`). + Mcontrol6 = 6, + /// Trigger source external to the trigger module (`tmexttrigger`). + Tmexttrigger = 7, + /// Custom trigger type 12. + Custom12 = 12, + /// Custom trigger type 13. + Custom13 = 13, + /// Custom trigger type 14. + Custom14 = 14, + /// Trigger disabled. + Disabled = 15, + } +} + +#[cfg(target_arch = "riscv32")] +read_write_csr_field! { + Tdata1, + /// Trigger type (spec field `type`, bits XLEN-1:XLEN-4). + trigger_type, + TriggerType: [28:31], +} + +#[cfg(not(target_arch = "riscv32"))] +read_write_csr_field! { + Tdata1, + /// Trigger type (spec field `type`, bits XLEN-1:XLEN-4). + trigger_type, + TriggerType: [60:63], +} + +#[cfg(target_arch = "riscv32")] +read_write_csr_field! { + Tdata1, + /// Debug-mode-only write enable (bit XLEN-5). + dmode: 27, +} + +#[cfg(not(target_arch = "riscv32"))] +read_write_csr_field! { + Tdata1, + /// Debug-mode-only write enable (bit XLEN-5). + dmode: 59, +} + +#[cfg(target_arch = "riscv32")] +read_write_csr_field! { + Tdata1, + /// Trigger-specific data (bits XLEN-6:0). + data: [0:26], +} + +#[cfg(not(target_arch = "riscv32"))] +read_write_csr_field! { + Tdata1, + /// Trigger-specific data (bits XLEN-6:0). + data: [0:58], +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::result::Error; + + #[test] + fn test_tdata1() { + let mut tdata1 = Tdata1::from_bits(0); + + test_csr_field!(tdata1, dmode); + test_csr_field!( + tdata1, + data: [Tdata1::DATA_SHIFT, Tdata1::DATA_SHIFT + Tdata1::DATA_WIDTH - 1], + 0 + ); + + [ + TriggerType::None, + TriggerType::Legacy, + TriggerType::Mcontrol, + TriggerType::Icount, + TriggerType::Itrigger, + TriggerType::Etrigger, + TriggerType::Mcontrol6, + TriggerType::Tmexttrigger, + TriggerType::Custom12, + TriggerType::Custom13, + TriggerType::Custom14, + TriggerType::Disabled, + ] + .into_iter() + .for_each(|variant| { + test_csr_field!(tdata1, trigger_type: variant); + }); + + tdata1 = Tdata1::from_bits(8 << Tdata1::TRIGGER_TYPE_SHIFT); + assert_eq!(tdata1.try_trigger_type(), Err(Error::InvalidVariant(8))); + } +} diff --git a/riscv/src/register/tdata2.rs b/riscv/src/register/tdata2.rs new file mode 100644 index 00000000..c9515e50 --- /dev/null +++ b/riscv/src/register/tdata2.rs @@ -0,0 +1,21 @@ +//! tdata2 register — Trigger Data 2 (0x7a2) +//! +//! Second data register of the trigger selected by [`tselect`](crate::register::tselect). For address/data +//! match triggers this typically holds the compare value. + +read_write_csr_as_usize!(0x7a2); + +#[cfg(test)] +mod tests { + use super::*; + use crate::result::Error; + + #[test] + fn test_tdata2_read_write() { + for i in 0..usize::BITS { + let val = 1usize << i; + assert_eq!(unsafe { try_write(val) }, Err(Error::Unimplemented)); + assert_eq!(try_read(), Err(Error::Unimplemented)); + } + } +} diff --git a/riscv/src/register/tdata3.rs b/riscv/src/register/tdata3.rs new file mode 100644 index 00000000..f1a20b49 --- /dev/null +++ b/riscv/src/register/tdata3.rs @@ -0,0 +1,22 @@ +//! tdata3 register — Trigger Data 3 (0x7a3) +//! +//! Third data register of the trigger selected by [`tselect`](crate::register::tselect). +//! Holds trigger-specific data. When [`tdata1`](crate::register::tdata1) `type` is 2, 3, 4, 5, or 6, +//! this register is interpreted as `textra32` (RV32) or `textra64` (RV64). + +read_write_csr_as_usize!(0x7a3); + +#[cfg(test)] +mod tests { + use super::*; + use crate::result::Error; + + #[test] + fn test_tdata3_read_write() { + for i in 0..usize::BITS { + let val = 1usize << i; + assert_eq!(unsafe { try_write(val) }, Err(Error::Unimplemented)); + assert_eq!(try_read(), Err(Error::Unimplemented)); + } + } +} diff --git a/riscv/src/register/tselect.rs b/riscv/src/register/tselect.rs new file mode 100644 index 00000000..6c303722 --- /dev/null +++ b/riscv/src/register/tselect.rs @@ -0,0 +1,22 @@ +//! tselect register — Trigger Select (0x7a0) +//! +//! Selects which trigger the `tdata1`, `tdata2` and `tdata3` registers access. +//! The register is WARL, so writing an unsupported index and reading the value +//! back is the way to discover how many triggers the hart implements. + +read_write_csr_as_usize!(0x7a0); + +#[cfg(test)] +mod tests { + use super::*; + use crate::result::Error; + + #[test] + fn test_tselect_read_write() { + for i in 0..usize::BITS { + let val = 1usize << i; + assert_eq!(unsafe { try_write(val) }, Err(Error::Unimplemented)); + assert_eq!(try_read(), Err(Error::Unimplemented)); + } + } +}