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