Skip to main content

ab_riscv_primitives/instructions/
rv64.rs

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