1pub 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#[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 pub const ZERO: Self = Self(0);
55
56 #[inline(always)]
60 pub const fn range_to(self, vl: Vl) -> RangeInclusive<u16> {
61 let vl = u32::from(vl);
62 if vl == 0 {
63 #[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#[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 pub const ZERO: Self = Self(0);
129
130 #[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 #[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 #[inline(always)]
151 pub const fn bytes(self) -> u16 {
152 self.0.div_ceil(u8::BITS) as u16
153 }
154}
155
156#[derive(ConstParamTy, Debug, Clone, Copy)]
158#[derive_const(PartialEq, Eq)]
159#[repr(u32)]
160pub enum Elen {
161 L8 = 8,
163 L16 = 16,
165 L32 = 32,
167 L64 = 64,
169 L128 = 128,
171 L256 = 256,
173 L512 = 512,
175 L1024 = 1024,
177 L2048 = 2048,
179 L4096 = 4096,
181 L8192 = 8192,
183 L16_384 = 16_384,
185 L32_768 = 32_768,
187 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#[derive(ConstParamTy, Debug, Clone, Copy)]
200#[derive_const(PartialEq, Eq)]
201#[repr(u32)]
202pub enum Vlen {
203 L8 = 8,
205 L16 = 16,
207 L32 = 32,
209 L64 = 64,
211 L128 = 128,
213 L256 = 256,
215 L512 = 512,
217 L1024 = 1024,
219 L2048 = 2048,
221 L4096 = 4096,
223 L8192 = 8192,
225 L16_384 = 16_384,
227 L32_768 = 32_768,
229 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 #[inline(always)]
243 pub const fn bytes(self) -> u32 {
244 self as u32 / u8::BITS
245 }
246}
247
248pub 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#[derive(Debug, Clone, Copy)]
263#[derive_const(PartialEq, Eq)]
264#[repr(u8)]
265pub enum VsStatus {
266 Off = 0,
268 Initial = 1,
270 Clean = 2,
272 Dirty = 3,
274}
275
276impl VsStatus {
277 #[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 #[inline(always)]
290 pub const fn to_bits(self) -> u8 {
291 self as u8
292 }
293}
294
295#[derive(Debug, Clone, Copy)]
301#[derive_const(PartialEq, Eq)]
302#[repr(u8)]
303pub enum Vlmul {
304 M1 = 0b000,
306 M2 = 0b001,
308 M4 = 0b010,
310 M8 = 0b011,
312 Mf8 = 0b101,
314 Mf4 = 0b110,
316 Mf2 = 0b111,
318}
319
320impl Vlmul {
321 #[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 #[inline(always)]
338 pub const fn to_bits(self) -> u8 {
339 self as u8
340 }
341
342 #[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 unsafe { Vl::new(vl).unwrap_unchecked() }
361 }
362
363 #[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 #[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 #[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 let g = if num < den { num } else { den };
413 let (n, d) = (num / g, den / g);
414 #[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 Some(NonZeroU8::new(if d > 1 { 1 } else { n as u8 }).expect("Not zero; qed"))
426 }
427
428 #[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#[derive(Debug, Clone, Copy)]
460#[derive_const(PartialEq, Eq)]
461#[repr(u8)]
462pub enum VsewFactor {
463 F2 = 2,
465 F4 = 4,
467 F8 = 8,
469}
470
471impl VsewFactor {
472 #[inline(always)]
474 pub const fn factor(self) -> u8 {
475 self as u8
476 }
477}
478
479#[derive(Debug, Clone, Copy)]
483#[derive_const(PartialEq, Eq)]
484#[repr(u8)]
485pub enum Vsew {
486 E8 = 8,
488 E16 = 16,
490 E32 = 32,
492 E64 = 64,
494}
495
496impl Vsew {
497 #[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 #[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 panic!("Invalid register width")
527 }
528 }
529 }
530 }
531
532 #[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 #[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 #[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 #[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 #[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 #[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#[derive(Debug, Clone, Copy)]
627#[derive_const(PartialEq, Eq)]
628#[repr(u8)]
629pub enum Eew {
630 E8 = 1,
632 E16 = 2,
634 E32 = 4,
636 E64 = 8,
638}
639
640impl Eew {
641 pub const MAX_BYTES: u8 = Self::E64.bytes_width();
643
644 #[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 #[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 #[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 #[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#[derive(Debug, Clone, Copy)]
706#[derive_const(Default, PartialEq, Eq)]
707#[repr(u8)]
708pub enum Vxrm {
709 #[default]
711 Rnu = 0b00,
712 Rne = 0b01,
714 Rdn = 0b10,
716 Rod = 0b11,
718}
719
720impl Vxrm {
721 #[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 #[inline(always)]
734 pub const fn to_bits(self) -> u8 {
735 self as u8
736 }
737}
738
739#[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 vma: bool,
754 vta: bool,
756 vsew: Vsew,
758 vlmul: Vlmul,
760}
761
762impl<const ELEN: Elen, const VLEN: Vlen> Vtype<ELEN, VLEN>
763where
764 [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
765{
766 pub const fn vma(&self) -> bool {
768 self.vma
769 }
770
771 pub const fn vta(&self) -> bool {
773 self.vta
774 }
775
776 pub const fn vsew(&self) -> Vsew {
778 self.vsew
779 }
780
781 pub const fn vlmul(&self) -> Vlmul {
783 self.vlmul
784 }
785
786 #[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 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 #[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 #[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#[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}