Skip to main content

ab_riscv_primitives/instructions/zvbb/
zvkb.rs

1//! Zvkb extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::hart::{HartConfig, VectorHartConfig};
7use crate::instructions::v::zvexx::ZveXxInstruction;
8use crate::instructions::v::zvexx::arith::ZveXxArithInstruction;
9use crate::instructions::v::zvexx::carry::ZveXxCarryInstruction;
10use crate::instructions::v::zvexx::config::ZveXxConfigInstruction;
11use crate::instructions::v::zvexx::fixed_point::ZveXxFixedPointInstruction;
12use crate::instructions::v::zvexx::load::{LoadStoreNreg, Nf, SegVmNf, ZveXxLoadInstruction};
13use crate::instructions::v::zvexx::mask::ZveXxMaskInstruction;
14use crate::instructions::v::zvexx::muldiv::ZveXxMulDivInstruction;
15use crate::instructions::v::zvexx::perm::ZveXxPermInstruction;
16use crate::instructions::v::zvexx::reduction::ZveXxReductionInstruction;
17use crate::instructions::v::zvexx::store::ZveXxStoreInstruction;
18use crate::instructions::v::zvexx::widen_narrow::ZveXxWidenNarrowInstruction;
19use crate::instructions::v::{Eew, V};
20use crate::instructions::zicsr::ZicsrInstruction;
21use crate::instructions::{ImplementedExtension, Instruction, IsaExtension};
22use crate::registers::general_purpose::Register;
23use crate::registers::vector::VReg;
24use ab_riscv_macros::instruction;
25use core::fmt;
26
27/// RISC-V Zvkb vector cryptography bit-manipulation instruction.
28///
29/// All use the OP-V major opcode (0b101_0111). Encoding spaces:
30///
31/// - `vandn.[vv,vx]`: funct6=0b000001, OPIVV/OPIVX; `vm` controls masking (0=masked, 1=unmasked)
32/// - `vbrev8.v`:      funct6=0b010010, OPMVV, vs1=0b01000; `vm` controls masking
33/// - `vrev8.v`:       funct6=0b010010, OPMVV, vs1=0b01001; `vm` controls masking
34/// - `vrol.[vv,vx]`:  funct6=0b010101, OPIVV/OPIVX; `vm` controls masking
35/// - `vror.[vv,vx]`:  funct6=0b010100, OPIVV/OPIVX; `vm` controls masking
36/// - `vror.vi`:       funct6=0b01010_u, OPIVI, where `u` (bit\[26], the low bit of funct6) is
37///   immediate bit\[5]; combined with the 5-bit field in bits\[19:15] (immediate bits\[4:0]) this
38///   forms a 6-bit unsigned immediate (0-63), wide enough for SEW=64 rotate amounts. `vm` at
39///   bit\[25] is unaffected and controls masking as usual.
40///
41/// For instructions with a `vm` field: `vm=true` means unmasked (process all body elements);
42/// `vm=false` means masked by v0 (skip elements where v0\[i]=0, leaving them undisturbed).
43#[instruction(
44    inherit = [ZveXxInstruction],
45)]
46#[derive(Debug, Clone, Copy)]
47#[derive_const(PartialEq, Eq)]
48#[rustfmt::skip]
49pub enum ZvkbInstruction<Hart>
50where
51    Hart: HartConfig,
52{
53    // vandn: vd[i] = ~vs1[i] & vs2[i]  (or ~rs1 & vs2[i])
54    // Essential for the Chi step of the Keccak permutation (SHA-3).
55
56    /// `vandn.vv vd, vs2, vs1, vm`
57    VandnVv { vd: VReg, vs2: VReg, vs1: VReg, vm: bool },
58    /// `vandn.vx vd, vs2, rs1, vm`
59    VandnVx { vd: VReg, vs2: VReg, rs1: Hart::Reg, vm: bool },
60
61    // vbrev8: reverse bits within each byte of each SEW-wide element
62
63    /// `vbrev8.v vd, vs2, vm`
64    Vbrev8V { vd: VReg, vs2: VReg, vm: bool },
65
66    // vrev8: reverse bytes within each SEW-wide element
67
68    /// `vrev8.v vd, vs2, vm`
69    Vrev8V  { vd: VReg, vs2: VReg, vm: bool },
70
71    // vrol: rotate left; no immediate form (use vror.vi with negated immediate instead)
72
73    /// `vrol.vv vd, vs2, vs1, vm`
74    VrolVv  { vd: VReg, vs2: VReg, vs1: VReg, vm: bool },
75    /// `vrol.vx vd, vs2, rs1, vm`
76    VrolVx  { vd: VReg, vs2: VReg, rs1: Hart::Reg, vm: bool },
77
78    // vror: rotate right
79    // vror.vi has a 6-bit unsigned immediate: bits[4:0] from vs1[19:15], bit[5] from funct6's low
80    // bit (bit[26]). `vm` at bit[25] is the standard mask-control bit, independent of the
81    // immediate.
82
83    /// `vror.vv vd, vs2, vs1, vm`
84    VrorVv  { vd: VReg, vs2: VReg, vs1: VReg, vm: bool },
85    /// `vror.vx vd, vs2, rs1, vm`
86    VrorVx  { vd: VReg, vs2: VReg, rs1: Hart::Reg, vm: bool },
87    /// `vror.vi vd, vs2, uimm, vm` - `uimm` is 6-bit unsigned (0..=63); `vm` is the
88    /// standard mask-control bit at bit\[25], orthogonal to the immediate.
89    VrorVi  { vd: VReg, vs2: VReg, uimm: u8, vm: bool },
90}
91
92#[instruction]
93const impl<Reg, Hart> Instruction for ZvkbInstruction<Hart>
94where
95    Reg: [const] Register,
96    Hart: [const] VectorHartConfig<Reg = Reg>,
97{
98    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zvkb", 1, 0)];
99
100    const ALIGNMENT: u8 = align_of::<u32>() as u8;
101
102    type Hart = Hart;
103
104    #[inline(always)]
105    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
106    fn try_decode(instruction: u32) -> Option<Self> {
107        let opcode = (instruction & 0b111_1111) as u8;
108
109        if opcode != 0b101_0111 {
110            None?;
111        }
112
113        let vd_bits = ((instruction >> 7) & 0x1f) as u8;
114        let funct3 = ((instruction >> 12) & 0b111) as u8;
115        let vs1_bits = ((instruction >> 15) & 0x1f) as u8;
116        let vs2_bits = ((instruction >> 20) & 0x1f) as u8;
117        // vm=1 means unmasked, vm=0 means masked by v0
118        let vm = ((instruction >> 25) & 1) as u8 == 1;
119        let funct6 = ((instruction >> 26) & 0b11_1111) as u8;
120
121        let vd = VReg::from_bits(vd_bits)?;
122        if vd.is_mask(vm) {
123            None?;
124        }
125        let vs2 = VReg::from_bits(vs2_bits)?;
126        if vs2.is_mask(vm) {
127            None?;
128        }
129
130        match funct3 {
131            // OPIVV: vandn.vv, vrol.vv, vror.vv
132            0b000 => {
133                let vs1 = VReg::from_bits(vs1_bits)?;
134                if vs1.is_mask(vm) {
135                    None?;
136                }
137                match funct6 {
138                    0b00_0001 => Some(Self::VandnVv { vd, vs2, vs1, vm }),
139                    0b01_0100 => Some(Self::VrorVv { vd, vs2, vs1, vm }),
140                    0b01_0101 => Some(Self::VrolVv { vd, vs2, vs1, vm }),
141                    _ => None,
142                }
143            }
144            // OPIVX: vandn.vx, vrol.vx, vror.vx
145            0b100 => {
146                let rs1 = Reg::from_bits(vs1_bits)?;
147                match funct6 {
148                    0b00_0001 => Some(Self::VandnVx { vd, vs2, rs1, vm }),
149                    0b01_0100 => Some(Self::VrorVx { vd, vs2, rs1, vm }),
150                    0b01_0101 => Some(Self::VrolVx { vd, vs2, rs1, vm }),
151                    _ => None,
152                }
153            }
154            // OPIVI: vror.vi only - 6-bit unsigned immediate: bits[4:0] from vs1[19:15], bit[5]
155            // from funct6's low bit (bit[26]); `vm` at bit[25] is unaffected
156            0b011 => {
157                if funct6 >> 1 != 0b0_1010 {
158                    None?;
159                }
160                let uimm = vs1_bits | ((funct6 & 1) << 5);
161                Some(Self::VrorVi { vd, vs2, uimm, vm })
162            }
163            // OPMVV: vbrev8.v and vrev8.v - unary, vs1 encodes the sub-operation
164            // funct6=0b010010 (VXUNARY0 sub-space); sub-opcodes 0b01000/0b01001 belong
165            // to Zvkb; other sub-opcodes in this space (vzext, vsext) are not claimed here
166            0b010 => {
167                if funct6 != 0b01_0010 {
168                    None?;
169                }
170                match vs1_bits {
171                    0b01000 => Some(Self::Vbrev8V { vd, vs2, vm }),
172                    0b01001 => Some(Self::Vrev8V { vd, vs2, vm }),
173                    _ => None,
174                }
175            }
176            _ => None,
177        }
178    }
179
180    #[inline(always)]
181    fn size(&self) -> u8 {
182        size_of::<u32>() as u8
183    }
184}
185
186#[instruction]
187impl<Reg, Hart> fmt::Display for ZvkbInstruction<Hart>
188where
189    Reg: fmt::Display + Copy,
190    Hart: HartConfig<Reg = Reg>,
191{
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        #[rustfmt::skip]
194        match self {
195            Self::VandnVv { vd, vs2, vs1, vm }  => write!(f, "vandn.vv {vd}, {vs2}, {vs1}{}", mask_suffix(vm)),
196            Self::VandnVx { vd, vs2, rs1, vm }  => write!(f, "vandn.vx {vd}, {vs2}, {rs1}{}", mask_suffix(vm)),
197            Self::Vbrev8V { vd, vs2, vm }       => write!(f, "vbrev8.v {vd}, {vs2}{}", mask_suffix(vm)),
198            Self::Vrev8V  { vd, vs2, vm }       => write!(f, "vrev8.v {vd}, {vs2}{}", mask_suffix(vm)),
199            Self::VrolVv  { vd, vs2, vs1, vm }  => write!(f, "vrol.vv {vd}, {vs2}, {vs1}{}", mask_suffix(vm)),
200            Self::VrolVx  { vd, vs2, rs1, vm }  => write!(f, "vrol.vx {vd}, {vs2}, {rs1}{}", mask_suffix(vm)),
201            Self::VrorVv  { vd, vs2, vs1, vm }  => write!(f, "vror.vv {vd}, {vs2}, {vs1}{}", mask_suffix(vm)),
202            Self::VrorVx  { vd, vs2, rs1, vm }  => write!(f, "vror.vx {vd}, {vs2}, {rs1}{}", mask_suffix(vm)),
203            Self::VrorVi  { vd, vs2, uimm, vm } => write!(f, "vror.vi {vd}, {vs2}, {uimm}{}", mask_suffix(vm)),
204        }
205    }
206}
207
208/// Format mask suffix for display
209#[inline(always)]
210fn mask_suffix(vm: &bool) -> &'static str {
211    if *vm { "" } else { ", v0.t" }
212}