Skip to main content

ab_riscv_interpreter/v/zvexx/config/
zvexx_config_helpers.rs

1//! Opaque helpers for ZveXx extension
2
3use crate::v::vector_registers::VectorRegistersExt;
4use crate::v::zvexx::zvexx_helpers::INSTRUCTION_SIZE;
5use crate::{ExecutionError, ProgramCounter, RegisterFile};
6use ab_riscv_primitives::prelude::*;
7use core::fmt;
8
9/// Apply `vsetvli` / `vsetvl` logic.
10///
11/// Both share identical `AVL` resolution; they differ only in how the `vtype` value is obtained
12/// (immediate for `vsetvli`, register for `vsetvl`).
13#[doc(hidden)]
14pub fn apply_vsetvl<Reg, Regs, ExtState, Memory, PC, CustomError>(
15    regs: &mut Regs,
16    ext_state: &mut ExtState,
17    program_counter: &PC,
18    rd: Reg,
19    rs1: Reg,
20    rs1_value: Reg::Type,
21    vtype_raw: Reg::Type,
22) -> Result<(), ExecutionError<Reg::Type, CustomError>>
23where
24    Reg: Register,
25    Regs: RegisterFile<Reg>,
26    ExtState: VectorRegistersExt<Reg, CustomError>,
27    [(); ExtState::ELEN as usize]:,
28    [(); ExtState::VLEN as usize]:,
29    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
30    CustomError: fmt::Debug,
31{
32    // Check whether vector instructions are enabled
33    if !ext_state.vector_instructions_allowed() {
34        return Err(ExecutionError::IllegalInstruction {
35            address: program_counter.old_pc(INSTRUCTION_SIZE),
36        });
37    }
38
39    let Some(new_vtype) = Vtype::from_raw::<Reg>(vtype_raw) else {
40        ext_state.set_vtype(None);
41        ext_state.set_vl(0);
42        regs.write(rd, Reg::Type::from(0u8));
43        ext_state.mark_vs_dirty();
44        ext_state.reset_vstart();
45
46        return Ok(());
47    };
48
49    let vlmax = ext_state.vlmax_for_vtype(new_vtype);
50
51    let rs1_is_zero = rs1 == Reg::ZERO;
52    let rd_is_zero = rd == Reg::ZERO;
53
54    let new_vl = if !rs1_is_zero {
55        // Truncate to `u32`: `VLMAX` fits in `u32` (max 65536)
56        let avl = rs1_value.as_u64() as u32;
57        ext_state.compute_vl(avl, vlmax)
58    } else if !rd_is_zero {
59        //` rs1=x0, rd!=x0`: `AVL = max`, `result` is `VLMAX`
60        vlmax
61    } else {
62        // `rs1=x0, rd=x0`: use current `vl` as `AVL`, keep `vl` unchanged if `VLMAX` stays the
63        // same. If `VLMAX` changes, this is reserved, and we set `vill` (conservative choice per
64        // spec).
65        let current_vl = ext_state.vl();
66        let old_vtype = ext_state.vtype();
67        let old_vlmax = old_vtype.map_or_default(|old_vtype| ext_state.vlmax_for_vtype(old_vtype));
68
69        if vlmax != old_vlmax {
70            ext_state.set_vtype(None);
71            ext_state.set_vl(0);
72            ext_state.mark_vs_dirty();
73            ext_state.reset_vstart();
74
75            return Ok(());
76        }
77
78        ext_state.compute_vl(current_vl, vlmax)
79    };
80
81    ext_state.set_vtype(Some(new_vtype));
82    ext_state.set_vl(new_vl);
83    regs.write(rd, Reg::Type::from(new_vl));
84    ext_state.mark_vs_dirty();
85    ext_state.reset_vstart();
86
87    Ok(())
88}
89
90/// Apply `vsetivli` logic.
91///
92/// `AVL` comes from 5-bit zero-extended immediate (0..31). No `rs1=x0/rd=x0` special casing
93/// applies to this variant.
94#[doc(hidden)]
95pub fn apply_vsetivli<Reg, Regs, ExtState, Memory, PC, CustomError>(
96    regs: &mut Regs,
97    ext_state: &mut ExtState,
98    program_counter: &PC,
99    rd: Reg,
100    uimm: u8,
101    vtypei: u16,
102) -> Result<(), ExecutionError<Reg::Type, CustomError>>
103where
104    Reg: Register,
105    Regs: RegisterFile<Reg>,
106    ExtState: VectorRegistersExt<Reg, CustomError>,
107    [(); ExtState::ELEN as usize]:,
108    [(); ExtState::VLEN as usize]:,
109    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
110    CustomError: fmt::Debug,
111{
112    // Check whether vector instructions are enabled
113    if !ext_state.vector_instructions_allowed() {
114        return Err(ExecutionError::IllegalInstruction {
115            address: program_counter.old_pc(INSTRUCTION_SIZE),
116        });
117    }
118
119    let vtype_raw = Reg::Type::from(vtypei);
120
121    if let Some(new_vtype) = Vtype::from_raw::<Reg>(vtype_raw) {
122        let vlmax = ext_state.vlmax_for_vtype(new_vtype);
123        let avl = u32::from(uimm);
124        let new_vl = ext_state.compute_vl(avl, vlmax);
125
126        ext_state.set_vtype(Some(new_vtype));
127        ext_state.set_vl(new_vl);
128        regs.write(rd, Reg::Type::from(new_vl));
129    } else {
130        ext_state.set_vtype(None);
131        ext_state.set_vl(0);
132        regs.write(rd, Reg::Type::from(0u8));
133    }
134    ext_state.mark_vs_dirty();
135    ext_state.reset_vstart();
136
137    Ok(())
138}