Skip to main content

ab_riscv_primitives/instructions/rv64/b/
zbb.rs

1//! RV64 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 RV64 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 Rv64ZbbZbkbSharedInstruction<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    Rolw { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
29    Ror { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
30    Rori { rd: Hart::Reg, rs1: Hart::Reg, shamt: Shamt<u64> },
31    Roriw { rd: Hart::Reg, rs1: Hart::Reg, shamt: Shamt<u32> },
32    Rorw { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
33    Rev8 { rd: Hart::Reg, rs1: Hart::Reg },
34}
35
36#[instruction]
37const impl<Reg, Hart> Instruction for Rv64ZbbZbkbSharedInstruction<Hart>
38where
39    Reg: [const] Register<Type = u64>,
40    Hart: [const] HartConfig<Reg = Reg>,
41{
42    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[];
43
44    const ALIGNMENT: u8 = align_of::<u32>() as u8;
45
46    type Hart = Hart;
47
48    #[inline(always)]
49    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
50    fn try_decode(instruction: u32) -> Option<Self> {
51        let opcode = (instruction & 0b111_1111) as u8;
52        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
53        let funct3 = ((instruction >> 12) & 0b111) as u8;
54        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
55        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
56        let funct7 = ((instruction >> 25) & 0b111_1111) as u8;
57        let funct6 = ((instruction >> 26) & 0b11_1111) as u8;
58        // bits 25:20 for I-type distinctions
59        let low6 = ((instruction >> 20) & 0x3f) as u8;
60        let funct12 = ((instruction >> 20) & 0xfff) as u16;
61
62        match opcode {
63            // OP-IMM
64            0b001_0011 => {
65                let rd = Reg::from_bits(rd_bits)?;
66                let rs1 = Reg::from_bits(rs1_bits)?;
67                match funct3 {
68                    0b101 => {
69                        if funct12 == 0b0110_1011_1000 {
70                            Some(Self::Rev8 { rd, rs1 })
71                        } else if funct6 == 0b01_1000 {
72                            Some(Self::Rori {
73                                rd,
74                                rs1,
75                                shamt: Shamt::<u64>::new(low6)?,
76                            })
77                        } else {
78                            None
79                        }
80                    }
81                    _ => None,
82                }
83            }
84            // OP / R-type
85            0b011_0011 => {
86                let rd = Reg::from_bits(rd_bits)?;
87                let rs1 = Reg::from_bits(rs1_bits)?;
88                let rs2 = Reg::from_bits(rs2_bits)?;
89                match (funct3, funct7) {
90                    (0b001, 0b011_0000) => Some(Self::Rol { rd, rs1, rs2 }),
91                    (0b100, 0b010_0000) => Some(Self::Xnor { rd, rs1, rs2 }),
92                    (0b101, 0b011_0000) => Some(Self::Ror { rd, rs1, rs2 }),
93                    (0b110, 0b010_0000) => Some(Self::Orn { rd, rs1, rs2 }),
94                    (0b111, 0b010_0000) => Some(Self::Andn { rd, rs1, rs2 }),
95                    _ => None,
96                }
97            }
98            // OP-IMM-32
99            0b001_1011 => {
100                let rd = Reg::from_bits(rd_bits)?;
101                let rs1 = Reg::from_bits(rs1_bits)?;
102                match (funct3, funct7) {
103                    (0b101, 0b011_0000) => {
104                        let shamt = Shamt::<u32>::new(rs2_bits)?;
105                        Some(Self::Roriw { rd, rs1, shamt })
106                    }
107                    _ => None,
108                }
109            }
110            // OP-32
111            0b011_1011 => {
112                let rd = Reg::from_bits(rd_bits)?;
113                let rs1 = Reg::from_bits(rs1_bits)?;
114                let rs2 = Reg::from_bits(rs2_bits)?;
115                match (funct3, funct7) {
116                    (0b001, 0b011_0000) => Some(Self::Rolw { rd, rs1, rs2 }),
117                    (0b101, 0b011_0000) => Some(Self::Rorw { rd, rs1, rs2 }),
118                    _ => None,
119                }
120            }
121            _ => None,
122        }
123    }
124
125    #[inline(always)]
126    fn size(&self) -> u8 {
127        size_of::<u32>() as u8
128    }
129}
130
131#[instruction]
132impl<Reg, Hart> fmt::Display for Rv64ZbbZbkbSharedInstruction<Hart>
133where
134    Reg: fmt::Display,
135    Hart: HartConfig<Reg = Reg>,
136{
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        match self {
139            Self::Andn { rd, rs1, rs2 } => write!(f, "andn {rd}, {rs1}, {rs2}"),
140            Self::Orn { rd, rs1, rs2 } => write!(f, "orn {rd}, {rs1}, {rs2}"),
141            Self::Xnor { rd, rs1, rs2 } => write!(f, "xnor {rd}, {rs1}, {rs2}"),
142            Self::Rol { rd, rs1, rs2 } => write!(f, "rol {rd}, {rs1}, {rs2}"),
143            Self::Rolw { rd, rs1, rs2 } => write!(f, "rolw {rd}, {rs1}, {rs2}"),
144            Self::Ror { rd, rs1, rs2 } => write!(f, "ror {rd}, {rs1}, {rs2}"),
145            Self::Rori { rd, rs1, shamt } => write!(f, "rori {rd}, {rs1}, {shamt}"),
146            Self::Roriw { rd, rs1, shamt } => write!(f, "roriw {rd}, {rs1}, {shamt}"),
147            Self::Rorw { rd, rs1, rs2 } => write!(f, "rorw {rd}, {rs1}, {rs2}"),
148            Self::Rev8 { rd, rs1 } => write!(f, "rev8 {rd}, {rs1}"),
149        }
150    }
151}
152
153/// RISC-V RV64 Zbb instruction (Basic bit manipulation)
154#[instruction(
155    reorder = [
156        Andn, Orn, Xnor, Clz, Clzw, Ctz, Ctzw, Cpop, Cpopw, Max, Maxu, Min, Minu, Sextb, Sexth,
157        Zexth, Rol, Rolw, Ror, Rori, Roriw, Rorw, Orcb, Rev8,
158    ],
159    inherit = [Rv64ZbbZbkbSharedInstruction],
160)]
161#[derive(Debug, Clone, Copy)]
162#[derive_const(PartialEq, Eq)]
163#[rustfmt::skip]
164pub enum Rv64ZbbInstruction<Hart>
165where
166    Hart: HartConfig,
167{
168    // RV64 Zbb instructions
169    Clz { rd: Hart::Reg, rs1: Hart::Reg },
170    Clzw { rd: Hart::Reg, rs1: Hart::Reg },
171    Ctz { rd: Hart::Reg, rs1: Hart::Reg },
172    Ctzw { rd: Hart::Reg, rs1: Hart::Reg },
173    Cpop { rd: Hart::Reg, rs1: Hart::Reg },
174    Cpopw { rd: Hart::Reg, rs1: Hart::Reg },
175    Max { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
176    Maxu { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
177    Min { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
178    Minu { rd: Hart::Reg, rs1: Hart::Reg, rs2: Hart::Reg },
179    Sextb { rd: Hart::Reg, rs1: Hart::Reg },
180    Sexth { rd: Hart::Reg, rs1: Hart::Reg },
181    Zexth { rd: Hart::Reg, rs1: Hart::Reg },
182    Orcb { rd: Hart::Reg, rs1: Hart::Reg },
183}
184
185#[instruction]
186const impl<Reg, Hart> Instruction for Rv64ZbbInstruction<Hart>
187where
188    Reg: [const] Register<Type = u64>,
189    Hart: [const] HartConfig<Reg = Reg>,
190{
191    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zbb", 1, 0)];
192
193    const ALIGNMENT: u8 = align_of::<u32>() as u8;
194
195    type Hart = Hart;
196
197    #[inline(always)]
198    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
199    fn try_decode(instruction: u32) -> Option<Self> {
200        let opcode = (instruction & 0b111_1111) as u8;
201        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
202        let funct3 = ((instruction >> 12) & 0b111) as u8;
203        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
204        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
205        let funct7 = ((instruction >> 25) & 0b111_1111) as u8;
206        let funct6 = ((instruction >> 26) & 0b11_1111) as u8;
207        // bits 25:20 for I-type distinctions
208        let low6 = ((instruction >> 20) & 0x3f) as u8;
209        let funct12 = ((instruction >> 20) & 0xfff) as u16;
210
211        match opcode {
212            // OP-IMM
213            0b001_0011 => {
214                let rd = Reg::from_bits(rd_bits)?;
215                let rs1 = Reg::from_bits(rs1_bits)?;
216                match funct3 {
217                    0b001 => {
218                        if funct6 == 0b01_1000 {
219                            match low6 {
220                                0 => Some(Self::Clz { rd, rs1 }),
221                                1 => Some(Self::Ctz { rd, rs1 }),
222                                2 => Some(Self::Cpop { rd, rs1 }),
223                                4 => Some(Self::Sextb { rd, rs1 }),
224                                5 => Some(Self::Sexth { rd, rs1 }),
225                                _ => None,
226                            }
227                        } else {
228                            None
229                        }
230                    }
231                    0b101 => {
232                        if funct12 == 0b0010_1000_0111 {
233                            Some(Self::Orcb { rd, rs1 })
234                        } else {
235                            None
236                        }
237                    }
238                    _ => None,
239                }
240            }
241            // OP / R-type
242            0b011_0011 => {
243                let rd = Reg::from_bits(rd_bits)?;
244                let rs1 = Reg::from_bits(rs1_bits)?;
245                let rs2 = Reg::from_bits(rs2_bits)?;
246                match (funct3, funct7) {
247                    (0b100, 0b000_0101) => Some(Self::Min { rd, rs1, rs2 }),
248                    (0b101, 0b000_0101) => Some(Self::Minu { rd, rs1, rs2 }),
249                    (0b110, 0b000_0101) => Some(Self::Max { rd, rs1, rs2 }),
250                    (0b111, 0b000_0101) => Some(Self::Maxu { rd, rs1, rs2 }),
251                    _ => None,
252                }
253            }
254            // OP-IMM-32
255            0b001_1011 => {
256                let rd = Reg::from_bits(rd_bits)?;
257                let rs1 = Reg::from_bits(rs1_bits)?;
258                match (funct3, funct7) {
259                    (0b001, 0b011_0000) => match rs2_bits {
260                        0 => Some(Self::Clzw { rd, rs1 }),
261                        1 => Some(Self::Ctzw { rd, rs1 }),
262                        2 => Some(Self::Cpopw { rd, rs1 }),
263                        _ => None,
264                    },
265                    _ => None,
266                }
267            }
268            // OP-32
269            0b011_1011 => {
270                let rd = Reg::from_bits(rd_bits)?;
271                let rs1 = Reg::from_bits(rs1_bits)?;
272                match (funct3, funct7) {
273                    (0b100, 0b000_0100) if rs2_bits == 0 => Some(Self::Zexth { rd, rs1 }),
274                    _ => None,
275                }
276            }
277            _ => None,
278        }
279    }
280
281    #[inline(always)]
282    fn size(&self) -> u8 {
283        size_of::<u32>() as u8
284    }
285}
286
287#[instruction]
288impl<Reg, Hart> fmt::Display for Rv64ZbbInstruction<Hart>
289where
290    Reg: fmt::Display + Copy,
291    Hart: HartConfig<Reg = Reg>,
292{
293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294        match self {
295            Self::Clz { rd, rs1 } => write!(f, "clz {rd}, {rs1}"),
296            Self::Clzw { rd, rs1 } => write!(f, "clzw {rd}, {rs1}"),
297            Self::Ctz { rd, rs1 } => write!(f, "ctz {rd}, {rs1}"),
298            Self::Ctzw { rd, rs1 } => write!(f, "ctzw {rd}, {rs1}"),
299            Self::Cpop { rd, rs1 } => write!(f, "cpop {rd}, {rs1}"),
300            Self::Cpopw { rd, rs1 } => write!(f, "cpopw {rd}, {rs1}"),
301            Self::Max { rd, rs1, rs2 } => write!(f, "max {rd}, {rs1}, {rs2}"),
302            Self::Maxu { rd, rs1, rs2 } => write!(f, "maxu {rd}, {rs1}, {rs2}"),
303            Self::Min { rd, rs1, rs2 } => write!(f, "min {rd}, {rs1}, {rs2}"),
304            Self::Minu { rd, rs1, rs2 } => write!(f, "minu {rd}, {rs1}, {rs2}"),
305            Self::Sextb { rd, rs1 } => write!(f, "sext.b {rd}, {rs1}"),
306            Self::Sexth { rd, rs1 } => write!(f, "sext.h {rd}, {rs1}"),
307            Self::Zexth { rd, rs1 } => write!(f, "zext.h {rd}, {rs1}"),
308            Self::Orcb { rd, rs1 } => write!(f, "orc.b {rd}, {rs1}"),
309        }
310    }
311}