Skip to main content

ab_riscv_primitives/instructions/
rv32.rs

1//! Base RISC-V RV32 instruction set
2
3pub mod b;
4pub mod c;
5pub mod m;
6#[cfg(test)]
7mod tests;
8pub mod zce;
9pub mod zk;
10
11use crate::instructions::Instruction;
12use crate::instructions::utils::{I24, I24WithZeroedBits};
13use crate::registers::general_purpose::Register;
14use ab_riscv_macros::instruction;
15use core::fmt;
16
17/// RISC-V RV32 instruction
18#[instruction]
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum Rv32Instruction<Reg> {
21    // R-type
22    Add { rd: Reg, rs1: Reg, rs2: Reg },
23    Sub { rd: Reg, rs1: Reg, rs2: Reg },
24    Sll { rd: Reg, rs1: Reg, rs2: Reg },
25    Slt { rd: Reg, rs1: Reg, rs2: Reg },
26    Sltu { rd: Reg, rs1: Reg, rs2: Reg },
27    Xor { rd: Reg, rs1: Reg, rs2: Reg },
28    Srl { rd: Reg, rs1: Reg, rs2: Reg },
29    Sra { rd: Reg, rs1: Reg, rs2: Reg },
30    Or { rd: Reg, rs1: Reg, rs2: Reg },
31    And { rd: Reg, rs1: Reg, rs2: Reg },
32
33    // I-type
34    Addi { rd: Reg, rs1: Reg, imm: i16 },
35    Slti { rd: Reg, rs1: Reg, imm: i16 },
36    Sltiu { rd: Reg, rs1: Reg, imm: i16 },
37    Xori { rd: Reg, rs1: Reg, imm: i16 },
38    Ori { rd: Reg, rs1: Reg, imm: i16 },
39    Andi { rd: Reg, rs1: Reg, imm: i16 },
40    Slli { rd: Reg, rs1: Reg, shamt: u8 },
41    Srli { rd: Reg, rs1: Reg, shamt: u8 },
42    Srai { rd: Reg, rs1: Reg, shamt: u8 },
43
44    // Loads (I-type)
45    Lb { rd: Reg, rs1: Reg, imm: i16 },
46    Lh { rd: Reg, rs1: Reg, imm: i16 },
47    Lw { rd: Reg, rs1: Reg, imm: i16 },
48    Lbu { rd: Reg, rs1: Reg, imm: i16 },
49    Lhu { rd: Reg, rs1: Reg, imm: i16 },
50
51    // Jalr (I-type)
52    Jalr { rd: Reg, rs1: Reg, imm: i16 },
53
54    // S-type
55    Sb { rs2: Reg, rs1: Reg, imm: i16 },
56    Sh { rs2: Reg, rs1: Reg, imm: i16 },
57    Sw { rs2: Reg, rs1: Reg, imm: i16 },
58
59    // B-type
60    Beq { rs1: Reg, rs2: Reg, imm: I24 },
61    Bne { rs1: Reg, rs2: Reg, imm: I24 },
62    Blt { rs1: Reg, rs2: Reg, imm: I24 },
63    Bge { rs1: Reg, rs2: Reg, imm: I24 },
64    Bltu { rs1: Reg, rs2: Reg, imm: I24 },
65    Bgeu { rs1: Reg, rs2: Reg, imm: I24 },
66
67    // Lui (U-type)
68    Lui { rd: Reg, imm: I24WithZeroedBits<12> },
69
70    // Auipc (U-type)
71    Auipc { rd: Reg, imm: I24WithZeroedBits<12> },
72
73    // Jal (J-type)
74    Jal { rd: Reg, imm: I24 },
75
76    // Fence
77    Fence { pred: u8, succ: u8 },
78    FenceTso,
79
80    // System instructions
81    Ecall,
82    Ebreak,
83
84    // Unimplemented/illegal
85    Unimp,
86}
87
88#[instruction]
89const impl<Reg> Instruction for Rv32Instruction<Reg>
90where
91    Reg: [const] Register<Type = u32>,
92{
93    type Reg = Reg;
94
95    #[inline(always)]
96    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
97    fn try_decode(instruction: u32) -> Option<Self> {
98        let opcode = (instruction & 0b111_1111) as u8;
99        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
100        let funct3 = ((instruction >> 12) & 0b111) as u8;
101        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
102        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
103        let funct7 = ((instruction >> 25) & 0b111_1111) as u8;
104
105        match opcode {
106            // R-type
107            0b011_0011 => {
108                let rd = Reg::from_bits(rd_bits)?;
109                let rs1 = Reg::from_bits(rs1_bits)?;
110                let rs2 = Reg::from_bits(rs2_bits)?;
111                match (funct3, funct7) {
112                    (0b000, 0b000_0000) => Some(Self::Add { rd, rs1, rs2 }),
113                    (0b000, 0b010_0000) => Some(Self::Sub { rd, rs1, rs2 }),
114                    (0b001, 0b000_0000) => Some(Self::Sll { rd, rs1, rs2 }),
115                    (0b010, 0b000_0000) => Some(Self::Slt { rd, rs1, rs2 }),
116                    (0b011, 0b000_0000) => Some(Self::Sltu { rd, rs1, rs2 }),
117                    (0b100, 0b000_0000) => Some(Self::Xor { rd, rs1, rs2 }),
118                    (0b101, 0b000_0000) => Some(Self::Srl { rd, rs1, rs2 }),
119                    (0b101, 0b010_0000) => Some(Self::Sra { rd, rs1, rs2 }),
120                    (0b110, 0b000_0000) => Some(Self::Or { rd, rs1, rs2 }),
121                    (0b111, 0b000_0000) => Some(Self::And { rd, rs1, rs2 }),
122                    _ => None,
123                }
124            }
125            // I-type
126            0b001_0011 => {
127                let rd = Reg::from_bits(rd_bits)?;
128                let rs1 = Reg::from_bits(rs1_bits)?;
129                let imm = (instruction.cast_signed() >> 20) as i16;
130                match funct3 {
131                    0b000 => Some(Self::Addi { rd, rs1, imm }),
132                    0b010 => Some(Self::Slti { rd, rs1, imm }),
133                    0b011 => Some(Self::Sltiu { rd, rs1, imm }),
134                    0b100 => Some(Self::Xori { rd, rs1, imm }),
135                    0b110 => Some(Self::Ori { rd, rs1, imm }),
136                    0b111 => Some(Self::Andi { rd, rs1, imm }),
137                    0b001 => {
138                        // RV32: 5-bit shamt, funct7 must be 0b0000000
139                        let shamt = (instruction >> 20) as u8 & 0b1_1111;
140                        if funct7 == 0b000_0000 {
141                            Some(Self::Slli { rd, rs1, shamt })
142                        } else {
143                            None
144                        }
145                    }
146                    0b101 => {
147                        // RV32: 5-bit shamt, funct7 distinguishes SRLI/SRAI
148                        let shamt = (instruction >> 20) as u8 & 0b1_1111;
149                        match funct7 {
150                            0b000_0000 => Some(Self::Srli { rd, rs1, shamt }),
151                            0b010_0000 => Some(Self::Srai { rd, rs1, shamt }),
152                            _ => None,
153                        }
154                    }
155                    _ => None,
156                }
157            }
158            // Loads (I-type)
159            0b000_0011 => {
160                let rd = Reg::from_bits(rd_bits)?;
161                let rs1 = Reg::from_bits(rs1_bits)?;
162                let imm = (instruction.cast_signed() >> 20) as i16;
163                match funct3 {
164                    0b000 => Some(Self::Lb { rd, rs1, imm }),
165                    0b001 => Some(Self::Lh { rd, rs1, imm }),
166                    0b010 => Some(Self::Lw { rd, rs1, imm }),
167                    0b100 => Some(Self::Lbu { rd, rs1, imm }),
168                    0b101 => Some(Self::Lhu { rd, rs1, imm }),
169                    _ => None,
170                }
171            }
172            // Jalr (I-type)
173            0b110_0111 => {
174                let rd = Reg::from_bits(rd_bits)?;
175                let rs1 = Reg::from_bits(rs1_bits)?;
176                if funct3 == 0b000 {
177                    let imm = (instruction.cast_signed() >> 20) as i16;
178                    Some(Self::Jalr { rd, rs1, imm })
179                } else {
180                    None
181                }
182            }
183            // S-type
184            0b010_0011 => {
185                let rs1 = Reg::from_bits(rs1_bits)?;
186                let rs2 = Reg::from_bits(rs2_bits)?;
187                let imm11_5 = ((instruction >> 25) & 0b111_1111).cast_signed();
188                let imm4_0 = ((instruction >> 7) & 0b1_1111).cast_signed();
189                let imm = (imm11_5 << 5) | imm4_0;
190                // Sign extend
191                let imm = ((imm << 20) >> 20) as i16;
192                match funct3 {
193                    0b000 => Some(Self::Sb { rs2, rs1, imm }),
194                    0b001 => Some(Self::Sh { rs2, rs1, imm }),
195                    0b010 => Some(Self::Sw { rs2, rs1, imm }),
196                    _ => None,
197                }
198            }
199            // B-type
200            0b110_0011 => {
201                let rs1 = Reg::from_bits(rs1_bits)?;
202                let rs2 = Reg::from_bits(rs2_bits)?;
203                let imm12 = ((instruction >> 31) & 1).cast_signed();
204                let imm10_5 = ((instruction >> 25) & 0b11_1111).cast_signed();
205                let imm4_1 = ((instruction >> 8) & 0b1111).cast_signed();
206                let imm11 = ((instruction >> 7) & 1).cast_signed();
207                let imm = (imm12 << 12) | (imm11 << 11) | (imm10_5 << 5) | (imm4_1 << 1);
208                // Sign extend
209                let imm = I24::from_i32((imm << 19) >> 19);
210                match funct3 {
211                    0b000 => Some(Self::Beq { rs1, rs2, imm }),
212                    0b001 => Some(Self::Bne { rs1, rs2, imm }),
213                    0b100 => Some(Self::Blt { rs1, rs2, imm }),
214                    0b101 => Some(Self::Bge { rs1, rs2, imm }),
215                    0b110 => Some(Self::Bltu { rs1, rs2, imm }),
216                    0b111 => Some(Self::Bgeu { rs1, rs2, imm }),
217                    _ => None,
218                }
219            }
220            // Lui (U-type)
221            0b011_0111 => {
222                let rd = Reg::from_bits(rd_bits)?;
223                let imm = I24WithZeroedBits::from_i32((instruction & 0xffff_f000).cast_signed());
224                Some(Self::Lui { rd, imm })
225            }
226            // Auipc (U-type)
227            0b001_0111 => {
228                let rd = Reg::from_bits(rd_bits)?;
229                let imm = I24WithZeroedBits::from_i32((instruction & 0xffff_f000).cast_signed());
230                Some(Self::Auipc { rd, imm })
231            }
232            // Jal (J-type)
233            0b110_1111 => {
234                let rd = Reg::from_bits(rd_bits)?;
235                let imm20 = ((instruction >> 31) & 1).cast_signed();
236                let imm10_1 = ((instruction >> 21) & 0b11_1111_1111).cast_signed();
237                let imm11 = ((instruction >> 20) & 1).cast_signed();
238                let imm19_12 = ((instruction >> 12) & 0b1111_1111).cast_signed();
239                let imm = (imm20 << 20) | (imm19_12 << 12) | (imm11 << 11) | (imm10_1 << 1);
240                // Sign extend
241                let imm = I24::from_i32((imm << 11) >> 11);
242                Some(Self::Jal { rd, imm })
243            }
244            // Fence (I-type like, simplified for EM)
245            0b000_1111 => {
246                if funct3 == 0b000 && rd_bits == 0 && rs1_bits == 0 {
247                    let fm = (instruction >> 28) & 0b1111;
248                    let pred = ((instruction >> 24) & 0xf) as u8;
249                    let succ = ((instruction >> 20) & 0xf) as u8;
250                    match fm {
251                        0b0000 => Some(Self::Fence { pred, succ }),
252                        0b1000 => {
253                            // FENCE.TSO: fm=8, pred=RW(0b0011), succ=RW(0b0011) - fixed by spec
254                            if pred == 0b0011 && succ == 0b0011 {
255                                Some(Self::FenceTso)
256                            } else {
257                                None
258                            }
259                        }
260                        _ => None,
261                    }
262                } else {
263                    None
264                }
265            }
266            // System instructions
267            0b111_0011 => {
268                let imm = (instruction >> 20) & 0xfff;
269                if funct3 == 0 && rd_bits == 0 && rs1_bits == 0 {
270                    match imm {
271                        0 => Some(Self::Ecall),
272                        1 => Some(Self::Ebreak),
273                        _ => None,
274                    }
275                } else if funct3 == 0b001 && rd_bits == 0 && rs1_bits == 0 && imm == 0xc00 {
276                    // `0xc000_1073` is emitted as `unimp`/illegal instruction by various compilers,
277                    // including Rust when it hits a panic
278                    Some(Self::Unimp)
279                } else {
280                    None
281                }
282            }
283            _ => None,
284        }
285    }
286
287    #[inline(always)]
288    fn alignment() -> u8 {
289        align_of::<u32>() as u8
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}