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