1pub 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#[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 pub const ZERO: Self = Self(0);
52
53 #[inline(always)]
57 pub const fn range_to(self, vl: Vl) -> RangeInclusive<u16> {
58 let vl = u32::from(vl);
59 if vl == 0 {
60 #[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#[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 pub const ZERO: Self = Self(0);
125
126 #[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 #[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 #[inline(always)]
147 pub const fn bytes(self) -> u16 {
148 self.0.div_ceil(u8::BITS) as u16
149 }
150}
151
152#[derive(ConstParamTy, Debug, Clone, Copy, PartialEq, Eq)]
154#[repr(u32)]
155pub enum Elen {
156 L8 = 8,
158 L16 = 16,
160 L32 = 32,
162 L64 = 64,
164 L128 = 128,
166 L256 = 256,
168 L512 = 512,
170 L1024 = 1024,
172 L2048 = 2048,
174 L4096 = 4096,
176 L8192 = 8192,
178 L16_384 = 16_384,
180 L32_768 = 32_768,
182 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#[derive(ConstParamTy, Debug, Clone, Copy, PartialEq, Eq)]
195#[repr(u32)]
196pub enum Vlen {
197 L8 = 8,
199 L16 = 16,
201 L32 = 32,
203 L64 = 64,
205 L128 = 128,
207 L256 = 256,
209 L512 = 512,
211 L1024 = 1024,
213 L2048 = 2048,
215 L4096 = 4096,
217 L8192 = 8192,
219 L16_384 = 16_384,
221 L32_768 = 32_768,
223 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 #[inline(always)]
237 pub const fn bytes(self) -> u32 {
238 self as u32 / u8::BITS
239 }
240}
241
242pub 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257#[repr(u8)]
258pub enum VsStatus {
259 Off = 0,
261 Initial = 1,
263 Clean = 2,
265 Dirty = 3,
267}
268
269impl VsStatus {
270 #[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 #[inline(always)]
283 pub const fn to_bits(self) -> u8 {
284 self as u8
285 }
286}
287
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
294#[repr(u8)]
295pub enum Vlmul {
296 M1 = 0b000,
298 M2 = 0b001,
300 M4 = 0b010,
302 M8 = 0b011,
304 Mf8 = 0b101,
306 Mf4 = 0b110,
308 Mf2 = 0b111,
310}
311
312impl Vlmul {
313 #[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 #[inline(always)]
330 pub const fn to_bits(self) -> u8 {
331 self as u8
332 }
333
334 #[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 unsafe { Vl::new(vl).unwrap_unchecked() }
353 }
354
355 #[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 #[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 #[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 let g = if num < den { num } else { den };
405 let (n, d) = (num / g, den / g);
406 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 Some(NonZeroU8::new(if d > 1 { 1 } else { n as u8 }).expect("Not zero; qed"))
417 }
418
419 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
451#[repr(u8)]
452pub enum VsewFactor {
453 F2 = 2,
455 F4 = 4,
457 F8 = 8,
459}
460
461impl VsewFactor {
462 #[inline(always)]
464 pub const fn factor(self) -> u8 {
465 self as u8
466 }
467}
468
469#[derive(Debug, Clone, Copy, PartialEq, Eq)]
473#[repr(u8)]
474pub enum Vsew {
475 E8 = 8,
477 E16 = 16,
479 E32 = 32,
481 E64 = 64,
483}
484
485impl Vsew {
486 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
596#[repr(u8)]
597pub enum Eew {
598 E8 = 1,
600 E16 = 2,
602 E32 = 4,
604 E64 = 8,
606}
607
608impl Eew {
609 pub const MAX_BYTES: u8 = Self::E64.bytes_width();
611
612 #[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 #[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 #[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 #[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#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
674#[repr(u8)]
675pub enum Vxrm {
676 #[default]
678 Rnu = 0b00,
679 Rne = 0b01,
681 Rdn = 0b10,
683 Rod = 0b11,
685}
686
687impl Vxrm {
688 #[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 #[inline(always)]
701 pub const fn to_bits(self) -> u8 {
702 self as u8
703 }
704}
705
706#[derive(Debug, Clone, Copy, PartialEq, Eq)]
714pub struct Vtype<const ELEN: Elen, const VLEN: Vlen>
715where
716 [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
717{
718 vma: bool,
720 vta: bool,
722 vsew: Vsew,
724 vlmul: Vlmul,
726}
727
728impl<const ELEN: Elen, const VLEN: Vlen> Vtype<ELEN, VLEN>
729where
730 [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
731{
732 pub const fn vma(&self) -> bool {
734 self.vma
735 }
736
737 pub const fn vta(&self) -> bool {
739 self.vta
740 }
741
742 pub const fn vsew(&self) -> Vsew {
744 self.vsew
745 }
746
747 pub const fn vlmul(&self) -> Vlmul {
749 self.vlmul
750 }
751
752 #[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 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 #[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 #[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}