Skip to main content

ab_riscv_interpreter/v/
vector_registers.rs

1//! Vector registers
2
3use crate::{Csrs, CustomErrorPlaceholder};
4use ab_riscv_primitives::prelude::*;
5use core::fmt;
6
7pub(crate) const VLENB_USIZE<const VLEN: Vlen>: usize = VLEN.bytes() as usize;
8
9/// Alignment wrapper for vector registers
10#[derive(Debug, Clone, Copy)]
11// Aligned to 128 bytes, which is u32 * 32 registers, the minimum reasonable value to use in most
12// cases
13#[repr(align(128))]
14pub struct VectorRegisterFile<const VLEN: Vlen>([[u8; VLENB_USIZE::<VLEN>]; 32]);
15
16const impl<const VLEN: Vlen> Default for VectorRegisterFile<VLEN> {
17    #[inline(always)]
18    fn default() -> Self {
19        Self([[0; _]; _])
20    }
21}
22
23impl<const VLEN: Vlen> VectorRegisterFile<VLEN> {
24    /// Get reference to a vector register
25    #[inline(always)]
26    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
27    pub fn get(&self, index: VReg) -> &[u8; VLENB_USIZE::<VLEN>] {
28        // SAFETY: Always in-range
29        unsafe { self.0.get_unchecked(usize::from(index.to_bits())) }
30    }
31
32    /// Get mutable reference to a vector register
33    #[inline(always)]
34    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
35    pub fn get_mut(&mut self, index: VReg) -> &mut [u8; VLENB_USIZE::<VLEN>] {
36        // SAFETY: Always in-range
37        unsafe { self.0.get_unchecked_mut(usize::from(index.to_bits())) }
38    }
39}
40
41/// Base for [`VectorRegisters`].
42///
43/// This is primarily a workaround for type system cycles.
44pub trait VectorRegistersBase {
45    /// Maximum vector element width `ELEN` in bits
46    const ELEN: Elen;
47    /// Vector register width `VLEN` in bits
48    const VLEN: Vlen;
49}
50
51// TODO: Figure out a way to make `VectorRegisters + VectorRegistersExt` trait bounds work without
52//  type system cycles
53/// Vector register state.
54///
55/// This trait contains only methods that implementations genuinely need to provide. Derived
56/// accessors for simpler CSRs are in [`VectorRegistersExt`].
57///
58/// Note that due to Rust type system limitations, you should use [`VectorRegistersExt`] in trait
59/// bounds instead of this trait directly or else the solver will fail.
60///
61/// Methods for `vtype` and `vl` live here (not in the ext trait) because they have non-trivial
62/// update semantics: `vtype` must maintain a cached decoded form and handle the XLEN-dependent vill
63/// bit, and `vl` is read-only via CSR instructions but writable by `vsetvl{i}` and fault-only-first
64/// loads.
65///
66/// `ELEN` is the maximum element width in bits.
67pub trait VectorRegisters<CustomError = CustomErrorPlaceholder>
68where
69    Self: VectorRegistersBase,
70    [(); SUPPORTED_ELEN_VLEN::<{ Self::ELEN }, { Self::VLEN }>]:,
71{
72    /// Read the vector register file
73    fn read_vregs(&self) -> &VectorRegisterFile<{ Self::VLEN }>;
74
75    /// Mutable access to the vector register file
76    fn write_vregs(&mut self) -> &mut VectorRegisterFile<{ Self::VLEN }>;
77
78    /// Check whether vector instructions are currently permitted.
79    ///
80    /// Returns `false` when `mstatus.VS == Off` (or equivalent like `sstatus`/`vstatus`). In
81    /// environments without these status registers, returns `true` always.
82    fn vector_instructions_allowed(&self) -> bool;
83
84    /// Mark the vector state as dirty.
85    ///
86    /// Must set VS to Dirty in `mstatus` (and `sstatus`/`vsstatus` shadows) when those registers
87    /// exist. No-op otherwise.
88    fn mark_vs_dirty(&mut self);
89
90    /// Compute `vl` from `AVL` and `VLMAX` per spec constraints.
91    ///
92    /// The simplest compliant implementation (which is used by default) is `min(AVL, VLMAX)`. More
93    /// sophisticated implementations may return values in `[ceil(AVL/2), VLMAX]` for
94    /// `AVL < 2*VLMAX`, but this simple strategy satisfies all three spec requirements.
95    #[inline(always)]
96    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
97    fn compute_vl(&self, avl: Vl, vlmax: Vl) -> Vl {
98        avl.min(vlmax)
99    }
100
101    /// Compute `VLMAX` for a given vtype
102    #[inline(always)]
103    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
104    fn vlmax_for_vtype(&self, vtype: Vtype<{ Self::ELEN }, { Self::VLEN }>) -> Vl {
105        vtype.vlmul().vlmax::<{ Self::VLEN }>(vtype.vsew())
106    }
107}
108
109/// Derived convenience accessors for vector CSRs that are simple read/write fields (vstart, vxrm,
110/// vxsat, vcsr).
111///
112/// Intended for types that implement both [`VectorRegisters`] and [`Csrs`].
113///
114/// NOTE: While the default methods implemented via the [`Csrs`] trait are correct, custom
115/// higher-performance implementations are often possible by overriding them and, for example,
116/// caching various CSRs as separate pre-decoded values rather than going through a generic code
117/// path with XLEN-sized raw CSR values during reads.
118pub trait VectorRegistersExt<Reg, CustomError = CustomErrorPlaceholder>
119where
120    Self: Csrs<Reg, CustomError> + VectorRegisters<CustomError>,
121    [(); SUPPORTED_ELEN_VLEN::<{ Self::ELEN }, { Self::VLEN }>]:,
122    Reg: Register,
123    CustomError: fmt::Debug,
124{
125    /// Initialize the vector state to the recommended default configuration.
126    ///
127    /// Per spec: `vtype.vill` = 1, remaining `vtype` bits = `0`, `vl` = 0.
128    /// `vstart`, `vxrm`, `vxsat` may have arbitrary values at reset but are zeroed here for
129    /// deterministic behavior.
130    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
131    fn initialize_vector_state(&mut self) {
132        self.set_vtype(None);
133        self.set_vl(Vl::ZERO);
134        self.set_vstart(Vstart::ZERO);
135        self.set_vxrm(Vxrm::default());
136        self.set_vxsat(false);
137    }
138
139    /// Get current `vstart`
140    #[inline(always)]
141    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
142    fn vstart(&self) -> Vstart {
143        let raw = self
144            .read_csr(VectorCsr::Vstart.to_csr_index())
145            .unwrap_or_default()
146            .as_u64();
147        Vstart::from(raw as u16)
148    }
149
150    /// Set `vstart`.
151    ///
152    /// The default implementation ignores writes to uninitialized CSR in release mode and panics in
153    /// debug.
154    #[inline(always)]
155    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
156    fn set_vstart(&mut self, vstart: Vstart) {
157        let result = self.write_csr(
158            VectorCsr::Vstart.to_csr_index(),
159            Reg::Type::from(u16::from(vstart)),
160        );
161        debug_assert!(
162            result.is_ok(),
163            "Implementation must initialize `vstart` CSR"
164        );
165    }
166
167    /// Reset `vstart` to zero.
168    ///
169    /// Per spec, all vector instructions reset `vstart` to zero at the end of execution.
170    #[inline(always)]
171    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
172    fn reset_vstart(&mut self) {
173        self.set_vstart(Vstart::ZERO);
174    }
175
176    /// Get `vxsat` (single bit)
177    #[inline(always)]
178    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
179    fn vxsat(&self) -> bool {
180        let raw = self
181            .read_csr(VectorCsr::Vxsat.to_csr_index())
182            .unwrap_or_default()
183            .as_u64();
184        (raw & 1) == 1
185    }
186
187    /// Set `vxsat`.
188    ///
189    /// The default implementation ignores writes to uninitialized CSR in release mode and panics in
190    /// debug.
191    #[inline(always)]
192    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
193    fn set_vxsat(&mut self, vxsat: bool) {
194        let masked = Reg::Type::from(u8::from(vxsat));
195        let result = self.write_csr(VectorCsr::Vxsat.to_csr_index(), masked);
196        debug_assert!(result.is_ok(), "Implementation must initialize `vxsat` CSR");
197        // Mirror `vxsat` into `vcsr[0]`, preserving `vcsr[2:1]` (`vxrm`)
198        let old_vcsr = self
199            .read_csr(VectorCsr::Vcsr.to_csr_index())
200            .unwrap_or_default();
201        let new_vcsr = (old_vcsr & !Reg::Type::from(1u8)) | masked;
202        let result = self.write_csr(VectorCsr::Vcsr.to_csr_index(), new_vcsr);
203        debug_assert!(result.is_ok(), "Implementation must initialize `vcsr` CSR");
204    }
205
206    /// Get `vxrm`
207    #[inline(always)]
208    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
209    fn vxrm(&self) -> Vxrm {
210        let raw = self
211            .read_csr(VectorCsr::Vxrm.to_csr_index())
212            .unwrap_or_default()
213            .as_u64();
214        Vxrm::from_bits(raw as u8)
215    }
216
217    /// Set `vxrm`.
218    ///
219    /// The default implementation ignores writes to uninitialized CSR in release mode and panics in
220    /// debug.
221    #[inline(always)]
222    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
223    fn set_vxrm(&mut self, vxrm: Vxrm) {
224        let masked = Reg::Type::from(vxrm.to_bits());
225        let result = self.write_csr(VectorCsr::Vxrm.to_csr_index(), masked);
226        debug_assert!(result.is_ok(), "Implementation must initialize `vxrm` CSR");
227        // Mirror `vxrm` into `vcsr[2:1]`, preserving `vcsr[0]` (`vxsat`)
228        let old_vcsr = self
229            .read_csr(VectorCsr::Vcsr.to_csr_index())
230            .unwrap_or_default();
231        let new_vcsr = (old_vcsr & !Reg::Type::from(0b110u8)) | (masked << 1u8);
232        let result = self.write_csr(VectorCsr::Vcsr.to_csr_index(), new_vcsr);
233        debug_assert!(result.is_ok(), "Implementation must initialize `vcsr` CSR");
234    }
235
236    /// Get the current vl
237    #[inline(always)]
238    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
239    fn vl(&self) -> Vl {
240        let vl = self
241            .read_csr(VectorCsr::Vl.to_csr_index())
242            .unwrap_or_default()
243            .as_u64() as u32;
244        // Should always be `Some()`, but can't be guaranteed here
245        Vl::new(vl).unwrap_or_default()
246    }
247
248    /// Set vl.
249    ///
250    /// The implementation must update both its internal decoded cache and the raw CSR value (for
251    /// reads via Zicsr, writes via Zicsr are not allowed).
252    ///
253    /// The default implementation ignores writes to uninitialized CSR in release mode and panics in
254    /// debug.
255    #[inline(always)]
256    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
257    fn set_vl(&mut self, vl: Vl) {
258        let result = self.write_csr(VectorCsr::Vl.to_csr_index(), Reg::Type::from(u32::from(vl)));
259        debug_assert!(result.is_ok(), "Implementation must initialize `vl` CSR");
260    }
261
262    /// Get the current decoded vtype
263    #[inline(always)]
264    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
265    fn vtype(&self) -> Option<Vtype<{ Self::ELEN }, { Self::VLEN }>> {
266        self.read_csr(VectorCsr::Vtype.to_csr_index())
267            .ok()
268            .and_then(Vtype::from_raw::<Reg>)
269    }
270
271    /// Set the vtype register from a decoded `Vtype`.
272    ///
273    /// The implementation must update both its internal decoded cache and the raw CSR value (for
274    /// reads via Zicsr, writes via Zicsr are not allowed).
275    ///
276    /// The default implementation ignores writes to uninitialized CSR in release mode and panics in
277    /// debug.
278    #[inline(always)]
279    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
280    fn set_vtype(&mut self, vtype: Option<Vtype<{ Self::ELEN }, { Self::VLEN }>>) {
281        let vtype_raw = if let Some(vt) = vtype {
282            vt.to_raw::<Reg>()
283        } else {
284            Vtype::<{ Self::ELEN }, { Self::VLEN }>::illegal_raw::<Reg>()
285        };
286
287        let result = self.write_csr(VectorCsr::Vtype.to_csr_index(), vtype_raw);
288        debug_assert!(result.is_ok(), "Implementation must initialize `vtype` CSR");
289    }
290}