Skip to main content

ab_riscv_primitives/instructions/
v.rs

1//! V extension
2
3pub mod zvexx;
4
5use crate::registers::general_purpose::{RegType, Register};
6use core::fmt;
7
8/// `mstatus.VS` / `sstatus.VS` / `vsstatus.VS` field encoding.
9///
10/// Context status for the vector extension, analogous to `mstatus.FS`.
11/// Located at bits `[10:9]` in the respective status registers.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(u8)]
14pub enum VsStatus {
15    /// Vector unit is off; any vector instruction or CSR access raises illegal instruction
16    Off = 0,
17    /// Vector state is known to be in its initial state
18    Initial = 1,
19    /// Vector state is potentially modified but matches the last saved state
20    Clean = 2,
21    /// Vector state has been modified since the last save
22    Dirty = 3,
23}
24
25impl VsStatus {
26    /// Decode from a 2-bit field value
27    #[inline(always)]
28    pub const fn from_bits(bits: u8) -> Self {
29        match bits & 0b11 {
30            0 => Self::Off,
31            1 => Self::Initial,
32            2 => Self::Clean,
33            _ => Self::Dirty,
34        }
35    }
36
37    /// Encode to a 2-bit field value
38    #[inline(always)]
39    pub const fn to_bits(self) -> u8 {
40        self as u8
41    }
42}
43
44/// Vector length multiplier (LMUL) setting
45///
46/// Encoded in `vtype[2:0]` as a signed 3-bit value.
47/// `LMUL = 2^vlmul` where `vlmul` is sign-extended. Positive values give integer multipliers,
48/// negative values give fractional.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[repr(u8)]
51pub enum Vlmul {
52    /// LMUL = 1 (`vlmul` encoding 0b000)
53    M1 = 0b000,
54    /// LMUL = 2 (`vlmul` encoding 0b001)
55    M2 = 0b001,
56    /// LMUL = 4 (`vlmul` encoding 0b010)
57    M4 = 0b010,
58    /// LMUL = 8 (`vlmul` encoding 0b011)
59    M8 = 0b011,
60    /// LMUL = 1/8 (`vlmul` encoding 0b101)
61    Mf8 = 0b101,
62    /// LMUL = 1/4 (`vlmul` encoding 0b110)
63    Mf4 = 0b110,
64    /// LMUL = 1/2 (`vlmul` encoding 0b111)
65    Mf2 = 0b111,
66}
67
68impl Vlmul {
69    /// Decode from the 3-bit `vlmul` field. Returns `None` for reserved encoding 0b100.
70    #[inline(always)]
71    pub const fn from_bits(bits: u8) -> Option<Self> {
72        match bits & 0b111 {
73            0b000 => Some(Self::M1),
74            0b001 => Some(Self::M2),
75            0b010 => Some(Self::M4),
76            0b011 => Some(Self::M8),
77            0b101 => Some(Self::Mf8),
78            0b110 => Some(Self::Mf4),
79            0b111 => Some(Self::Mf2),
80            _ => None,
81        }
82    }
83
84    /// Encode to the 3-bit `vlmul` field
85    #[inline(always)]
86    pub const fn to_bits(self) -> u8 {
87        self as u8
88    }
89
90    /// Compute `VLMAX = LMUL * VLEN / SEW`.
91    ///
92    /// For fractional LMUL, this is `VLEN / (SEW * denominator)`.
93    /// Returns 0 when the result would be less than 1 (insufficient bits).
94    #[inline(always)]
95    pub const fn vlmax(self, vlen_bits: u32, sew_bits: u32) -> u32 {
96        match self {
97            Self::M1 => vlen_bits / sew_bits,
98            Self::M2 => (vlen_bits * 2) / sew_bits,
99            Self::M4 => (vlen_bits * 4) / sew_bits,
100            Self::M8 => (vlen_bits * 8) / sew_bits,
101            Self::Mf2 => vlen_bits / (sew_bits * 2),
102            Self::Mf4 => vlen_bits / (sew_bits * 4),
103            Self::Mf8 => vlen_bits / (sew_bits * 8),
104        }
105    }
106
107    /// Number of vector registers occupied by one register group at this `LMUL`.
108    ///
109    /// Fractional `LMUL` values (`Mf2`, `Mf4`, `Mf8`) each occupy exactly `1` register.
110    /// Integer `LMUL` values occupy `1`, `2`, `4, or `8` registers respectively.
111    #[inline(always)]
112    pub const fn register_count(self) -> u8 {
113        match self {
114            Self::Mf8 | Self::Mf4 | Self::Mf2 | Self::M1 => 1,
115            Self::M2 => 2,
116            Self::M4 => 4,
117            Self::M8 => 8,
118        }
119    }
120
121    /// LMUL as a `(numerator, denominator)` fraction where `LMUL = num / den`.
122    ///
123    /// Both values are powers of two with exactly one equal to `1`. Useful for computing
124    /// `EMUL = (EEW / SEW) * LMUL` without floating-point arithmetic.
125    #[inline(always)]
126    pub const fn as_fraction(self) -> (u8, u8) {
127        match self {
128            Self::Mf8 => (1, 8),
129            Self::Mf4 => (1, 4),
130            Self::Mf2 => (1, 2),
131            Self::M1 => (1, 1),
132            Self::M2 => (2, 1),
133            Self::M4 => (4, 1),
134            Self::M8 => (8, 1),
135        }
136    }
137
138    /// Compute `EMUL` for an indexed load: `EMUL = (index_eew / sew) * LMUL`.
139    ///
140    /// Returns the register count for the index register group, or `None` when `EMUL` falls
141    /// outside the legal range `[1/8, 8]`.
142    #[inline(always)]
143    pub const fn index_register_count(self, index_eew: Eew, sew: Vsew) -> Option<u8> {
144        let (lmul_num, lmul_den) = self.as_fraction();
145        let num = u16::from(index_eew.bits_width()) * u16::from(lmul_num);
146        let den = u16::from(sew.bits_width()) * u16::from(lmul_den);
147        // Both are products of powers of two; GCD equals the smaller value.
148        let g = if num < den { num } else { den };
149        let (n, d) = (num / g, den / g);
150        // Legal EMUL fractions: 1/8, 1/4, 1/2, 1, 2, 4, 8
151        let legal = matches!(
152            (n, d),
153            (1, 8) | (1, 4) | (1, 2) | (1, 1) | (2, 1) | (4, 1) | (8, 1)
154        );
155        if !legal {
156            return None;
157        }
158        // Register count is max(1, n/d) = n when d==1, else 1
159        Some(if d > 1 { 1 } else { n as u8 })
160    }
161
162    /// Compute EMUL for a data operand of a memory instruction with a given effective element
163    /// width: `EMUL = (eew / sew) * LMUL`.
164    ///
165    /// Mathematically identical to [`Self::index_register_count`], but exposed under a distinct
166    /// name for call sites where the EEW describes the *data* being loaded or stored rather than an
167    /// index. Keeping the two entry points separate avoids accidental semantic drift if
168    /// one of them is later specialised.
169    ///
170    /// Returns `None` when the resulting EMUL falls outside the legal range `[1/8, 8]`.
171    #[inline(always)]
172    pub const fn data_register_count(self, eew: Eew, sew: Vsew) -> Option<u8> {
173        self.index_register_count(eew, sew)
174    }
175}
176
177impl fmt::Display for Vlmul {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        match self {
180            Self::M1 => write!(f, "m1"),
181            Self::M2 => write!(f, "m2"),
182            Self::M4 => write!(f, "m4"),
183            Self::M8 => write!(f, "m8"),
184            Self::Mf8 => write!(f, "mf8"),
185            Self::Mf4 => write!(f, "mf4"),
186            Self::Mf2 => write!(f, "mf2"),
187        }
188    }
189}
190
191/// Factor by which Vsew width is divided
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193#[repr(u8)]
194pub enum VsewFactor {
195    /// Divide width by 2
196    F2 = 2,
197    /// Divide width by 4
198    F4 = 4,
199    /// Divide width by 8
200    F8 = 8,
201}
202
203impl VsewFactor {
204    /// Divide Vsew width by a given factor
205    pub const fn factor(self) -> u8 {
206        self as u8
207    }
208}
209
210/// Selected element width (SEW).
211///
212/// Encoded in `vtype[5:3]` as `vsew`. `SEW = 8 * 2^vsew`.
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214#[repr(u8)]
215pub enum Vsew {
216    /// SEW = 8 bits (vsew = 0b000)
217    E8 = 8,
218    /// SEW = 16 bits (vsew = 0b001)
219    E16 = 16,
220    /// SEW = 32 bits (vsew = 0b010)
221    E32 = 32,
222    /// SEW = 64 bits (vsew = 0b011)
223    E64 = 64,
224}
225
226impl Vsew {
227    /// Decode from the 3-bit vsew field. Returns `None` for reserved encodings.
228    #[inline(always)]
229    pub const fn from_bits(bits: u8) -> Option<Self> {
230        match bits & 0b111 {
231            0b000 => Some(Self::E8),
232            0b001 => Some(Self::E16),
233            0b010 => Some(Self::E32),
234            0b011 => Some(Self::E64),
235            _ => None,
236        }
237    }
238
239    /// Encode to the 3-bit vsew field
240    #[inline(always)]
241    pub const fn to_bits(self) -> u8 {
242        match self {
243            Vsew::E8 => 0b000,
244            Vsew::E16 => 0b001,
245            Vsew::E32 => 0b010,
246            Vsew::E64 => 0b011,
247        }
248    }
249
250    /// Get the double element width, if available
251    pub const fn double_width(self) -> Option<Self> {
252        match self {
253            Self::E8 => Some(Self::E16),
254            Self::E16 => Some(Self::E32),
255            Self::E32 => Some(Self::E64),
256            Self::E64 => None,
257        }
258    }
259
260    /// Divide Vsew width by a given factor
261    pub const fn divide_by_factor(self, factor: VsewFactor) -> Option<Self> {
262        match self.bits_width().div_exact(factor.factor())? {
263            8 => Some(Self::E8),
264            16 => Some(Self::E16),
265            32 => Some(Self::E32),
266            _ => None,
267        }
268    }
269
270    /// Element width in bits
271    #[inline(always)]
272    pub const fn bits_width(self) -> u8 {
273        match self {
274            Self::E8 => 8,
275            Self::E16 => 16,
276            Self::E32 => 32,
277            Self::E64 => 64,
278        }
279    }
280
281    /// Element width in bytes
282    #[inline(always)]
283    pub const fn bytes_width(self) -> u8 {
284        match self {
285            Self::E8 => 1,
286            Self::E16 => 2,
287            Self::E32 => 4,
288            Self::E64 => 8,
289        }
290    }
291
292    /// Convert to the corresponding `Eew` variant.
293    ///
294    /// Every valid `Vsew` value has a directly corresponding `Eew` value because both
295    /// enumerate the same set of widths (8/16/32/64 bits). The conversion is always
296    /// successful.
297    #[inline(always)]
298    pub const fn as_eew(self) -> Eew {
299        match self {
300            Self::E8 => Eew::E8,
301            Self::E16 => Eew::E16,
302            Self::E32 => Eew::E32,
303            Self::E64 => Eew::E64,
304        }
305    }
306}
307
308impl fmt::Display for Vsew {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        match self {
311            Self::E8 => write!(f, "e8"),
312            Self::E16 => write!(f, "e16"),
313            Self::E32 => write!(f, "e32"),
314            Self::E64 => write!(f, "e64"),
315        }
316    }
317}
318
319/// Effective element width for vector memory operations
320#[derive(Debug, Clone, Copy, PartialEq, Eq)]
321#[repr(u8)]
322pub enum Eew {
323    /// 8-bit elements
324    E8 = 1,
325    /// 16-bit elements
326    E16 = 2,
327    /// 32-bit elements
328    E32 = 4,
329    /// 64-bit elements
330    E64 = 8,
331}
332
333impl Eew {
334    /// Max element width in bytes
335    pub const MAX_BYTES: u8 = Self::E64.bytes_width();
336
337    /// Decode the width field into an element width
338    #[inline(always)]
339    pub const fn from_width(width: u8) -> Option<Self> {
340        match width {
341            0b000 => Some(Self::E8),
342            0b101 => Some(Self::E16),
343            0b110 => Some(Self::E32),
344            0b111 => Some(Self::E64),
345            _ => None,
346        }
347    }
348
349    /// Encode to the 3-bit Eew field
350    #[inline(always)]
351    pub const fn to_bits(self) -> u8 {
352        match self {
353            Eew::E8 => 0b000,
354            Eew::E16 => 0b101,
355            Eew::E32 => 0b110,
356            Eew::E64 => 0b111,
357        }
358    }
359
360    /// Element width in bits
361    #[inline(always)]
362    pub const fn bits_width(self) -> u8 {
363        match self {
364            Self::E8 => 8,
365            Self::E16 => 16,
366            Self::E32 => 32,
367            Self::E64 => 64,
368        }
369    }
370
371    /// Element width in bytes.
372    ///
373    /// Guaranteed to be `<= Self::MAX_BYTES`.
374    #[inline(always)]
375    pub const fn bytes_width(self) -> u8 {
376        match self {
377            Self::E8 => 1,
378            Self::E16 => 2,
379            Self::E32 => 4,
380            Self::E64 => 8,
381        }
382    }
383}
384
385impl fmt::Display for Eew {
386    #[inline]
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        fmt::Display::fmt(&self.bits_width(), f)
389    }
390}
391
392/// Vector fixed-point rounding mode.
393///
394/// Encoded in the `vxrm` CSR bits `[1:0]` and mirrored in `vcsr[2:1]`.
395#[derive(Debug, Clone, Copy, PartialEq, Eq)]
396#[repr(u8)]
397pub enum Vxrm {
398    /// Round-to-nearest-up (rnu)
399    Rnu = 0b00,
400    /// Round-to-nearest-even (rne)
401    Rne = 0b01,
402    /// Round-down / truncate (rdn)
403    Rdn = 0b10,
404    /// Round-to-odd (rod)
405    Rod = 0b11,
406}
407
408impl Vxrm {
409    /// Decode from a 2-bit field
410    #[inline(always)]
411    pub const fn from_bits(bits: u8) -> Self {
412        match bits & 0b11 {
413            0b00 => Self::Rnu,
414            0b01 => Self::Rne,
415            0b10 => Self::Rdn,
416            _ => Self::Rod,
417        }
418    }
419
420    /// Encode to a 2-bit field
421    #[inline(always)]
422    pub const fn to_bits(self) -> u8 {
423        self as u8
424    }
425}
426
427/// Decoded `vtype` register contents.
428///
429/// The vtype CSR controls the interpretation of the vector register file: element width, register
430/// grouping, and tail/mask agnostic policies.
431///
432/// The raw encoding is XLEN-dependent (vill is at bit XLEN-1), but this decoded form is
433/// XLEN-independent.
434#[derive(Debug, Clone, Copy, PartialEq, Eq)]
435pub struct Vtype<const ELEN: u32, const VLEN: u32> {
436    /// Vector mask agnostic policy (bit `7`)
437    vma: bool,
438    /// Vector tail agnostic policy (bit `6`)
439    vta: bool,
440    /// Selected element width (bits `[5:3]`)
441    vsew: Vsew,
442    /// Vector length multiplier (bits `[2:0]`)
443    vlmul: Vlmul,
444}
445
446impl<const ELEN: u32, const VLEN: u32> Vtype<ELEN, VLEN> {
447    /// Vector mask agnostic policy (bit `7`)
448    pub const fn vma(&self) -> bool {
449        self.vma
450    }
451
452    /// Vector tail agnostic policy (bit `6`)
453    pub const fn vta(&self) -> bool {
454        self.vta
455    }
456
457    /// Selected element width (bits `[5:3]`)
458    pub const fn vsew(&self) -> Vsew {
459        self.vsew
460    }
461
462    /// Vector length multiplier (bits `[2:0]`)
463    pub const fn vlmul(&self) -> Vlmul {
464        self.vlmul
465    }
466
467    /// Decode from raw register value.
468    ///
469    /// The `XLEN` is taken from `Reg::XLEN` and must be 32 for RV32 or 64 for RV64. The `vill` bit
470    /// is placed at bit position `Reg::XLEN - 1`.
471    ///
472    /// All bits in `[Reg::XLEN-1:8]` must be zero; non-zero bits indicate an unrecognized
473    /// encoding and cause `None` to be returned (this includes `vill`).
474    #[inline(always)]
475    pub const fn from_raw<Reg>(raw: Reg::Type) -> Option<Self>
476    where
477        Reg: [const] Register,
478    {
479        let raw = raw.as_u64();
480
481        // All bits in [XLEN-1:8] must be zero
482        if (raw >> 8u8) != 0 {
483            return None;
484        }
485
486        let vlmul_bits = (raw & 0b111) as u8;
487        let vsew_bits = ((raw >> 3u8) & 0b111) as u8;
488        let vta = ((raw >> 6u8) & 1) != 0;
489        let vma = ((raw >> 7u8) & 1) != 0;
490
491        let vlmul = Vlmul::from_bits(vlmul_bits)?;
492        let vsew = Vsew::from_bits(vsew_bits)?;
493
494        let sew = vsew.bits_width();
495        if u32::from(sew) > ELEN {
496            return None;
497        }
498
499        if vlmul.vlmax(VLEN, u32::from(sew)) == 0 {
500            return None;
501        }
502
503        Some(Self {
504            vma,
505            vta,
506            vsew,
507            vlmul,
508        })
509    }
510
511    /// Encode to a raw `vtype` register value of type `Reg::Type`.
512    ///
513    /// The encoded value contains `vlmul`, `vsew`, `vta`, and `vma` in bits `[7:0]` with
514    /// `vill = 0`. To construct a raw value with `vill = 1` (illegal configuration), use
515    /// [`Self::illegal_raw`].
516    #[inline(always)]
517    pub const fn to_raw<Reg>(self) -> Reg::Type
518    where
519        Reg: [const] Register,
520    {
521        let mut raw = 0u8;
522        raw |= self.vlmul.to_bits();
523        raw |= self.vsew.to_bits() << 3u8;
524
525        if self.vta {
526            raw |= 1 << 6u8;
527        }
528
529        if self.vma {
530            raw |= 1 << 7u8;
531        }
532
533        Reg::Type::from(raw)
534    }
535
536    /// Construct a raw value for `vtype` with `vill=1` (illegal configuration).
537    ///
538    /// Per spec: when `vill` is set, the remaining bits are zero and `vl` is also set to zero. Any
539    /// subsequent vector instruction that depends on `vtype` will raise an illegal-instruction
540    /// exception.
541    #[inline(always)]
542    pub const fn illegal_raw<Reg>() -> Reg::Type
543    where
544        Reg: [const] Register,
545    {
546        let vill_bit = Reg::XLEN - 1;
547        Reg::Type::from(1u8) << vill_bit
548    }
549}