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