Skip to main content

ab_riscv_primitives/instructions/
zvbb.rs

1//! Zvbb extension
2
3#[cfg(test)]
4mod tests;
5pub mod zvkb;
6
7use crate::hart::{HartConfig, VectorHartConfig};
8use crate::instructions::v::zvexx::ZveXxInstruction;
9use crate::instructions::v::zvexx::arith::ZveXxArithInstruction;
10use crate::instructions::v::zvexx::carry::ZveXxCarryInstruction;
11use crate::instructions::v::zvexx::config::ZveXxConfigInstruction;
12use crate::instructions::v::zvexx::fixed_point::ZveXxFixedPointInstruction;
13use crate::instructions::v::zvexx::load::{LoadStoreNreg, Nf, SegVmNf, ZveXxLoadInstruction};
14use crate::instructions::v::zvexx::mask::ZveXxMaskInstruction;
15use crate::instructions::v::zvexx::muldiv::ZveXxMulDivInstruction;
16use crate::instructions::v::zvexx::perm::ZveXxPermInstruction;
17use crate::instructions::v::zvexx::reduction::ZveXxReductionInstruction;
18use crate::instructions::v::zvexx::store::ZveXxStoreInstruction;
19use crate::instructions::v::zvexx::widen_narrow::ZveXxWidenNarrowInstruction;
20use crate::instructions::v::{Eew, V};
21use crate::instructions::zicsr::ZicsrInstruction;
22use crate::instructions::zvbb::zvkb::ZvkbInstruction;
23use crate::instructions::{ImplementedExtension, Instruction, IsaExtension};
24use crate::registers::general_purpose::Register;
25use crate::registers::vector::VReg;
26use ab_riscv_macros::instruction;
27use core::fmt;
28
29/// RISC-V Zvbb vector bit-manipulation instruction.
30///
31/// Zvbb is a strict superset of Zvkb; this type encodes only the instructions unique to Zvbb.
32/// The Zvkb subset (vandn, vbrev8, vrev8, vrol, vror) is inherited from [`ZvkbInstruction`].
33///
34/// All use the OP-V major opcode (0b101_0111). Encoding spaces:
35///
36/// - `vbrev.v`:       funct6=0b010010, OPMVV, vs1=0b01010; `vm` controls masking
37/// - `vclz.v`:        funct6=0b010010, OPMVV, vs1=0b01100; `vm` controls masking
38/// - `vctz.v`:        funct6=0b010010, OPMVV, vs1=0b01101; `vm` controls masking
39/// - `vcpop.v`:       funct6=0b010010, OPMVV, vs1=0b01110; `vm` controls masking
40/// - `vwsll.[vv,vx]`: funct6=0b110101, OPIVV/OPIVX; `vm` controls masking
41/// - `vwsll.vi`:      funct6=0b110101, OPIVI; 5-bit unsigned immediate in bits\[19:15] (0-31);
42///   bit\[25] is the standard `vm` field, orthogonal to the immediate
43///
44/// The four unary operations share VXUNARY0 (funct6=0b010010, OPMVV); Zvkb claims vs1
45/// sub-opcodes 0b01000 and 0b01001; Zvbb claims 0b01010, 0b01100, 0b01101, and 0b01110.
46/// Sub-opcode 0b01011 is an undefined gap between vbrev and vclz.
47///
48/// Note: `vwsll` funct6 (0b110101) coincides with `vwadd.wv` from the base V extension;
49/// these instructions are mutually exclusive within a combined decoder.
50///
51/// For instructions with a `vm` field: `vm=true` means unmasked (process all body elements);
52/// `vm=false` means masked by v0 (skip elements where v0\[i]=0, leaving them undisturbed).
53#[instruction(
54    inherit = [ZvkbInstruction],
55)]
56#[derive(Debug, Clone, Copy)]
57#[derive_const(PartialEq, Eq)]
58#[rustfmt::skip]
59pub enum ZvbbInstruction<Hart>
60where
61    Hart: HartConfig,
62{
63    // vbrev: bit-reverse within each SEW-wide element (element granularity, unlike vbrev8's byte granularity)
64    /// `vbrev.v vd, vs2, vm`
65    VbrevV  { vd: VReg, vs2: VReg, vm: bool },
66    // vclz: count leading zeros within each SEW-wide element; result in [0, SEW]
67    /// `vclz.v vd, vs2, vm`
68    VclzV   { vd: VReg, vs2: VReg, vm: bool },
69    // vctz: count trailing zeros within each SEW-wide element; result in [0, SEW]
70    /// `vctz.v vd, vs2, vm`
71    VctzV   { vd: VReg, vs2: VReg, vm: bool },
72    // vcpop: population count (number of set bits) within each SEW-wide element
73    /// `vcpop.v vd, vs2, vm`
74    VcpopV  { vd: VReg, vs2: VReg, vm: bool },
75    // vwsll: widening shift left logical; result is 2*SEW wide, both sources are SEW wide
76    /// `vwsll.vv vd, vs2, vs1, vm`
77    VwsllVv { vd: VReg, vs2: VReg, vs1: VReg, vm: bool },
78    /// `vwsll.vx vd, vs2, rs1, vm`
79    VwsllVx { vd: VReg, vs2: VReg, rs1: Hart::Reg,  vm: bool },
80    /// `vwsll.vi vd, vs2, uimm, vm` - `uimm` is 5-bit unsigned (0..=31); `vm` is the
81    /// standard mask-control bit at bit\[25], orthogonal to the immediate.
82    VwsllVi { vd: VReg, vs2: VReg, uimm: u8,  vm: bool },
83}
84
85#[instruction]
86const impl<Reg, Hart> Instruction for ZvbbInstruction<Hart>
87where
88    Reg: [const] Register,
89    Hart: [const] VectorHartConfig<Reg = Reg>,
90{
91    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zvbb", 1, 0)];
92
93    const ALIGNMENT: u8 = align_of::<u32>() as u8;
94
95    type Hart = Hart;
96
97    #[inline(always)]
98    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
99    fn try_decode(instruction: u32) -> Option<Self> {
100        let opcode = (instruction & 0b111_1111) as u8;
101
102        if opcode != 0b101_0111 {
103            None?;
104        }
105
106        let vd_bits = ((instruction >> 7) & 0x1f) as u8;
107        let funct3 = ((instruction >> 12) & 0b111) as u8;
108        let vs1_bits = ((instruction >> 15) & 0x1f) as u8;
109        let vs2_bits = ((instruction >> 20) & 0x1f) as u8;
110        // vm=1 means unmasked, vm=0 means masked by v0
111        let vm = ((instruction >> 25) & 1) as u8 == 1;
112        let funct6 = ((instruction >> 26) & 0b11_1111) as u8;
113
114        let vd = VReg::from_bits(vd_bits)?;
115        if vd.is_mask(vm) {
116            None?;
117        }
118        let vs2 = VReg::from_bits(vs2_bits)?;
119        if vs2.is_mask(vm) {
120            None?;
121        }
122
123        match funct3 {
124            // OPIVV: vwsll.vv
125            0b000 => {
126                if funct6 != 0b11_0101 {
127                    None?;
128                }
129                let vs1 = VReg::from_bits(vs1_bits)?;
130                if vs1.is_mask(vm) {
131                    None?;
132                }
133                Some(Self::VwsllVv { vd, vs2, vs1, vm })
134            }
135            // OPIVX: vwsll.vx
136            0b100 => {
137                if funct6 != 0b11_0101 {
138                    None?;
139                }
140                let rs1 = Reg::from_bits(vs1_bits)?;
141                Some(Self::VwsllVx { vd, vs2, rs1, vm })
142            }
143            // OPIVI: vwsll.vi - standard 5-bit unsigned immediate in bits[19:15]; vm is bit[25]
144            0b011 => {
145                if funct6 != 0b11_0101 {
146                    None?;
147                }
148                let uimm = vs1_bits;
149                Some(Self::VwsllVi { vd, vs2, uimm, vm })
150            }
151            // OPMVV: vbrev.v, vclz.v, vctz.v, vcpop.v
152            // funct6=0b010010 (VXUNARY0 sub-space); Zvkb claims vs1 0b01000/0b01001;
153            // Zvbb claims 0b01010, 0b01100, 0b01101, 0b01110; vs1 0b01011 is an undefined gap
154            0b010 => {
155                if funct6 != 0b01_0010 {
156                    None?;
157                }
158                match vs1_bits {
159                    0b01010 => Some(Self::VbrevV { vd, vs2, vm }),
160                    0b01100 => Some(Self::VclzV { vd, vs2, vm }),
161                    0b01101 => Some(Self::VctzV { vd, vs2, vm }),
162                    0b01110 => Some(Self::VcpopV { vd, vs2, vm }),
163                    _ => None,
164                }
165            }
166            _ => None,
167        }
168    }
169
170    #[inline(always)]
171    fn size(&self) -> u8 {
172        size_of::<u32>() as u8
173    }
174}
175
176#[instruction]
177impl<Reg, Hart> fmt::Display for ZvbbInstruction<Hart>
178where
179    Reg: fmt::Display + Copy,
180    Hart: HartConfig<Reg = Reg>,
181{
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        #[rustfmt::skip]
184        match self {
185            Self::VbrevV  { vd, vs2, vm }        => write!(f, "vbrev.v {vd}, {vs2}{}", mask_suffix(vm)),
186            Self::VclzV   { vd, vs2, vm }        => write!(f, "vclz.v {vd}, {vs2}{}", mask_suffix(vm)),
187            Self::VctzV   { vd, vs2, vm }        => write!(f, "vctz.v {vd}, {vs2}{}", mask_suffix(vm)),
188            Self::VcpopV  { vd, vs2, vm }        => write!(f, "vcpop.v {vd}, {vs2}{}", mask_suffix(vm)),
189            Self::VwsllVv { vd, vs2, vs1, vm }   => write!(f, "vwsll.vv {vd}, {vs2}, {vs1}{}", mask_suffix(vm)),
190            Self::VwsllVx { vd, vs2, rs1, vm }   => write!(f, "vwsll.vx {vd}, {vs2}, {rs1}{}", mask_suffix(vm)),
191            Self::VwsllVi { vd, vs2, uimm, vm }  => write!(f, "vwsll.vi {vd}, {vs2}, {uimm}{}", mask_suffix(vm)),
192        }
193    }
194}
195
196/// Format mask suffix for display
197#[inline(always)]
198fn mask_suffix(vm: &bool) -> &'static str {
199    if *vm { "" } else { ", v0.t" }
200}