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