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