Skip to main content

ab_riscv_interpreter/v/
vector_registers.rs

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