Skip to main content

ab_riscv_primitives/instructions/
v.rs

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