Skip to main content

ab_riscv_interpreter/v/zvexx/
config.rs

1//! ZveXx configuration instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zvexx_config_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::{
9    CsrError, Csrs, ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands,
10    ExecutionError, ExecutionResult, FetchInstructionResult, InstructionFetcher,
11    OpaqueThreadedExecutionResult, ProgramCounter, RegisterFile, Rs1Rs2OperandValues,
12    Rs1Rs2Operands, ThreadedExecutableInstruction, ThreadedExecutionResult,
13};
14use ab_riscv_macros::instruction_execution;
15use ab_riscv_primitives::prelude::*;
16
17#[instruction_execution]
18const impl<Reg, Hart> ExecutableInstructionOperands for ZveXxConfigInstruction<Hart>
19where
20    Reg: Register,
21    Hart: VectorHartConfig<Reg = Reg>,
22{
23}
24
25#[instruction_execution]
26const impl<Reg, Hart, Env> ExecutableInstructionCsr<Env> for ZveXxConfigInstruction<Hart>
27where
28    Reg: [const] Register,
29    Hart: VectorHartConfig<Reg = Reg>,
30    Env: [const] Csrs<Reg>,
31{
32    /// Validate reads to vector CSRs from Zicsr instructions.
33    ///
34    /// All vector CSRs are accessible from unprivileged code (U-mode).
35    /// Reads are pass-through: the raw value stored in the CSR is the output value.
36    #[inline(always)]
37    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
38    fn prepare_csr_read(
39        _env: &Env,
40        csr_index: u16,
41        _will_write: bool,
42        raw_value: Reg::Type,
43        output_value: &mut Reg::Type,
44    ) -> Result<bool, CsrError> {
45        if VectorCsr::from_csr_index(csr_index).is_some() {
46            *output_value = raw_value;
47            Ok(true)
48        } else {
49            // Not a vector CSR
50            Ok(false)
51        }
52    }
53
54    /// Validate, sanitize, and mirror writes to vector CSRs from Zicsr instructions.
55    ///
56    /// Enforces WARL semantics and vcsr mirroring:
57    /// - `vl`, `vtype`, `vlenb` are read-only: writes are rejected
58    /// - `vxsat`: only bit 0 is writable; mirrors into `vcsr[0]`
59    /// - `vxrm`: only bits `[1:0]` are writable; mirrors into `vcsr[2:1]`
60    /// - `vcsr`: only bits `[2:0]` are writable; mirrors into `vxsat` and `vxrm`
61    /// - `vstart`: full XLEN write allowed (WARL, implementation may restrict range)
62    #[inline(always)]
63    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
64    fn prepare_csr_write(
65        env: &mut Env,
66        csr_index: u16,
67        write_value: Reg::Type,
68        output_value: &mut Reg::Type,
69    ) -> Result<bool, CsrError> {
70        if let Some(vcsr) = VectorCsr::from_csr_index(csr_index) {
71            // WARL: mask to valid bits, zero upper bits
72            *output_value = match vcsr {
73                VectorCsr::Vstart => {
74                    // WARL: allow full XLEN write, but clamp to implementation-supported range
75                    let max = Reg::Type::from(u16::MAX);
76                    write_value.min(max)
77                }
78                VectorCsr::Vxsat => {
79                    let masked = write_value & Reg::Type::from(1u8);
80                    // Mirror `vxsat` into `vcsr[0]`, preserving `vcsr[2:1]` (`vxrm`)
81                    let old_vcsr = env.read_csr(VectorCsr::Vcsr.to_csr_index())?;
82                    let new_vcsr = (old_vcsr & !Reg::Type::from(1u8)) | masked;
83                    env.write_csr(VectorCsr::Vcsr.to_csr_index(), new_vcsr)?;
84                    masked
85                }
86                VectorCsr::Vxrm => {
87                    let masked = write_value & Reg::Type::from(0b11u8);
88                    // Mirror `vxrm` into `vcsr[2:1]`, preserving `vcsr[0]` (`vxsat`)
89                    let old_vcsr = env.read_csr(VectorCsr::Vcsr.to_csr_index())?;
90                    let new_vcsr = (old_vcsr & !Reg::Type::from(0b110u8)) | (masked << 1u8);
91                    env.write_csr(VectorCsr::Vcsr.to_csr_index(), new_vcsr)?;
92                    masked
93                }
94                VectorCsr::Vcsr => {
95                    // Mirror `vcsr[0]` -> `vxsat`
96                    let new_vxsat = write_value & Reg::Type::from(1u8);
97                    env.write_csr(VectorCsr::Vxsat.to_csr_index(), new_vxsat)?;
98
99                    // Mirror `vcsr[2:1]` -> `vxrm`
100                    let new_vxrm = (write_value >> 1u8) & Reg::Type::from(0b11u8);
101                    env.write_csr(VectorCsr::Vxrm.to_csr_index(), new_vxrm)?;
102
103                    write_value & Reg::Type::from(0b111u8)
104                }
105                VectorCsr::Vl | VectorCsr::Vtype | VectorCsr::Vlenb => {
106                    // Read-only CSRs (from Zicsr perspective)
107                    Err(CsrError::ReadOnly { csr_index })?
108                }
109            };
110            Ok(true)
111        } else {
112            Ok(false)
113        }
114    }
115}
116
117#[instruction_execution]
118const impl<Reg, Hart, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
119    for ZveXxConfigInstruction<Hart>
120where
121    Reg: [const] Register,
122    Hart: VectorHartConfig<Reg = Reg>,
123    Regs: [const] RegisterFile<Reg>,
124    Env: [const] VectorRegistersExt<Hart = Hart>,
125    PC: [const] ProgramCounter<Reg::Type, Memory>,
126{
127    #[inline(always)]
128    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
129    fn execute(
130        self,
131        Rs1Rs2OperandValues {
132            rs1_value,
133            rs2_value,
134        }: Rs1Rs2OperandValues<Reg::Type>,
135        _regs: &mut Regs,
136        env: &mut Env,
137        _memory: &mut Memory,
138        program_counter: &PC,
139    ) -> ExecutionResult<Reg> {
140        match self {
141            Self::Vsetvli { rd, rs1, vtypei } => {
142                let rd_value = zvexx_config_helpers::apply_vsetvl(
143                    env,
144                    program_counter,
145                    rd,
146                    rs1,
147                    rs1_value,
148                    Reg::Type::from(vtypei),
149                )?;
150
151                ExecutionResult::Continue {
152                    rd,
153                    value: rd_value,
154                }
155            }
156            Self::Vsetivli { rd, uimm, vtypei } => {
157                let rd_value =
158                    zvexx_config_helpers::apply_vsetivli(env, program_counter, uimm, vtypei)?;
159
160                ExecutionResult::Continue {
161                    rd,
162                    value: rd_value,
163                }
164            }
165            Self::Vsetvl { rd, rs1, rs2: _ } => {
166                let vtype_raw = rs2_value;
167                let rd_value = zvexx_config_helpers::apply_vsetvl(
168                    env,
169                    program_counter,
170                    rd,
171                    rs1,
172                    rs1_value,
173                    vtype_raw,
174                )?;
175
176                ExecutionResult::Continue {
177                    rd,
178                    value: rd_value,
179                }
180            }
181        }
182    }
183}