Skip to main content

ab_riscv_primitives/instructions/
rv32.rs

1//! Base RISC-V RV32 instruction set
2
3pub mod a;
4pub mod b;
5pub mod c;
6pub mod m;
7#[cfg(test)]
8mod tests;
9pub mod zabha;
10pub mod zacas;
11pub mod zalasr;
12pub mod zce;
13pub mod zk;
14
15use crate::instructions::Instruction;
16use crate::instructions::utils::{I24, I24WithZeroedBits};
17use crate::registers::general_purpose::Register;
18use ab_riscv_macros::instruction;
19use core::fmt;
20
21/// RISC-V RV32 instruction
22#[instruction]
23#[derive(Debug, Clone, Copy)]
24#[derive_const(PartialEq, Eq)]
25pub enum Rv32Instruction<Reg> {
26    // R-type
27    Add { rd: Reg, rs1: Reg, rs2: Reg },
28    Sub { rd: Reg, rs1: Reg, rs2: Reg },
29    Sll { rd: Reg, rs1: Reg, rs2: Reg },
30    Slt { rd: Reg, rs1: Reg, rs2: Reg },
31    Sltu { rd: Reg, rs1: Reg, rs2: Reg },
32    Xor { rd: Reg, rs1: Reg, rs2: Reg },
33    Srl { rd: Reg, rs1: Reg, rs2: Reg },
34    Sra { rd: Reg, rs1: Reg, rs2: Reg },
35    Or { rd: Reg, rs1: Reg, rs2: Reg },
36    And { rd: Reg, rs1: Reg, rs2: Reg },
37
38    // I-type
39    Addi { rd: Reg, rs1: Reg, imm: i16 },
40    Slti { rd: Reg, rs1: Reg, imm: i16 },
41    Sltiu { rd: Reg, rs1: Reg, imm: i16 },
42    Xori { rd: Reg, rs1: Reg, imm: i16 },
43    Ori { rd: Reg, rs1: Reg, imm: i16 },
44    Andi { rd: Reg, rs1: Reg, imm: i16 },
45    Slli { rd: Reg, rs1: Reg, shamt: u8 },
46    Srli { rd: Reg, rs1: Reg, shamt: u8 },
47    Srai { rd: Reg, rs1: Reg, shamt: u8 },
48
49    // Loads (I-type)
50    Lb { rd: Reg, rs1: Reg, imm: i16 },
51    Lh { rd: Reg, rs1: Reg, imm: i16 },
52    Lw { rd: Reg, rs1: Reg, imm: i16 },
53    Lbu { rd: Reg, rs1: Reg, imm: i16 },
54    Lhu { rd: Reg, rs1: Reg, imm: i16 },
55
56    // Jalr (I-type)
57    Jalr { rd: Reg, rs1: Reg, imm: i16 },
58
59    // S-type
60    Sb { rs2: Reg, rs1: Reg, imm: i16 },
61    Sh { rs2: Reg, rs1: Reg, imm: i16 },
62    Sw { rs2: Reg, rs1: Reg, imm: i16 },
63
64    // B-type
65    Beq { rs1: Reg, rs2: Reg, imm: i16 },
66    Bne { rs1: Reg, rs2: Reg, imm: i16 },
67    Blt { rs1: Reg, rs2: Reg, imm: i16 },
68    Bge { rs1: Reg, rs2: Reg, imm: i16 },
69    Bltu { rs1: Reg, rs2: Reg, imm: i16 },
70    Bgeu { rs1: Reg, rs2: Reg, imm: i16 },
71
72    // Lui (U-type)
73    Lui { rd: Reg, imm: I24WithZeroedBits<12> },
74
75    // Auipc (U-type)
76    Auipc { rd: Reg, imm: I24WithZeroedBits<12> },
77
78    // Jal (J-type)
79    Jal { rd: Reg, imm: I24 },
80
81    // Fence
82    Fence { pred: u8, succ: u8 },
83    FenceTso,
84
85    // System instructions
86    Ecall,
87    Ebreak,
88
89    // Unimplemented/illegal
90    Unimp,
91}
92
93#[instruction]
94const impl<Reg> Instruction for Rv32Instruction<Reg>
95where
96    Reg: [const] Register<Type = u32>,
97{
98    const ALIGNMENT: u8 = align_of::<u32>() as u8;
99
100    type Reg = Reg;
101
102    #[inline(always)]
103    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
104    fn try_decode(instruction: u32) -> Option<Self> {
105        let opcode = (instruction & 0b111_1111) as u8;
106        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
107        let funct3 = ((instruction >> 12) & 0b111) as u8;
108        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
109        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
110        let funct7 = ((instruction >> 25) & 0b111_1111) as u8;
111
112        match opcode {
113            // R-type
114            0b011_0011 => {
115                let rd = Reg::from_bits(rd_bits)?;
116                let rs1 = Reg::from_bits(rs1_bits)?;
117                let rs2 = Reg::from_bits(rs2_bits)?;
118                match (funct3, funct7) {
119                    (0b000, 0b000_0000) => Some(Self::Add { rd, rs1, rs2 }),
120                    (0b000, 0b010_0000) => Some(Self::Sub { rd, rs1, rs2 }),
121                    (0b001, 0b000_0000) => Some(Self::Sll { rd, rs1, rs2 }),
122                    (0b010, 0b000_0000) => Some(Self::Slt { rd, rs1, rs2 }),
123                    (0b011, 0b000_0000) => Some(Self::Sltu { rd, rs1, rs2 }),
124                    (0b100, 0b000_0000) => Some(Self::Xor { rd, rs1, rs2 }),
125                    (0b101, 0b000_0000) => Some(Self::Srl { rd, rs1, rs2 }),
126                    (0b101, 0b010_0000) => Some(Self::Sra { rd, rs1, rs2 }),
127                    (0b110, 0b000_0000) => Some(Self::Or { rd, rs1, rs2 }),
128                    (0b111, 0b000_0000) => Some(Self::And { rd, rs1, rs2 }),
129                    _ => None,
130                }
131            }
132            // I-type
133            0b001_0011 => {
134                let rd = Reg::from_bits(rd_bits)?;
135                let rs1 = Reg::from_bits(rs1_bits)?;
136                let imm = (instruction.cast_signed() >> 20) as i16;
137                match funct3 {
138                    0b000 => Some(Self::Addi { rd, rs1, imm }),
139                    0b010 => Some(Self::Slti { rd, rs1, imm }),
140                    0b011 => Some(Self::Sltiu { rd, rs1, imm }),
141                    0b100 => Some(Self::Xori { rd, rs1, imm }),
142                    0b110 => Some(Self::Ori { rd, rs1, imm }),
143                    0b111 => Some(Self::Andi { rd, rs1, imm }),
144                    0b001 => {
145                        // RV32: 5-bit shamt, funct7 must be 0b0000000
146                        let shamt = (instruction >> 20) as u8 & 0b1_1111;
147                        if funct7 == 0b000_0000 {
148                            Some(Self::Slli { rd, rs1, shamt })
149                        } else {
150                            None
151                        }
152                    }
153                    0b101 => {
154                        // RV32: 5-bit shamt, funct7 distinguishes SRLI/SRAI
155                        let shamt = (instruction >> 20) as u8 & 0b1_1111;
156                        match funct7 {
157                            0b000_0000 => Some(Self::Srli { rd, rs1, shamt }),
158                            0b010_0000 => Some(Self::Srai { rd, rs1, shamt }),
159                            _ => None,
160                        }
161                    }
162                    _ => None,
163                }
164            }
165            // Loads (I-type)
166            0b000_0011 => {
167                let rd = Reg::from_bits(rd_bits)?;
168                let rs1 = Reg::from_bits(rs1_bits)?;
169                let imm = (instruction.cast_signed() >> 20) as i16;
170                match funct3 {
171                    0b000 => Some(Self::Lb { rd, rs1, imm }),
172                    0b001 => Some(Self::Lh { rd, rs1, imm }),
173                    0b010 => Some(Self::Lw { rd, rs1, imm }),
174                    0b100 => Some(Self::Lbu { rd, rs1, imm }),
175                    0b101 => Some(Self::Lhu { rd, rs1, imm }),
176                    _ => None,
177                }
178            }
179            // Jalr (I-type)
180            0b110_0111 => {
181                let rd = Reg::from_bits(rd_bits)?;
182                let rs1 = Reg::from_bits(rs1_bits)?;
183                if funct3 == 0b000 {
184                    let imm = (instruction.cast_signed() >> 20) as i16;
185                    Some(Self::Jalr { rd, rs1, imm })
186                } else {
187                    None
188                }
189            }
190            // S-type
191            0b010_0011 => {
192                let rs1 = Reg::from_bits(rs1_bits)?;
193                let rs2 = Reg::from_bits(rs2_bits)?;
194                let imm11_5 = ((instruction >> 25) & 0b111_1111).cast_signed();
195                let imm4_0 = ((instruction >> 7) & 0b1_1111).cast_signed();
196                let imm = (imm11_5 << 5) | imm4_0;
197                // Sign extend
198                let imm = ((imm << 20) >> 20) as i16;
199                match funct3 {
200                    0b000 => Some(Self::Sb { rs2, rs1, imm }),
201                    0b001 => Some(Self::Sh { rs2, rs1, imm }),
202                    0b010 => Some(Self::Sw { rs2, rs1, imm }),
203                    _ => None,
204                }
205            }
206            // B-type
207            0b110_0011 => {
208                let rs1 = Reg::from_bits(rs1_bits)?;
209                let rs2 = Reg::from_bits(rs2_bits)?;
210                let imm12 = ((instruction >> 31) & 1).cast_signed();
211                let imm10_5 = ((instruction >> 25) & 0b11_1111).cast_signed();
212                let imm4_1 = ((instruction >> 8) & 0b1111).cast_signed();
213                let imm11 = ((instruction >> 7) & 1).cast_signed();
214                let imm = (imm12 << 12) | (imm11 << 11) | (imm10_5 << 5) | (imm4_1 << 1);
215                // Sign extend
216                let imm = ((imm << 19) >> 19) as i16;
217                match funct3 {
218                    0b000 => Some(Self::Beq { rs1, rs2, imm }),
219                    0b001 => Some(Self::Bne { rs1, rs2, imm }),
220                    0b100 => Some(Self::Blt { rs1, rs2, imm }),
221                    0b101 => Some(Self::Bge { rs1, rs2, imm }),
222                    0b110 => Some(Self::Bltu { rs1, rs2, imm }),
223                    0b111 => Some(Self::Bgeu { rs1, rs2, imm }),
224                    _ => None,
225                }
226            }
227            // Lui (U-type)
228            0b011_0111 => {
229                let rd = Reg::from_bits(rd_bits)?;
230                let imm = I24WithZeroedBits::from_i32((instruction & 0xffff_f000).cast_signed());
231                Some(Self::Lui { rd, imm })
232            }
233            // Auipc (U-type)
234            0b001_0111 => {
235                let rd = Reg::from_bits(rd_bits)?;
236                let imm = I24WithZeroedBits::from_i32((instruction & 0xffff_f000).cast_signed());
237                Some(Self::Auipc { rd, imm })
238            }
239            // Jal (J-type)
240            0b110_1111 => {
241                let rd = Reg::from_bits(rd_bits)?;
242                let imm20 = ((instruction >> 31) & 1).cast_signed();
243                let imm10_1 = ((instruction >> 21) & 0b11_1111_1111).cast_signed();
244                let imm11 = ((instruction >> 20) & 1).cast_signed();
245                let imm19_12 = ((instruction >> 12) & 0b1111_1111).cast_signed();
246                let imm = (imm20 << 20) | (imm19_12 << 12) | (imm11 << 11) | (imm10_1 << 1);
247                // Sign extend
248                let imm = I24::from_i32((imm << 11) >> 11);
249                Some(Self::Jal { rd, imm })
250            }
251            // Fence (I-type like, simplified for EM)
252            0b000_1111 => {
253                if funct3 == 0b000 {
254                    let fm = (instruction >> 28) & 0b1111;
255                    let pred = ((instruction >> 24) & 0xf) as u8;
256                    let succ = ((instruction >> 20) & 0xf) as u8;
257                    if fm == 0b1000 && pred == 0b0011 && succ == 0b0011 {
258                        // FENCE.TSO: fm=8, pred=RW(0b0011), succ=RW(0b0011) - fixed by spec
259                        Some(Self::FenceTso)
260                    } else {
261                        // rd, rs1, and any other fm/pred/succ combination are reserved for
262                        // finer-grain fences and hints; implementations that don't support those
263                        // must ignore them and execute an ordinary (conservative) fence rather
264                        // than trapping.
265                        Some(Self::Fence { pred, succ })
266                    }
267                } else {
268                    None
269                }
270            }
271            // System instructions
272            0b111_0011 => {
273                let imm = (instruction >> 20) & 0xfff;
274                if funct3 == 0 && rd_bits == 0 && rs1_bits == 0 {
275                    match imm {
276                        0 => Some(Self::Ecall),
277                        1 => Some(Self::Ebreak),
278                        _ => None,
279                    }
280                } else if funct3 == 0b001 && rd_bits == 0 && rs1_bits == 0 && imm == 0xc00 {
281                    // `0xc000_1073` is emitted as `unimp`/illegal instruction by various compilers,
282                    // including Rust when it hits a panic
283                    Some(Self::Unimp)
284                } else {
285                    None
286                }
287            }
288            _ => None,
289        }
290    }
291
292    #[inline(always)]
293    fn size(&self) -> u8 {
294        size_of::<u32>() as u8
295    }
296}
297
298#[instruction]
299impl<Reg> fmt::Display for Rv32Instruction<Reg>
300where
301    Reg: fmt::Display,
302{
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        match self {
305            Self::Add { rd, rs1, rs2 } => write!(f, "add {rd}, {rs1}, {rs2}"),
306            Self::Sub { rd, rs1, rs2 } => write!(f, "sub {rd}, {rs1}, {rs2}"),
307            Self::Sll { rd, rs1, rs2 } => write!(f, "sll {rd}, {rs1}, {rs2}"),
308            Self::Slt { rd, rs1, rs2 } => write!(f, "slt {rd}, {rs1}, {rs2}"),
309            Self::Sltu { rd, rs1, rs2 } => write!(f, "sltu {rd}, {rs1}, {rs2}"),
310            Self::Xor { rd, rs1, rs2 } => write!(f, "xor {rd}, {rs1}, {rs2}"),
311            Self::Srl { rd, rs1, rs2 } => write!(f, "srl {rd}, {rs1}, {rs2}"),
312            Self::Sra { rd, rs1, rs2 } => write!(f, "sra {rd}, {rs1}, {rs2}"),
313            Self::Or { rd, rs1, rs2 } => write!(f, "or {rd}, {rs1}, {rs2}"),
314            Self::And { rd, rs1, rs2 } => write!(f, "and {rd}, {rs1}, {rs2}"),
315
316            Self::Addi { rd, rs1, imm } => write!(f, "addi {rd}, {rs1}, {imm}"),
317            Self::Slti { rd, rs1, imm } => write!(f, "slti {rd}, {rs1}, {imm}"),
318            Self::Sltiu { rd, rs1, imm } => write!(f, "sltiu {rd}, {rs1}, {imm}"),
319            Self::Xori { rd, rs1, imm } => write!(f, "xori {rd}, {rs1}, {imm}"),
320            Self::Ori { rd, rs1, imm } => write!(f, "ori {rd}, {rs1}, {imm}"),
321            Self::Andi { rd, rs1, imm } => write!(f, "andi {rd}, {rs1}, {imm}"),
322            Self::Slli { rd, rs1, shamt } => write!(f, "slli {rd}, {rs1}, {shamt}"),
323            Self::Srli { rd, rs1, shamt } => write!(f, "srli {rd}, {rs1}, {shamt}"),
324            Self::Srai { rd, rs1, shamt } => write!(f, "srai {rd}, {rs1}, {shamt}"),
325
326            Self::Lb { rd, rs1, imm } => write!(f, "lb {rd}, {imm}({rs1})"),
327            Self::Lh { rd, rs1, imm } => write!(f, "lh {rd}, {imm}({rs1})"),
328            Self::Lw { rd, rs1, imm } => write!(f, "lw {rd}, {imm}({rs1})"),
329            Self::Lbu { rd, rs1, imm } => write!(f, "lbu {rd}, {imm}({rs1})"),
330            Self::Lhu { rd, rs1, imm } => write!(f, "lhu {rd}, {imm}({rs1})"),
331
332            Self::Jalr { rd, rs1, imm } => write!(f, "jalr {rd}, {imm}({rs1})"),
333
334            Self::Sb { rs2, rs1, imm } => write!(f, "sb {rs2}, {imm}({rs1})"),
335            Self::Sh { rs2, rs1, imm } => write!(f, "sh {rs2}, {imm}({rs1})"),
336            Self::Sw { rs2, rs1, imm } => write!(f, "sw {rs2}, {imm}({rs1})"),
337
338            Self::Beq { rs1, rs2, imm } => write!(f, "beq {rs1}, {rs2}, {imm}"),
339            Self::Bne { rs1, rs2, imm } => write!(f, "bne {rs1}, {rs2}, {imm}"),
340            Self::Blt { rs1, rs2, imm } => write!(f, "blt {rs1}, {rs2}, {imm}"),
341            Self::Bge { rs1, rs2, imm } => write!(f, "bge {rs1}, {rs2}, {imm}"),
342            Self::Bltu { rs1, rs2, imm } => write!(f, "bltu {rs1}, {rs2}, {imm}"),
343            Self::Bgeu { rs1, rs2, imm } => write!(f, "bgeu {rs1}, {rs2}, {imm}"),
344
345            Self::Lui { rd, imm } => write!(f, "lui {rd}, 0x{:x}", imm.to_i32() >> 12),
346
347            Self::Auipc { rd, imm } => write!(f, "auipc {rd}, 0x{:x}", imm.to_i32() >> 12),
348
349            Self::Jal { rd, imm } => write!(f, "jal {rd}, {imm}"),
350
351            Self::Fence { pred, succ } => write!(f, "fence {pred}, {succ}"),
352            Self::FenceTso => write!(f, "fence.tso"),
353
354            Self::Ecall => write!(f, "ecall"),
355            Self::Ebreak => write!(f, "ebreak"),
356
357            Self::Unimp => write!(f, "unimp"),
358        }
359    }
360}