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::hint::cold_path;
7use core::marker::ConstParamTy;
8use core::num::NonZeroU8;
9use core::ops::RangeInclusive;
10use core::{cmp, fmt};
11
12/// Vector start element index
13#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
14pub struct Vstart(u16);
15
16impl fmt::Display for Vstart {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        write!(f, "{}", self.0)
19    }
20}
21
22const impl From<u16> for Vstart {
23    #[inline(always)]
24    fn from(value: u16) -> Self {
25        Self(value)
26    }
27}
28
29const impl From<Vstart> for u16 {
30    #[inline(always)]
31    fn from(value: Vstart) -> Self {
32        value.0
33    }
34}
35
36impl PartialEq<Vl> for Vstart {
37    fn eq(&self, other: &Vl) -> bool {
38        u32::from(self.0) == u32::from(*other)
39    }
40}
41
42impl PartialOrd<Vl> for Vstart {
43    #[inline(always)]
44    fn partial_cmp(&self, other: &Vl) -> Option<cmp::Ordering> {
45        Some(u32::from(self.0).cmp(&u32::from(*other)))
46    }
47}
48
49impl Vstart {
50    /// Zero vector start element index
51    pub const ZERO: Self = Self(0);
52
53    /// Returns a range of vector offsets from `self` to `vl`.
54    ///
55    /// Note: the range might be empty if `vl` is zero.
56    #[inline(always)]
57    pub const fn range_to(self, vl: Vl) -> RangeInclusive<u16> {
58        let vl = u32::from(vl);
59        if vl == 0 {
60            // Empty range
61            #[expect(clippy::reversed_empty_ranges, reason = "Intentional empty range")]
62            {
63                1..=0
64            }
65        } else {
66            self.0..=(vl - 1) as u16
67        }
68    }
69}
70
71/// Vector length
72#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
73pub struct Vl(u32);
74
75impl fmt::Display for Vl {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        write!(f, "{}", self.0)
78    }
79}
80
81const impl From<u8> for Vl {
82    #[inline(always)]
83    fn from(value: u8) -> Self {
84        Self(u32::from(value))
85    }
86}
87
88const impl From<u16> for Vl {
89    #[inline(always)]
90    fn from(value: u16) -> Self {
91        Self(u32::from(value))
92    }
93}
94
95const impl From<Vl> for u32 {
96    #[inline(always)]
97    fn from(value: Vl) -> Self {
98        value.0
99    }
100}
101
102const impl From<Vl> for u64 {
103    #[inline(always)]
104    fn from(value: Vl) -> Self {
105        u64::from(value.0)
106    }
107}
108
109impl PartialEq<Vstart> for Vl {
110    fn eq(&self, other: &Vstart) -> bool {
111        self.0 == u32::from(u16::from(*other))
112    }
113}
114
115impl PartialOrd<Vstart> for Vl {
116    #[inline(always)]
117    fn partial_cmp(&self, other: &Vstart) -> Option<cmp::Ordering> {
118        Some(self.0.cmp(&u32::from(u16::from(*other))))
119    }
120}
121
122impl Vl {
123    /// Zero vector length
124    pub const ZERO: Self = Self(0);
125
126    /// Create a new vector length from a value.
127    ///
128    /// Returns `None` if the value is greater than the maximum vector length.
129    #[inline(always)]
130    pub const fn new(n: u32) -> Option<Self> {
131        if n > u32::from(Vlen::L65_536) {
132            None
133        } else {
134            Some(Self(n))
135        }
136    }
137
138    /// Create a new vector length from a value with saturation
139    #[inline(always)]
140    pub const fn new_saturating(n: u32) -> Self {
141        let n = u32::from(Vlen::L65_536).min(n);
142        Self(n)
143    }
144
145    /// Returns the vector length in bytes
146    #[inline(always)]
147    pub const fn bytes(self) -> u16 {
148        self.0.div_ceil(u8::BITS) as u16
149    }
150}
151
152/// Element length
153#[derive(ConstParamTy, Debug, Clone, Copy, PartialEq, Eq)]
154#[repr(u32)]
155pub enum Elen {
156    /// Element length is 8 bits
157    L8 = 8,
158    /// Element length is 16 bits
159    L16 = 16,
160    /// Element length is 32 bits
161    L32 = 32,
162    /// Element length is 64 bits
163    L64 = 64,
164    /// Element length is 128 bits
165    L128 = 128,
166    /// Element length is 256 bits
167    L256 = 256,
168    /// Element length is 512 bits
169    L512 = 512,
170    /// Element length is 1024 bits
171    L1024 = 1024,
172    /// Element length is 2048 bits
173    L2048 = 2048,
174    /// Element length is 4096 bits
175    L4096 = 4096,
176    /// Element length is 8192 bits
177    L8192 = 8192,
178    /// Element length is 16_384 bits
179    L16_384 = 16_384,
180    /// Element length is 32_768 bits
181    L32_768 = 32_768,
182    /// Element length is 65_536 bits
183    L65_536 = 65_536,
184}
185
186const impl From<Elen> for u32 {
187    #[inline(always)]
188    fn from(value: Elen) -> Self {
189        value as u32
190    }
191}
192
193/// Vector length
194#[derive(ConstParamTy, Debug, Clone, Copy, PartialEq, Eq)]
195#[repr(u32)]
196pub enum Vlen {
197    /// Vector length is 8 bits
198    L8 = 8,
199    /// Vector length is 16 bits
200    L16 = 16,
201    /// Vector length is 32 bits
202    L32 = 32,
203    /// Vector length is 64 bits
204    L64 = 64,
205    /// Vector length is 128 bits
206    L128 = 128,
207    /// Vector length is 256 bits
208    L256 = 256,
209    /// Vector length is 512 bits
210    L512 = 512,
211    /// Vector length is 1024 bits
212    L1024 = 1024,
213    /// Vector length is 2048 bits
214    L2048 = 2048,
215    /// Vector length is 4096 bits
216    L4096 = 4096,
217    /// Vector length is 8192 bits
218    L8192 = 8192,
219    /// Vector length is 16_384 bits
220    L16_384 = 16_384,
221    /// Vector length is 32_768 bits
222    L32_768 = 32_768,
223    /// Vector length is 65_536 bits
224    L65_536 = 65_536,
225}
226
227const impl From<Vlen> for u32 {
228    #[inline(always)]
229    fn from(value: Vlen) -> Self {
230        value as u32
231    }
232}
233
234impl Vlen {
235    /// Vlen in bytes
236    #[inline(always)]
237    pub const fn bytes(self) -> u32 {
238        self as u32 / u8::BITS
239    }
240}
241
242/// Assertion for supported ELEN + VLEN combinations, to be used in `where` bounds (panics on
243/// invalid input)
244pub const SUPPORTED_ELEN_VLEN<const ELEN: Elen, const VLEN: Vlen>: usize = {
245    assert!(
246        u32::from(ELEN) <= u32::from(VLEN),
247        "ELEN must be <= VLEN"
248    );
249    0
250};
251
252/// `mstatus.VS` / `sstatus.VS` / `vsstatus.VS` field encoding.
253///
254/// Context status for the vector extension, analogous to `mstatus.FS`.
255/// Located at bits `[10:9]` in the respective status registers.
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257#[repr(u8)]
258pub enum VsStatus {
259    /// Vector unit is off; any vector instruction or CSR access raises illegal instruction
260    Off = 0,
261    /// Vector state is known to be in its initial state
262    Initial = 1,
263    /// Vector state is potentially modified but matches the last saved state
264    Clean = 2,
265    /// Vector state has been modified since the last save
266    Dirty = 3,
267}
268
269impl VsStatus {
270    /// Decode from a 2-bit field value
271    #[inline(always)]
272    pub const fn from_bits(bits: u8) -> Self {
273        match bits & 0b11 {
274            0 => Self::Off,
275            1 => Self::Initial,
276            2 => Self::Clean,
277            _ => Self::Dirty,
278        }
279    }
280
281    /// Encode to a 2-bit field value
282    #[inline(always)]
283    pub const fn to_bits(self) -> u8 {
284        self as u8
285    }
286}
287
288/// Vector length multiplier (LMUL) setting.
289///
290/// Encoded in `vtype[2:0]` as a signed 3-bit value.
291/// `LMUL = 2^vlmul` where `vlmul` is sign-extended. Positive values give integer multipliers,
292/// negative values give fractional.
293#[derive(Debug, Clone, Copy, PartialEq, Eq)]
294#[repr(u8)]
295pub enum Vlmul {
296    /// LMUL = 1 (`vlmul` encoding 0b000)
297    M1 = 0b000,
298    /// LMUL = 2 (`vlmul` encoding 0b001)
299    M2 = 0b001,
300    /// LMUL = 4 (`vlmul` encoding 0b010)
301    M4 = 0b010,
302    /// LMUL = 8 (`vlmul` encoding 0b011)
303    M8 = 0b011,
304    /// LMUL = 1/8 (`vlmul` encoding 0b101)
305    Mf8 = 0b101,
306    /// LMUL = 1/4 (`vlmul` encoding 0b110)
307    Mf4 = 0b110,
308    /// LMUL = 1/2 (`vlmul` encoding 0b111)
309    Mf2 = 0b111,
310}
311
312impl Vlmul {
313    /// Decode from the 3-bit `vlmul` field. Returns `None` for reserved encoding 0b100.
314    #[inline(always)]
315    pub const fn from_bits(bits: u8) -> Option<Self> {
316        match bits & 0b111 {
317            0b000 => Some(Self::M1),
318            0b001 => Some(Self::M2),
319            0b010 => Some(Self::M4),
320            0b011 => Some(Self::M8),
321            0b101 => Some(Self::Mf8),
322            0b110 => Some(Self::Mf4),
323            0b111 => Some(Self::Mf2),
324            _ => None,
325        }
326    }
327
328    /// Encode to the 3-bit `vlmul` field
329    #[inline(always)]
330    pub const fn to_bits(self) -> u8 {
331        self as u8
332    }
333
334    /// Compute `VLMAX = LMUL * VLEN / SEW`.
335    ///
336    /// For fractional LMUL, this is `VLEN / (SEW * denominator)`.
337    /// Returns `Vl::ZERO` when the result would be less than 1 (insufficient bits).
338    #[inline(always)]
339    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
340    pub const fn vlmax<const VLEN: Vlen>(self, sew: Vsew) -> Vl {
341        let sew_bits = u32::from(sew.bits_width());
342        let vl = match self {
343            Self::M1 => u32::from(VLEN) / sew_bits,
344            Self::M2 => (u32::from(VLEN) * 2) / sew_bits,
345            Self::M4 => (u32::from(VLEN) * 4) / sew_bits,
346            Self::M8 => (u32::from(VLEN) * 8) / sew_bits,
347            Self::Mf2 => u32::from(VLEN) / (sew_bits * 2),
348            Self::Mf4 => u32::from(VLEN) / (sew_bits * 4),
349            Self::Mf8 => u32::from(VLEN) / (sew_bits * 8),
350        };
351        // SAFETY: Can't exceed the maximum vector length for any `sew` value
352        unsafe { Vl::new(vl).unwrap_unchecked() }
353    }
354
355    /// Number of vector registers occupied by one register group at this `LMUL`.
356    ///
357    /// Fractional `LMUL` values (`Mf2`, `Mf4`, `Mf8`) each occupy exactly 1 register.
358    /// Integer `LMUL` values occupy 1, 2, 4, or 8 registers respectively.
359    #[inline(always)]
360    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
361    pub const fn register_count(self) -> NonZeroU8 {
362        let register_count = match self {
363            Self::Mf8 | Self::Mf4 | Self::Mf2 | Self::M1 => 1,
364            Self::M2 => 2,
365            Self::M4 => 4,
366            Self::M8 => 8,
367        };
368        NonZeroU8::new(register_count).expect("Not zero; qed")
369    }
370
371    /// LMUL as a `(numerator, denominator)` fraction where `LMUL = num / den`.
372    ///
373    /// Both values are powers of two with exactly one equal to `1`. Useful for computing
374    /// `EMUL = (EEW / SEW) * LMUL` without floating-point arithmetic.
375    #[inline(always)]
376    pub const fn as_fraction(self) -> (NonZeroU8, NonZeroU8) {
377        let (numerator, denominator) = match self {
378            Self::Mf8 => (1, 8),
379            Self::Mf4 => (1, 4),
380            Self::Mf2 => (1, 2),
381            Self::M1 => (1, 1),
382            Self::M2 => (2, 1),
383            Self::M4 => (4, 1),
384            Self::M8 => (8, 1),
385        };
386
387        (
388            NonZeroU8::new(numerator).expect("Not zero; qed"),
389            NonZeroU8::new(denominator).expect("Not zero; qed"),
390        )
391    }
392
393    /// Compute `EMUL` for an indexed load: `EMUL = (index_eew / sew) * LMUL`.
394    ///
395    /// Returns the register count for the index register group, or `None` when `EMUL` falls
396    /// outside the legal range `[1/8, 8]`.
397    #[inline(always)]
398    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
399    pub const fn index_register_count(self, index_eew: Eew, sew: Vsew) -> Option<NonZeroU8> {
400        let (lmul_num, lmul_den) = self.as_fraction();
401        let num = u16::from(index_eew.bits_width()) * u16::from(lmul_num.get());
402        let den = u16::from(sew.bits_width()) * u16::from(lmul_den.get());
403        // Both are products of powers of two; GCD equals the smaller value.
404        let g = if num < den { num } else { den };
405        let (n, d) = (num / g, den / g);
406        // Legal EMUL fractions: 1/8, 1/4, 1/2, 1, 2, 4, 8
407        let legal = matches!(
408            (n, d),
409            (1, 8) | (1, 4) | (1, 2) | (1, 1) | (2, 1) | (4, 1) | (8, 1)
410        );
411        if !legal {
412            cold_path();
413            return None;
414        }
415        // Register count is max(1, n/d) = n when d==1, else 1
416        Some(NonZeroU8::new(if d > 1 { 1 } else { n as u8 }).expect("Not zero; qed"))
417    }
418
419    /// Compute EMUL for a data operand of a memory instruction with a given effective element
420    /// width: `EMUL = (eew / sew) * LMUL`.
421    ///
422    /// Mathematically identical to [`Self::index_register_count`], but exposed under a distinct
423    /// name for call sites where the EEW describes the *data* being loaded or stored rather than an
424    /// index. Keeping the two entry points separate avoids accidental semantic drift if
425    /// one of them is later specialised.
426    ///
427    /// Returns `None` when the resulting EMUL falls outside the legal range `[1/8, 8]`.
428    #[inline(always)]
429    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
430    pub const fn data_register_count(self, eew: Eew, sew: Vsew) -> Option<NonZeroU8> {
431        self.index_register_count(eew, sew)
432    }
433}
434
435impl fmt::Display for Vlmul {
436    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437        match self {
438            Self::M1 => write!(f, "m1"),
439            Self::M2 => write!(f, "m2"),
440            Self::M4 => write!(f, "m4"),
441            Self::M8 => write!(f, "m8"),
442            Self::Mf8 => write!(f, "mf8"),
443            Self::Mf4 => write!(f, "mf4"),
444            Self::Mf2 => write!(f, "mf2"),
445        }
446    }
447}
448
449/// Factor by which Vsew width is divided
450#[derive(Debug, Clone, Copy, PartialEq, Eq)]
451#[repr(u8)]
452pub enum VsewFactor {
453    /// Divide width by 2
454    F2 = 2,
455    /// Divide width by 4
456    F4 = 4,
457    /// Divide width by 8
458    F8 = 8,
459}
460
461impl VsewFactor {
462    /// Return the numeric divisor used to scale down a [`Vsew`] bit-width
463    #[inline(always)]
464    pub const fn factor(self) -> u8 {
465        self as u8
466    }
467}
468
469/// Selected element width (SEW).
470///
471/// Encoded in `vtype[5:3]` as `vsew`. `SEW = 8 * 2^vsew`.
472#[derive(Debug, Clone, Copy, PartialEq, Eq)]
473#[repr(u8)]
474pub enum Vsew {
475    /// SEW = 8 bits (vsew = 0b000)
476    E8 = 8,
477    /// SEW = 16 bits (vsew = 0b001)
478    E16 = 16,
479    /// SEW = 32 bits (vsew = 0b010)
480    E32 = 32,
481    /// SEW = 64 bits (vsew = 0b011)
482    E64 = 64,
483}
484
485impl Vsew {
486    /// Decode from the 3-bit vsew field. Returns `None` for reserved encodings.
487    #[inline(always)]
488    pub const fn from_bits(bits: u8) -> Option<Self> {
489        match bits & 0b111 {
490            0b000 => Some(Self::E8),
491            0b001 => Some(Self::E16),
492            0b010 => Some(Self::E32),
493            0b011 => Some(Self::E64),
494            _ => {
495                cold_path();
496                None
497            }
498        }
499    }
500
501    /// Encode to the 3-bit vsew field
502    #[inline(always)]
503    pub const fn to_bits(self) -> u8 {
504        match self {
505            Vsew::E8 => 0b000,
506            Vsew::E16 => 0b001,
507            Vsew::E32 => 0b010,
508            Vsew::E64 => 0b011,
509        }
510    }
511
512    /// Get the double element width, if available
513    #[inline(always)]
514    pub const fn double_width(self) -> Option<Self> {
515        match self {
516            Self::E8 => Some(Self::E16),
517            Self::E16 => Some(Self::E32),
518            Self::E32 => Some(Self::E64),
519            Self::E64 => {
520                cold_path();
521                None
522            }
523        }
524    }
525
526    /// Divide Vsew width by a given factor
527    #[inline(always)]
528    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
529    pub const fn divide_by_factor(self, factor: VsewFactor) -> Option<Self> {
530        let Some(divide_by_factor) = self.bits_width().div_exact(factor.factor()) else {
531            cold_path();
532            return None;
533        };
534        match divide_by_factor {
535            8 => Some(Self::E8),
536            16 => Some(Self::E16),
537            32 => Some(Self::E32),
538            _ => {
539                cold_path();
540                None
541            }
542        }
543    }
544
545    /// Element width in bits
546    #[inline(always)]
547    pub const fn bits_width(self) -> u8 {
548        match self {
549            Self::E8 => 8,
550            Self::E16 => 16,
551            Self::E32 => 32,
552            Self::E64 => 64,
553        }
554    }
555
556    /// Element width in bytes
557    #[inline(always)]
558    pub const fn bytes_width(self) -> u8 {
559        match self {
560            Self::E8 => 1,
561            Self::E16 => 2,
562            Self::E32 => 4,
563            Self::E64 => 8,
564        }
565    }
566
567    /// Convert to the corresponding `Eew` variant.
568    ///
569    /// Every valid `Vsew` value has a directly corresponding `Eew` value because both
570    /// enumerate the same set of widths (8/16/32/64 bits). The conversion is always
571    /// successful.
572    #[inline(always)]
573    pub const fn as_eew(self) -> Eew {
574        match self {
575            Self::E8 => Eew::E8,
576            Self::E16 => Eew::E16,
577            Self::E32 => Eew::E32,
578            Self::E64 => Eew::E64,
579        }
580    }
581}
582
583impl fmt::Display for Vsew {
584    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
585        match self {
586            Self::E8 => write!(f, "e8"),
587            Self::E16 => write!(f, "e16"),
588            Self::E32 => write!(f, "e32"),
589            Self::E64 => write!(f, "e64"),
590        }
591    }
592}
593
594/// Effective element width for vector memory operations
595#[derive(Debug, Clone, Copy, PartialEq, Eq)]
596#[repr(u8)]
597pub enum Eew {
598    /// 8-bit elements
599    E8 = 1,
600    /// 16-bit elements
601    E16 = 2,
602    /// 32-bit elements
603    E32 = 4,
604    /// 64-bit elements
605    E64 = 8,
606}
607
608impl Eew {
609    /// Max element width in bytes
610    pub const MAX_BYTES: u8 = Self::E64.bytes_width();
611
612    /// Decode the width field into an element width
613    #[inline(always)]
614    pub const fn from_width(width: u8) -> Option<Self> {
615        match width {
616            0b000 => Some(Self::E8),
617            0b101 => Some(Self::E16),
618            0b110 => Some(Self::E32),
619            0b111 => Some(Self::E64),
620            _ => {
621                cold_path();
622                None
623            }
624        }
625    }
626
627    /// Encode to the 3-bit Eew field
628    #[inline(always)]
629    pub const fn to_bits(self) -> u8 {
630        match self {
631            Eew::E8 => 0b000,
632            Eew::E16 => 0b101,
633            Eew::E32 => 0b110,
634            Eew::E64 => 0b111,
635        }
636    }
637
638    /// Element width in bits
639    #[inline(always)]
640    pub const fn bits_width(self) -> u8 {
641        match self {
642            Self::E8 => 8,
643            Self::E16 => 16,
644            Self::E32 => 32,
645            Self::E64 => 64,
646        }
647    }
648
649    /// Element width in bytes.
650    ///
651    /// Guaranteed to be `<= Self::MAX_BYTES`.
652    #[inline(always)]
653    pub const fn bytes_width(self) -> u8 {
654        match self {
655            Self::E8 => 1,
656            Self::E16 => 2,
657            Self::E32 => 4,
658            Self::E64 => 8,
659        }
660    }
661}
662
663impl fmt::Display for Eew {
664    #[inline]
665    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666        fmt::Display::fmt(&self.bits_width(), f)
667    }
668}
669
670/// Vector fixed-point rounding mode.
671///
672/// Encoded in the `vxrm` CSR bits `[1:0]` and mirrored in `vcsr[2:1]`.
673#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
674#[repr(u8)]
675pub enum Vxrm {
676    /// Round-to-nearest-up (rnu)
677    #[default]
678    Rnu = 0b00,
679    /// Round-to-nearest-even (rne)
680    Rne = 0b01,
681    /// Round-down / truncate (rdn)
682    Rdn = 0b10,
683    /// Round-to-odd (rod)
684    Rod = 0b11,
685}
686
687impl Vxrm {
688    /// Decode from a 2-bit field
689    #[inline(always)]
690    pub const fn from_bits(bits: u8) -> Self {
691        match bits & 0b11 {
692            0b00 => Self::Rnu,
693            0b01 => Self::Rne,
694            0b10 => Self::Rdn,
695            _ => Self::Rod,
696        }
697    }
698
699    /// Encode to a 2-bit field
700    #[inline(always)]
701    pub const fn to_bits(self) -> u8 {
702        self as u8
703    }
704}
705
706/// Decoded `vtype` register contents.
707///
708/// The vtype CSR controls the interpretation of the vector register file: element width, register
709/// grouping, and tail/mask agnostic policies.
710///
711/// The raw encoding is XLEN-dependent (vill is at bit XLEN-1), but this decoded form is
712/// XLEN-independent.
713#[derive(Debug, Clone, Copy, PartialEq, Eq)]
714pub struct Vtype<const ELEN: Elen, const VLEN: Vlen>
715where
716    [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
717{
718    /// Vector mask agnostic policy (bit `7`)
719    vma: bool,
720    /// Vector tail agnostic policy (bit `6`)
721    vta: bool,
722    /// Selected element width (bits `[5:3]`)
723    vsew: Vsew,
724    /// Vector length multiplier (bits `[2:0]`)
725    vlmul: Vlmul,
726}
727
728impl<const ELEN: Elen, const VLEN: Vlen> Vtype<ELEN, VLEN>
729where
730    [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
731{
732    /// Vector mask agnostic policy (bit `7`)
733    pub const fn vma(&self) -> bool {
734        self.vma
735    }
736
737    /// Vector tail agnostic policy (bit `6`)
738    pub const fn vta(&self) -> bool {
739        self.vta
740    }
741
742    /// Selected element width (bits `[5:3]`)
743    pub const fn vsew(&self) -> Vsew {
744        self.vsew
745    }
746
747    /// Vector length multiplier (bits `[2:0]`)
748    pub const fn vlmul(&self) -> Vlmul {
749        self.vlmul
750    }
751
752    /// Decode from raw register value.
753    ///
754    /// The `XLEN` is taken from `Reg::XLEN` and must be 32 for RV32 or 64 for RV64. The `vill` bit
755    /// is placed at bit position `Reg::XLEN - 1`.
756    ///
757    /// All bits in `[Reg::XLEN-1:8]` must be zero; non-zero bits indicate an unrecognized
758    /// encoding and cause `None` to be returned (this includes `vill`).
759    #[inline(always)]
760    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
761    pub const fn from_raw<Reg>(raw: Reg::Type) -> Option<Self>
762    where
763        Reg: [const] Register,
764    {
765        let raw = raw.as_u64();
766
767        // All bits in [XLEN-1:8] must be zero
768        if (raw >> 8u8) != 0 {
769            cold_path();
770            return None;
771        }
772
773        let vlmul_bits = (raw & 0b111) as u8;
774        let vsew_bits = ((raw >> 3u8) & 0b111) as u8;
775        let vta = ((raw >> 6u8) & 1) != 0;
776        let vma = ((raw >> 7u8) & 1) != 0;
777
778        let Some(vlmul) = Vlmul::from_bits(vlmul_bits) else {
779            cold_path();
780            return None;
781        };
782        let Some(vsew) = Vsew::from_bits(vsew_bits) else {
783            cold_path();
784            return None;
785        };
786
787        let sew = vsew.bits_width();
788        if u32::from(sew) > u32::from(ELEN) {
789            cold_path();
790            return None;
791        }
792
793        if u32::from(vlmul.vlmax::<VLEN>(vsew)) == 0 {
794            cold_path();
795            return None;
796        }
797
798        Some(Self {
799            vma,
800            vta,
801            vsew,
802            vlmul,
803        })
804    }
805
806    /// Encode to a raw `vtype` register value of type `Reg::Type`.
807    ///
808    /// The encoded value contains `vlmul`, `vsew`, `vta`, and `vma` in bits `[7:0]` with
809    /// `vill = 0`. To construct a raw value with `vill = 1` (illegal configuration), use
810    /// [`Self::illegal_raw`].
811    #[inline(always)]
812    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
813    pub const fn to_raw<Reg>(self) -> Reg::Type
814    where
815        Reg: [const] Register,
816    {
817        let mut raw = 0u8;
818        raw |= self.vlmul.to_bits();
819        raw |= self.vsew.to_bits() << 3u8;
820
821        if self.vta {
822            raw |= 1 << 6u8;
823        }
824
825        if self.vma {
826            raw |= 1 << 7u8;
827        }
828
829        Reg::Type::from(raw)
830    }
831
832    /// Construct a raw value for `vtype` with `vill=1` (illegal configuration).
833    ///
834    /// Per spec: when `vill` is set, the remaining bits are zero and `vl` is also set to zero. Any
835    /// subsequent vector instruction that depends on `vtype` will raise an illegal-instruction
836    /// exception.
837    #[inline(always)]
838    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
839    pub const fn illegal_raw<Reg>() -> Reg::Type
840    where
841        Reg: [const] Register,
842    {
843        let vill_bit = Reg::XLEN - 1;
844        Reg::Type::from(1u8) << vill_bit
845    }
846}