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