Skip to main content

ab_riscv_primitives/instructions/rv32/b/
zbb.rs

1//! RV32 Zbb extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::hart::HartConfig;
7use crate::instructions::utils::Shamt;
8use crate::instructions::{ImplementedExtension, Instruction, IsaExtension};
9use crate::registers::general_purpose::Register;
10use ab_riscv_macros::instruction;
11use core::fmt;
12
13/// RISC-V RV32 instructions shared between Zbb and Zbkb extensions.
14///
15/// Not an extension on its own, only exists such that both Zbb and Zbkb can inherit it.
16#[instruction]
17#[derive(Debug, Clone, Copy)]
18#[derive_const(PartialEq, Eq)]
19#[rustfmt::skip]
20pub enum Rv32ZbbZbkbSharedInstruction<Hart>
21where
22    Hart: HartConfig,
23{
24    Andn { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
25    Orn { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
26    Xnor { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
27    Rol { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
28    Ror { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
29    Rori { rd: Hart::Reg, rs1: Hart::Reg, shamt: Shamt<u32> },
30    Rev8 { rd: Hart::Reg, rs1: Hart::Reg },
31}
32
33#[instruction]
34const impl<Reg, Hart> Instruction for Rv32ZbbZbkbSharedInstruction<Hart>
35where
36    Reg: [const] Register<Type = u32>,
37    Hart: [const] HartConfig<Reg = Reg>,
38{
39    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[];
40
41    const ALIGNMENT: u8 = align_of::<u32>() as u8;
42
43    type Hart = Hart;
44
45    #[inline(always)]
46    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
47    fn try_decode(instruction: u32) -> Option<Self> {
48        let opcode = (instruction & 0b111_1111) as u8;
49        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
50        let funct3 = ((instruction >> 12) & 0b111) as u8;
51        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
52        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
53        let funct7 = ((instruction >> 25) & 0b111_1111) as u8;
54        // For I-type: bits [25:20]
55        let low5 = ((instruction >> 20) & 0x1f) as u8;
56        let funct12 = ((instruction >> 20) & 0xfff) as u16;
57
58        match opcode {
59            // OP-IMM
60            0b001_0011 => {
61                let rd = Reg::from_bits(rd_bits)?;
62                let rs1 = Reg::from_bits(rs1_bits)?;
63                match funct3 {
64                    0b101 => {
65                        // rev8 for RV32: funct12 = 0b0110_1001_1000
66                        // rori: funct7 = 0b0110000, shamt in bits [24:20] (RV32 has no shamt[5] -
67                        // unlike RV64, all 7 funct7 bits are the opcode selector, not just 6)
68                        if funct12 == 0b0110_1001_1000 {
69                            Some(Self::Rev8 { rd, rs1 })
70                        } else if funct7 == 0b011_0000 {
71                            Some(Self::Rori {
72                                rd,
73                                rs1,
74                                shamt: Shamt::<u32>::new(low5)?,
75                            })
76                        } else {
77                            None
78                        }
79                    }
80                    _ => None,
81                }
82            }
83            // OP / R-type
84            0b011_0011 => {
85                let rd = Reg::from_bits(rd_bits)?;
86                let rs1 = Reg::from_bits(rs1_bits)?;
87                let rs2 = Reg::from_bits(rs2_bits)?;
88                match (funct3, funct7) {
89                    (0b001, 0b011_0000) => Some(Self::Rol { rd, rs1, rs2 }),
90                    (0b100, 0b010_0000) => Some(Self::Xnor { rd, rs1, rs2 }),
91                    (0b101, 0b011_0000) => Some(Self::Ror { rd, rs1, rs2 }),
92                    (0b110, 0b010_0000) => Some(Self::Orn { rd, rs1, rs2 }),
93                    (0b111, 0b010_0000) => Some(Self::Andn { rd, rs1, rs2 }),
94                    _ => None,
95                }
96            }
97            _ => None,
98        }
99    }
100
101    #[inline(always)]
102    fn size(&self) -> u8 {
103        size_of::<u32>() as u8
104    }
105}
106
107#[instruction]
108impl<Reg, Hart> fmt::Display for Rv32ZbbZbkbSharedInstruction<Hart>
109where
110    Reg: fmt::Display,
111    Hart: HartConfig<Reg = Reg>,
112{
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        match self {
115            Self::Andn { rd, rs1, rs2 } => write!(f, "andn {rd}, {rs1}, {rs2}"),
116            Self::Orn { rd, rs1, rs2 } => write!(f, "orn {rd}, {rs1}, {rs2}"),
117            Self::Xnor { rd, rs1, rs2 } => write!(f, "xnor {rd}, {rs1}, {rs2}"),
118            Self::Rol { rd, rs1, rs2 } => write!(f, "rol {rd}, {rs1}, {rs2}"),
119            Self::Ror { rd, rs1, rs2 } => write!(f, "ror {rd}, {rs1}, {rs2}"),
120            Self::Rori { rd, rs1, shamt } => write!(f, "rori {rd}, {rs1}, {shamt}"),
121            Self::Rev8 { rd, rs1 } => write!(f, "rev8 {rd}, {rs1}"),
122        }
123    }
124}
125
126/// RISC-V RV32 Zbb instruction (Basic bit manipulation)
127#[instruction(
128    reorder = [
129        Andn, Orn, Xnor, Clz, Ctz, Cpop, Max, Maxu, Min, Minu, Sextb, Sexth, Zexth, Rol, Ror, Rori,
130        Orcb, Rev8,
131    ],
132    inherit = [Rv32ZbbZbkbSharedInstruction],
133)]
134#[derive(Debug, Clone, Copy)]
135#[derive_const(PartialEq, Eq)]
136#[rustfmt::skip]
137pub enum Rv32ZbbInstruction<Hart>
138where
139    Hart: HartConfig,
140{
141    Clz { rd: Hart::Reg, rs1: Hart::Reg },
142    Ctz { rd: Hart::Reg, rs1: Hart::Reg },
143    Cpop { rd: Hart::Reg, rs1: Hart::Reg },
144    Max { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
145    Maxu { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
146    Min { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
147    Minu { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
148    Sextb { rd: Hart::Reg, rs1: Hart::Reg },
149    Sexth { rd: Hart::Reg, rs1: Hart::Reg },
150    Zexth { rd: Hart::Reg, rs1: Hart::Reg },
151    Orcb { rd: Hart::Reg, rs1: Hart::Reg },
152}
153
154#[instruction]
155const impl<Reg, Hart> Instruction for Rv32ZbbInstruction<Hart>
156where
157    Reg: [const] Register<Type = u32>,
158    Hart: [const] HartConfig<Reg = Reg>,
159{
160    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zbb", 1, 0)];
161
162    const ALIGNMENT: u8 = align_of::<u32>() as u8;
163
164    type Hart = Hart;
165
166    #[inline(always)]
167    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
168    fn try_decode(instruction: u32) -> Option<Self> {
169        let opcode = (instruction & 0b111_1111) as u8;
170        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
171        let funct3 = ((instruction >> 12) & 0b111) as u8;
172        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
173        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
174        let funct7 = ((instruction >> 25) & 0b111_1111) as u8;
175        let funct12 = ((instruction >> 20) & 0xfff) as u16;
176
177        match opcode {
178            // OP-IMM
179            0b001_0011 => {
180                let rd = Reg::from_bits(rd_bits)?;
181                let rs1 = Reg::from_bits(rs1_bits)?;
182                match funct3 {
183                    0b001 => {
184                        // RV32: shamt is 5 bits; funct7 distinguishes the operation
185                        if funct7 == 0b011_0000 {
186                            match rs2_bits {
187                                0 => Some(Self::Clz { rd, rs1 }),
188                                1 => Some(Self::Ctz { rd, rs1 }),
189                                2 => Some(Self::Cpop { rd, rs1 }),
190                                4 => Some(Self::Sextb { rd, rs1 }),
191                                5 => Some(Self::Sexth { rd, rs1 }),
192                                _ => None,
193                            }
194                        } else {
195                            None
196                        }
197                    }
198                    0b101 => {
199                        // orc.b: funct12 = 0b0010_1000_0111
200                        if funct12 == 0b0010_1000_0111 {
201                            Some(Self::Orcb { rd, rs1 })
202                        } else {
203                            None
204                        }
205                    }
206                    _ => None,
207                }
208            }
209            // OP / R-type
210            0b011_0011 => {
211                let rd = Reg::from_bits(rd_bits)?;
212                let rs1 = Reg::from_bits(rs1_bits)?;
213                let rs2 = Reg::from_bits(rs2_bits)?;
214                match (funct3, funct7) {
215                    (0b100, 0b000_0101) => Some(Self::Min { rd, rs1, rs2 }),
216                    // zext.h for RV32 uses OP (0b011_0011), funct7=0b000_0100, rs2=0
217                    (0b100, 0b000_0100) if rs2_bits == 0 => Some(Self::Zexth { rd, rs1 }),
218                    (0b101, 0b000_0101) => Some(Self::Minu { rd, rs1, rs2 }),
219                    (0b110, 0b000_0101) => Some(Self::Max { rd, rs1, rs2 }),
220                    (0b111, 0b000_0101) => Some(Self::Maxu { rd, rs1, rs2 }),
221                    _ => None,
222                }
223            }
224            _ => None,
225        }
226    }
227
228    #[inline(always)]
229    fn size(&self) -> u8 {
230        size_of::<u32>() as u8
231    }
232}
233
234#[instruction]
235impl<Reg, Hart> fmt::Display for Rv32ZbbInstruction<Hart>
236where
237    Reg: fmt::Display + Copy,
238    Hart: HartConfig<Reg = Reg>,
239{
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        match self {
242            Self::Clz { rd, rs1 } => write!(f, "clz {rd}, {rs1}"),
243            Self::Ctz { rd, rs1 } => write!(f, "ctz {rd}, {rs1}"),
244            Self::Cpop { rd, rs1 } => write!(f, "cpop {rd}, {rs1}"),
245            Self::Max { rd, rs1, rs2 } => write!(f, "max {rd}, {rs1}, {rs2}"),
246            Self::Maxu { rd, rs1, rs2 } => write!(f, "maxu {rd}, {rs1}, {rs2}"),
247            Self::Min { rd, rs1, rs2 } => write!(f, "min {rd}, {rs1}, {rs2}"),
248            Self::Minu { rd, rs1, rs2 } => write!(f, "minu {rd}, {rs1}, {rs2}"),
249            Self::Sextb { rd, rs1 } => write!(f, "sext.b {rd}, {rs1}"),
250            Self::Sexth { rd, rs1 } => write!(f, "sext.h {rd}, {rs1}"),
251            Self::Zexth { rd, rs1 } => write!(f, "zext.h {rd}, {rs1}"),
252            Self::Orcb { rd, rs1 } => write!(f, "orc.b {rd}, {rs1}"),
253        }
254    }
255}