Skip to main content

ab_riscv_primitives/instructions/v/zvexx/
load.rs

1//! ZveXx vector load instructions
2
3#[cfg(test)]
4mod tests;
5
6use crate::hart::{HartConfig, VectorHartConfig};
7use crate::instructions::v::{Eew, V, VRegGroupSize};
8use crate::instructions::{ImplementedExtension, Instruction, IsaExtension};
9use crate::registers::general_purpose::Register;
10use crate::registers::vector::VReg;
11use ab_riscv_macros::instruction;
12use core::fmt;
13
14/// Number of fields per segment for load/store instructions
15#[derive(Debug, Clone, Copy)]
16#[derive_const(PartialEq, Eq)]
17#[repr(u8)]
18pub enum Nf {
19    /// 1 field per segment
20    N1 = 1,
21    /// 2 fields per segment
22    N2 = 2,
23    /// 3 fields per segment
24    N3 = 3,
25    /// 4 fields per segment
26    N4 = 4,
27    /// 5 fields per segment
28    N5 = 5,
29    /// 6 fields per segment
30    N6 = 6,
31    /// 7 fields per segment
32    N7 = 7,
33    /// 8 fields per segment
34    N8 = 8,
35}
36
37impl fmt::Display for Nf {
38    #[inline]
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        fmt::Display::fmt(&self.fields_per_segment(), f)
41    }
42}
43
44impl Nf {
45    /// Maximum allowed value for `Nf`
46    pub const MAX: Self = Nf::N8;
47
48    /// Create a new instance.
49    ///
50    /// `nf` must be in the range `1..=8` or `None` is returned.
51    #[inline(always)]
52    pub const fn new(nf: u8) -> Option<Self> {
53        match nf {
54            1 => Some(Nf::N1),
55            2 => Some(Nf::N2),
56            3 => Some(Nf::N3),
57            4 => Some(Nf::N4),
58            5 => Some(Nf::N5),
59            6 => Some(Nf::N6),
60            7 => Some(Nf::N7),
61            8 => Some(Nf::N8),
62            _ => None,
63        }
64    }
65
66    /// Returns the number of fields per segment for the load/store instruction.
67    ///
68    /// Always in `1..=8` range.
69    #[inline(always)]
70    pub const fn fields_per_segment(&self) -> u8 {
71        *self as u8
72    }
73}
74
75/// `vm` and `nf` fields for segmented load/store instructions.
76///
77/// This is a more compact representation that fits within a single byte rather than two when
78/// storing these separately.
79#[derive(Debug, Clone, Copy)]
80#[derive_const(PartialEq, Eq)]
81pub struct SegVmNf(u8);
82
83impl SegVmNf {
84    /// Create a new instance
85    #[inline(always)]
86    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
87    pub const fn new(vm: bool, nf: Nf) -> Self {
88        Self((nf.fields_per_segment() << 1) | u8::from(vm))
89    }
90
91    /// Extracts the `vm` field from the `SegVmNf` representation
92    #[inline(always)]
93    pub const fn vm(&self) -> bool {
94        self.0 & 1 == 1
95    }
96
97    /// Extracts the `nf` field from the `SegVmNf` representation
98    #[inline(always)]
99    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
100    pub const fn nf(&self) -> Nf {
101        // SAFETY: Protected internal invariant
102        unsafe { Nf::new(self.0 >> 1).unwrap_unchecked() }
103    }
104}
105
106/// `nreg` field for load/store instructions
107#[derive(Debug, Clone, Copy)]
108#[derive_const(PartialEq, Eq)]
109#[repr(u8)]
110pub enum LoadStoreNreg {
111    /// 1 register
112    N1 = 1,
113    /// 2 registers
114    N2 = 2,
115    /// 4 registers
116    N4 = 4,
117    /// 8 registers
118    N8 = 8,
119}
120
121impl fmt::Display for LoadStoreNreg {
122    #[inline]
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        fmt::Display::fmt(&self.num_registers(), f)
125    }
126}
127
128impl LoadStoreNreg {
129    /// Create a new instance
130    #[inline(always)]
131    pub const fn new(n: u8) -> Option<Self> {
132        match n {
133            1 => Some(Self::N1),
134            2 => Some(Self::N2),
135            4 => Some(Self::N4),
136            8 => Some(Self::N8),
137            _ => None,
138        }
139    }
140
141    /// Get the number of registers
142    #[inline(always)]
143    pub const fn num_registers(&self) -> VRegGroupSize {
144        match self {
145            Self::N1 => VRegGroupSize::R1,
146            Self::N2 => VRegGroupSize::R2,
147            Self::N4 => VRegGroupSize::R4,
148            Self::N8 => VRegGroupSize::R8,
149        }
150    }
151}
152
153/// RISC-V ZveXx vector load instruction.
154///
155/// Encoded under the LOAD-FP major opcode (0x07). All loads use rs1 (GPR) as a base address and vd
156/// (vector register) as a destination.
157#[instruction]
158#[derive(Debug, Clone, Copy)]
159#[derive_const(PartialEq, Eq)]
160#[rustfmt::skip]
161#[doc(hidden)]
162pub enum ZveXxLoadInstruction<Hart>
163where
164    Hart: HartConfig,
165{
166    /// Unit-stride load: `vle{eew}.v vd, (rs1), vm`
167    ///
168    /// mop=00, lumop=00000, nf=000
169    Vle { vd: VReg, rs1: Hart::Reg, vm: bool, eew: Eew },
170    /// Unit-stride fault-only-first load: `vle{eew}ff.v vd, (rs1), vm`
171    ///
172    /// mop=00, lumop=10000, nf=000
173    Vleff { vd: VReg, rs1: Hart::Reg, vm: bool, eew: Eew },
174    /// Unit-stride mask load: `vlm.v vd, (rs1)`
175    ///
176    /// mop=00, lumop=01011, nf=000, eew=e8, vm=1
177    Vlm { vd: VReg, rs1: Hart::Reg },
178    /// Strided load: `vlse{eew}.v vd, (rs1), rs2, vm`
179    ///
180    /// mop=10, nf=000
181    Vlse { vd: VReg, rs1: Hart::Reg, rs2: Hart::Reg, vm: bool, eew: Eew },
182    /// Indexed-unordered load: `vluxei{eew}.v vd, (rs1), vs2, vm`
183    ///
184    /// mop=01, nf=000. eew is the index element width.
185    Vluxei { vd: VReg, rs1: Hart::Reg, vs2: VReg, vm: bool, eew: Eew },
186    /// Indexed-ordered load: `vloxei{eew}.v vd, (rs1), vs2, vm`
187    ///
188    /// mop=11, nf=000. eew is the index element width.
189    Vloxei { vd: VReg, rs1: Hart::Reg, vs2: VReg, vm: bool, eew: Eew },
190    /// Whole-register load: `vl{nreg}re{eew}.v vd, (rs1)`
191    ///
192    /// mop=00, lumop=01000, vm=1. nreg must be 1, 2, 4, or 8.
193    Vlr { vd: VReg, rs1: Hart::Reg, nreg: LoadStoreNreg, eew: Eew },
194    /// Unit-stride segment load: `vlseg{nf}e{eew}.v vd, (rs1), vm`
195    ///
196    /// mop=00, lumop=00000, nf>0
197    Vlseg { vd: VReg, rs1: Hart::Reg, eew: Eew, vm_nf: SegVmNf },
198    /// Unit-stride fault-only-first segment load: `vlseg{nf}e{eew}ff.v vd, (rs1), vm`
199    ///
200    /// mop=00, lumop=10000, nf>0
201    Vlsegff { vd: VReg, rs1: Hart::Reg, eew: Eew, vm_nf: SegVmNf },
202    /// Strided segment load: `vlsseg{nf}e{eew}.v vd, (rs1), rs2, vm`
203    ///
204    /// mop=10, nf>0
205    Vlsseg { vd: VReg, rs1: Hart::Reg, rs2: Hart::Reg, eew: Eew, vm_nf: SegVmNf },
206    /// Indexed-unordered segment load: `vluxseg{nf}ei{eew}.v vd, (rs1), vs2, vm`
207    ///
208    /// mop=01, nf>0
209    Vluxseg { vd: VReg, rs1: Hart::Reg, vs2: VReg, eew: Eew, vm_nf: SegVmNf },
210    /// Indexed-ordered segment load: `vloxseg{nf}ei{eew}.v vd, (rs1), vs2, vm`
211    ///
212    /// mop=11, nf>0
213    Vloxseg { vd: VReg, rs1: Hart::Reg, vs2: VReg, eew: Eew, vm_nf: SegVmNf },
214}
215
216#[instruction]
217const impl<Reg, Hart> Instruction for ZveXxLoadInstruction<Hart>
218where
219    Reg: [const] Register,
220    Hart: [const] VectorHartConfig<Reg = Reg>,
221{
222    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[];
223
224    const ALIGNMENT: u8 = align_of::<u32>() as u8;
225
226    type Hart = Hart;
227
228    #[inline(always)]
229    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
230    fn try_decode(instruction: u32) -> Option<Self> {
231        let opcode = (instruction & 0b111_1111) as u8;
232
233        // LOAD-FP major opcode
234        if opcode != 0b000_0111 {
235            None?;
236        }
237
238        let vd_bits = ((instruction >> 7) & 0x1f) as u8;
239        let width = ((instruction >> 12) & 0b111) as u8;
240        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
241        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
242        let vm = ((instruction >> 25) & 1) != 0;
243        let mop = ((instruction >> 26) & 0b11) as u8;
244        let mew = ((instruction >> 28) & 1) as u8;
245        let nf = ((instruction >> 29) & 0b111) as u8;
246
247        // mew must be 0 (reserved for >=128-bit)
248        if mew != 0 {
249            None?;
250        }
251
252        let vd = VReg::from_bits(vd_bits)?;
253        if vd.is_mask(vm) {
254            None?;
255        }
256        let rs1 = Reg::from_bits(rs1_bits)?;
257
258        // nf encodes number of fields minus 1 (nf=0 means 1 field)
259        let nf_val = nf + 1;
260
261        match mop {
262            // Unit-stride
263            0b00 => {
264                let lumop = rs2_bits;
265                match lumop {
266                    // Regular unit-stride load
267                    0b0_0000 => {
268                        let eew = Eew::from_width(width)?;
269                        // `EEW > ELEN` is reserved
270                        if !Hart::VECTOR_LENGTHS.elen.supports(eew) {
271                            None?;
272                        }
273                        if nf == 0 {
274                            Some(Self::Vle { vd, rs1, vm, eew })
275                        } else {
276                            Some(Self::Vlseg {
277                                vd,
278                                rs1,
279                                eew,
280                                vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
281                            })
282                        }
283                    }
284                    // Whole-register load
285                    0b0_1000 => {
286                        // vm must be 1 (unmasked)
287                        if !vm {
288                            None?;
289                        }
290                        let eew = Eew::from_width(width)?;
291                        // `EEW > ELEN` is reserved
292                        if !Hart::VECTOR_LENGTHS.elen.supports(eew) {
293                            None?;
294                        }
295                        let nreg = LoadStoreNreg::new(nf_val)?;
296                        Some(Self::Vlr { vd, rs1, nreg, eew })
297                    }
298                    // Mask load
299                    0b0_1011 => {
300                        // Must be eew=e8, vm=1, nf=0
301                        if width != 0b000 || !vm || nf != 0 {
302                            None?;
303                        }
304                        Some(Self::Vlm { vd, rs1 })
305                    }
306                    // Fault-only-first
307                    0b1_0000 => {
308                        let eew = Eew::from_width(width)?;
309                        // `EEW > ELEN` is reserved
310                        if !Hart::VECTOR_LENGTHS.elen.supports(eew) {
311                            None?;
312                        }
313                        if nf == 0 {
314                            Some(Self::Vleff { vd, rs1, vm, eew })
315                        } else {
316                            Some(Self::Vlsegff {
317                                vd,
318                                rs1,
319                                eew,
320                                vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
321                            })
322                        }
323                    }
324                    _ => None,
325                }
326            }
327            // Indexed-unordered
328            0b01 => {
329                let eew = Eew::from_width(width)?;
330                // `EEW > ELEN` is reserved
331                if !Hart::VECTOR_LENGTHS.elen.supports(eew) {
332                    None?;
333                }
334                let vs2 = VReg::from_bits(rs2_bits)?;
335                if vs2.is_mask(vm) {
336                    None?;
337                }
338
339                if !Self::implements_extension::<V<_>>()
340                    && Reg::XLEN == u32::BITS as u8
341                    && eew == Eew::E64
342                {
343                    None?;
344                }
345
346                if nf == 0 {
347                    Some(Self::Vluxei {
348                        vd,
349                        rs1,
350                        vs2,
351                        vm,
352                        eew,
353                    })
354                } else {
355                    Some(Self::Vluxseg {
356                        vd,
357                        rs1,
358                        vs2,
359                        eew,
360                        vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
361                    })
362                }
363            }
364            // Strided
365            0b10 => {
366                let eew = Eew::from_width(width)?;
367                // `EEW > ELEN` is reserved
368                if !Hart::VECTOR_LENGTHS.elen.supports(eew) {
369                    None?;
370                }
371                let rs2 = Reg::from_bits(rs2_bits)?;
372                if nf == 0 {
373                    Some(Self::Vlse {
374                        vd,
375                        rs1,
376                        rs2,
377                        vm,
378                        eew,
379                    })
380                } else {
381                    Some(Self::Vlsseg {
382                        vd,
383                        rs1,
384                        rs2,
385                        eew,
386                        vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
387                    })
388                }
389            }
390            // Indexed-ordered
391            0b11 => {
392                let eew = Eew::from_width(width)?;
393                // `EEW > ELEN` is reserved
394                if !Hart::VECTOR_LENGTHS.elen.supports(eew) {
395                    None?;
396                }
397                let vs2 = VReg::from_bits(rs2_bits)?;
398                if vs2.is_mask(vm) {
399                    None?;
400                }
401
402                if !Self::implements_extension::<V<_>>()
403                    && Reg::XLEN == u32::BITS as u8
404                    && eew == Eew::E64
405                {
406                    None?;
407                }
408
409                if nf == 0 {
410                    Some(Self::Vloxei {
411                        vd,
412                        rs1,
413                        vs2,
414                        vm,
415                        eew,
416                    })
417                } else {
418                    Some(Self::Vloxseg {
419                        vd,
420                        rs1,
421                        vs2,
422                        eew,
423                        vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
424                    })
425                }
426            }
427            _ => None,
428        }
429    }
430
431    #[inline(always)]
432    fn size(&self) -> u8 {
433        size_of::<u32>() as u8
434    }
435}
436
437#[instruction]
438impl<Reg, Hart> fmt::Display for ZveXxLoadInstruction<Hart>
439where
440    Reg: fmt::Display,
441    Hart: HartConfig<Reg = Reg>,
442{
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        #[rustfmt::skip]
445        match self {
446            Self::Vle { vd, rs1, vm, eew } => write!(f, "vle{eew}.v {vd}, ({rs1}){}", mask_suffix(vm)),
447            Self::Vleff { vd, rs1, vm, eew } => write!(f, "vle{eew}ff.v {vd}, ({rs1}){}", mask_suffix(vm)),
448            Self::Vlm { vd, rs1 } => write!(f, "vlm.v {vd}, ({rs1})"),
449            Self::Vlse { vd, rs1, rs2, vm, eew } => write!(f, "vlse{eew}.v {vd}, ({rs1}), {rs2}{}", mask_suffix(vm)),
450            Self::Vluxei { vd, rs1, vs2, vm, eew } => write!(f, "vluxei{eew}.v {vd}, ({rs1}), {vs2}{}", mask_suffix(vm)),
451            Self::Vloxei { vd, rs1, vs2, vm, eew } => write!(f, "vloxei{eew}.v {vd}, ({rs1}), {vs2}{}", mask_suffix(vm)),
452            Self::Vlr { vd, rs1, nreg, eew } => write!(f, "vl{nreg}re{eew}.v {vd}, ({rs1})"),
453            Self::Vlseg { vd, rs1, eew, vm_nf } => write!(f, "vlseg{}e{eew}.v {vd}, ({rs1}){}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
454            Self::Vlsegff { vd, rs1, eew, vm_nf } => write!(f, "vlseg{}e{eew}ff.v {vd}, ({rs1}){}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
455            Self::Vlsseg { vd, rs1, rs2, eew, vm_nf } => write!(f, "vlsseg{}e{eew}.v {vd}, ({rs1}), {rs2}{}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
456            Self::Vluxseg { vd, rs1, vs2, eew, vm_nf } => write!(f, "vluxseg{}ei{eew}.v {vd}, ({rs1}), {vs2}{}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
457            Self::Vloxseg { vd, rs1, vs2, eew, vm_nf } => write!(f, "vloxseg{}ei{eew}.v {vd}, ({rs1}), {vs2}{}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
458        }
459    }
460}
461
462/// Format mask suffix for display
463#[inline(always)]
464fn mask_suffix(vm: &bool) -> &'static str {
465    if *vm { "" } else { ", v0.t" }
466}