ab_riscv_primitives/instructions/v.rs
1//! V extension
2
3pub mod zve64x;
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()) * u16::from(lmul_num);
146 let den = u16::from(sew.bits()) * 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/// Selected element width (SEW).
192///
193/// Encoded in `vtype[5:3]` as `vsew`. `SEW = 8 * 2^vsew`.
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195#[repr(u8)]
196pub enum Vsew {
197 /// SEW = 8 bits (vsew = 0b000)
198 E8 = 0b000,
199 /// SEW = 16 bits (vsew = 0b001)
200 E16 = 0b001,
201 /// SEW = 32 bits (vsew = 0b010)
202 E32 = 0b010,
203 /// SEW = 64 bits (vsew = 0b011)
204 E64 = 0b011,
205}
206
207impl Vsew {
208 /// Decode from the 3-bit vsew field. Returns `None` for reserved encodings.
209 #[inline(always)]
210 pub const fn from_bits(bits: u8) -> Option<Self> {
211 match bits & 0b111 {
212 0b000 => Some(Self::E8),
213 0b001 => Some(Self::E16),
214 0b010 => Some(Self::E32),
215 0b011 => Some(Self::E64),
216 _ => None,
217 }
218 }
219
220 /// Encode to the 3-bit vsew field
221 #[inline(always)]
222 pub const fn to_bits(self) -> u8 {
223 self as u8
224 }
225
226 /// Element width in bits
227 #[inline(always)]
228 pub const fn bits(self) -> u8 {
229 match self {
230 Self::E8 => 8,
231 Self::E16 => 16,
232 Self::E32 => 32,
233 Self::E64 => 64,
234 }
235 }
236
237 /// Element width in bytes
238 #[inline(always)]
239 pub const fn bytes(self) -> u8 {
240 match self {
241 Self::E8 => 1,
242 Self::E16 => 2,
243 Self::E32 => 4,
244 Self::E64 => 8,
245 }
246 }
247
248 /// Convert to the corresponding `Eew` variant.
249 ///
250 /// Every valid `Vsew` value has a directly corresponding `Eew` value because both
251 /// enumerate the same set of widths (8/16/32/64 bits). The conversion is always
252 /// successful.
253 #[inline(always)]
254 pub const fn as_eew(self) -> Eew {
255 match self {
256 Self::E8 => Eew::E8,
257 Self::E16 => Eew::E16,
258 Self::E32 => Eew::E32,
259 Self::E64 => Eew::E64,
260 }
261 }
262}
263
264impl fmt::Display for Vsew {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 match self {
267 Self::E8 => write!(f, "e8"),
268 Self::E16 => write!(f, "e16"),
269 Self::E32 => write!(f, "e32"),
270 Self::E64 => write!(f, "e64"),
271 }
272 }
273}
274
275/// Effective element width for vector memory operations
276#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277#[repr(u8)]
278pub enum Eew {
279 /// 8-bit elements
280 E8 = 0b000,
281 /// 16-bit elements
282 E16 = 0b101,
283 /// 32-bit elements
284 E32 = 0b110,
285 /// 64-bit elements
286 E64 = 0b111,
287}
288
289impl Eew {
290 /// Max element width in bytes
291 pub const MAX_BYTES: u8 = 8;
292
293 /// Decode the width field into an element width
294 #[inline(always)]
295 pub const fn from_width(width: u8) -> Option<Self> {
296 match width {
297 0b000 => Some(Self::E8),
298 0b101 => Some(Self::E16),
299 0b110 => Some(Self::E32),
300 0b111 => Some(Self::E64),
301 _ => None,
302 }
303 }
304
305 /// Element width in bits
306 #[inline(always)]
307 pub const fn bits(self) -> u8 {
308 match self {
309 Self::E8 => 8,
310 Self::E16 => 16,
311 Self::E32 => 32,
312 Self::E64 => 64,
313 }
314 }
315
316 /// Element width in bytes.
317 ///
318 /// Guaranteed to be `<= Self::MAX_BYTES`.
319 #[inline(always)]
320 pub const fn bytes(self) -> u8 {
321 match self {
322 Self::E8 => 1,
323 Self::E16 => 2,
324 Self::E32 => 4,
325 Self::E64 => 8,
326 }
327 }
328}
329
330impl fmt::Display for Eew {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 self.bits().fmt(f)
333 }
334}
335
336/// Vector fixed-point rounding mode.
337///
338/// Encoded in the `vxrm` CSR bits `[1:0]` and mirrored in `vcsr[2:1]`.
339#[derive(Debug, Clone, Copy, PartialEq, Eq)]
340#[repr(u8)]
341pub enum Vxrm {
342 /// Round-to-nearest-up (rnu)
343 Rnu = 0b00,
344 /// Round-to-nearest-even (rne)
345 Rne = 0b01,
346 /// Round-down / truncate (rdn)
347 Rdn = 0b10,
348 /// Round-to-odd (rod)
349 Rod = 0b11,
350}
351
352impl Vxrm {
353 /// Decode from a 2-bit field
354 #[inline(always)]
355 pub const fn from_bits(bits: u8) -> Self {
356 match bits & 0b11 {
357 0b00 => Self::Rnu,
358 0b01 => Self::Rne,
359 0b10 => Self::Rdn,
360 _ => Self::Rod,
361 }
362 }
363
364 /// Encode to a 2-bit field
365 #[inline(always)]
366 pub const fn to_bits(self) -> u8 {
367 self as u8
368 }
369}
370
371/// Decoded `vtype` register contents.
372///
373/// The vtype CSR controls the interpretation of the vector register file: element width, register
374/// grouping, and tail/mask agnostic policies.
375///
376/// The raw encoding is XLEN-dependent (vill is at bit XLEN-1), but this decoded form is
377/// XLEN-independent.
378#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379pub struct Vtype<const ELEN: u32, const VLEN: u32> {
380 /// Vector mask agnostic policy (bit `7`)
381 vma: bool,
382 /// Vector tail agnostic policy (bit `6`)
383 vta: bool,
384 /// Selected element width (bits `[5:3]`)
385 vsew: Vsew,
386 /// Vector length multiplier (bits `[2:0]`)
387 vlmul: Vlmul,
388}
389
390impl<const ELEN: u32, const VLEN: u32> Vtype<ELEN, VLEN> {
391 /// Vector mask agnostic policy (bit `7`)
392 pub const fn vma(&self) -> bool {
393 self.vma
394 }
395
396 /// Vector tail agnostic policy (bit `6`)
397 pub const fn vta(&self) -> bool {
398 self.vta
399 }
400
401 /// Selected element width (bits `[5:3]`)
402 pub const fn vsew(&self) -> Vsew {
403 self.vsew
404 }
405
406 /// Vector length multiplier (bits `[2:0]`)
407 pub const fn vlmul(&self) -> Vlmul {
408 self.vlmul
409 }
410
411 /// Decode from raw register value.
412 ///
413 /// The `XLEN` is taken from `Reg::XLEN` and must be 32 for RV32 or 64 for RV64. The `vill` bit
414 /// is placed at bit position `Reg::XLEN - 1`.
415 ///
416 /// All bits in `[Reg::XLEN-1:8]` must be zero; non-zero bits indicate an unrecognized
417 /// encoding and cause `None` to be returned (this includes `vill`).
418 #[inline(always)]
419 pub const fn from_raw<Reg>(raw: Reg::Type) -> Option<Self>
420 where
421 Reg: [const] Register,
422 {
423 let raw = raw.as_u64();
424
425 // All bits in [XLEN-1:8] must be zero
426 if (raw >> 8) != 0 {
427 return None;
428 }
429
430 let vlmul_bits = (raw & 0b111) as u8;
431 let vsew_bits = ((raw >> 3) & 0b111) as u8;
432 let vta = ((raw >> 6) & 1) != 0;
433 let vma = ((raw >> 7) & 1) != 0;
434
435 let vlmul = Vlmul::from_bits(vlmul_bits)?;
436 let vsew = Vsew::from_bits(vsew_bits)?;
437
438 let sew = vsew.bits();
439 if u32::from(sew) > ELEN {
440 return None;
441 }
442
443 if vlmul.vlmax(VLEN, u32::from(sew)) == 0 {
444 return None;
445 }
446
447 Some(Self {
448 vma,
449 vta,
450 vsew,
451 vlmul,
452 })
453 }
454
455 /// Encode to a raw `vtype` register value of type `Reg::Type`.
456 ///
457 /// The encoded value contains `vlmul`, `vsew`, `vta`, and `vma` in bits `[7:0]` with
458 /// `vill = 0`. To construct a raw value with `vill = 1` (illegal configuration), use
459 /// [`Self::illegal_raw`].
460 #[inline(always)]
461 pub const fn to_raw<Reg>(self) -> Reg::Type
462 where
463 Reg: [const] Register,
464 {
465 let mut raw = 0u8;
466 raw |= self.vlmul.to_bits();
467 raw |= self.vsew.to_bits() << 3;
468
469 if self.vta {
470 raw |= 1 << 6;
471 }
472
473 if self.vma {
474 raw |= 1 << 7;
475 }
476
477 Reg::Type::from(raw)
478 }
479
480 /// Construct a raw value for `vtype` with `vill=1` (illegal configuration).
481 ///
482 /// Per spec: when `vill` is set, the remaining bits are zero and `vl` is also set to zero. Any
483 /// subsequent vector instruction that depends on `vtype` will raise an illegal-instruction
484 /// exception.
485 #[inline(always)]
486 pub const fn illegal_raw<Reg>() -> Reg::Type
487 where
488 Reg: [const] Register,
489 {
490 let vill_bit = Reg::XLEN - 1;
491 Reg::Type::from(1u8) << vill_bit
492 }
493}