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_config::VectorConfig;
4use crate::v::vector_registers::VectorRegistersExt;
5use crate::v::zvexx::zvexx_helpers::INSTRUCTION_SIZE;
6use crate::{ExecutionError, PackedAddress, ProgramCounter};
7use ab_riscv_primitives::prelude::*;
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)]
18#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
19pub const fn apply_vsetvl<Reg, Env, Memory, PC>(
20    env: &mut Env,
21    program_counter: &PC,
22    rd: Reg,
23    rs1: Reg,
24    rs1_value: Reg::Type,
25    vtype_raw: Reg::Type,
26) -> Result<Reg::Type, ExecutionError<Reg::Type>>
27where
28    Reg: [const] Register,
29    Env: [const] VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
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_vector_config(None);
43        env.mark_vs_dirty();
44        env.reset_vstart();
45
46        return Ok(Reg::Type::from(0u8));
47    };
48
49    let rs1_is_zero = rs1 == Reg::ZERO;
50    let rd_is_zero = rd == Reg::ZERO;
51
52    let new_config = if !rs1_is_zero {
53        // AVL is an unsigned XLEN-wide value. Saturate rather than truncate, since anything above
54        // `u32::MAX` exceeds any `VLMAX` just as well.
55        let avl = u32::try_from(rs1_value.as_u64()).unwrap_or(u32::MAX);
56        VectorConfig::from_avl(new_vtype, Vl::new_saturating(avl))
57    } else if !rd_is_zero {
58        // `rs1=x0, rd!=x0`: `AVL = max`, `result` is `VLMAX`
59        VectorConfig::from_avl(new_vtype, new_vtype.vlmax())
60    } else if let Some(old_config) = env.vector_config()
61        && old_config.vlmax() == new_vtype.vlmax()
62    {
63        // `rs1=x0, rd=x0`: use current `vl` as `AVL`, which keeps `vl` unchanged since `VLMAX`
64        // stays the same
65        VectorConfig::from_avl(new_vtype, old_config.vl().get())
66    } else {
67        // `rs1=x0, rd=x0` with `VLMAX` changing (or `vill` set) is reserved, and we set `vill`
68        // (conservative choice per spec)
69        cold_path();
70        env.set_vector_config(None);
71        env.mark_vs_dirty();
72        env.reset_vstart();
73
74        return Ok(Reg::Type::from(0u8));
75    };
76
77    env.set_vector_config(Some(new_config));
78    env.mark_vs_dirty();
79    env.reset_vstart();
80
81    Ok(Reg::Type::from(u32::from(new_config.vl().get())))
82}
83
84/// Apply `vsetivli` logic.
85///
86/// `AVL` comes from 5-bit zero-extended immediate (0..31). No `rs1=x0/rd=x0` special casing
87/// applies to this variant.
88///
89/// Returns `rd_value`.
90#[inline(always)]
91#[doc(hidden)]
92#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
93pub const fn apply_vsetivli<Reg, Env, Memory, PC>(
94    env: &mut Env,
95    program_counter: &PC,
96    uimm: u8,
97    vtypei: u16,
98) -> Result<Reg::Type, ExecutionError<Reg::Type>>
99where
100    Reg: [const] Register,
101    Env: [const] VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
102    PC: [const] ProgramCounter<Reg::Type, Memory>,
103{
104    // Check whether vector instructions are enabled
105    if !env.vector_instructions_allowed() {
106        cold_path();
107        return Err(ExecutionError::IllegalInstruction {
108            address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
109        });
110    }
111
112    let vtype_raw = Reg::Type::from(vtypei);
113
114    let rd_value = if let Some(new_vtype) = Vtype::from_raw::<Reg>(vtype_raw) {
115        let new_config = VectorConfig::from_avl(new_vtype, Vl::from(uimm));
116        env.set_vector_config(Some(new_config));
117        Reg::Type::from(u32::from(new_config.vl().get()))
118    } else {
119        cold_path();
120        env.set_vector_config(None);
121        Reg::Type::from(0u8)
122    };
123    env.mark_vs_dirty();
124    env.reset_vstart();
125
126    Ok(rd_value)
127}