decode.gno
2.54 Kb · 73 lines
1package riscv
2
3// RV32 instruction decode: bit fields, not a parser.
4//
5// Every instruction is exactly 32 bits and the opcode is the low 7. The rest is
6// slicing: which fields exist depends on the format, and there are six formats.
7// This file is only the slicing; exec.gno decides what the fields mean.
8
9// Instruction formats. Named as the spec names them.
10const (
11 opLUI = 0x37
12 opAUIPC = 0x17
13 opJAL = 0x6F
14 opJALR = 0x67
15 opBranch = 0x63
16 opLoad = 0x03
17 opStore = 0x23
18 opImm = 0x13
19 opReg = 0x33
20 opSystem = 0x73
21 opFence = 0x0F
22)
23
24// opcode returns the low 7 bits.
25func opcode(inst uint32) uint32 { return inst & 0x7F }
26
27// rd, rs1 and rs2 are register selectors, 5 bits each, always in the same place
28// when the format has them.
29func rd(inst uint32) uint32 { return (inst >> 7) & 0x1F }
30func rs1(inst uint32) uint32 { return (inst >> 15) & 0x1F }
31func rs2(inst uint32) uint32 { return (inst >> 20) & 0x1F }
32
33// funct3 and funct7 narrow an opcode to an operation.
34func funct3(inst uint32) uint32 { return (inst >> 12) & 0x7 }
35func funct7(inst uint32) uint32 { return inst >> 25 }
36
37// The immediates. Each format scatters the bits differently, and every one of
38// them is SIGN EXTENDED from its top bit: RISC-V has no zero-extended immediate
39// in the base set, and getting that wrong is the classic decoder bug because
40// small positive programs still work.
41//
42// The shift-right-on-int32 idiom is the sign extension: Go defines >> on a
43// signed type as arithmetic.
44
45// immI is the 12-bit immediate of I-type (loads, ALU-immediate, JALR, ECALL).
46func immI(inst uint32) uint32 { return uint32(int32(inst) >> 20) }
47
48// immS is S-type (stores), split across two fields.
49func immS(inst uint32) uint32 {
50 return uint32(int32(inst)>>25<<5) | ((inst >> 7) & 0x1F)
51}
52
53// immB is B-type (branches): S-type with the bits rotated, and bit 0 always
54// zero because a branch target is 2-byte aligned.
55func immB(inst uint32) uint32 {
56 imm := uint32(int32(inst)>>31) << 12 // sign, bit 12
57 imm |= ((inst >> 7) & 0x1) << 11 // bit 11
58 imm |= ((inst >> 25) & 0x3F) << 5 // bits 10:5
59 imm |= ((inst >> 8) & 0xF) << 1 // bits 4:1
60 return imm
61}
62
63// immU is U-type (LUI, AUIPC): the top 20 bits, already in place.
64func immU(inst uint32) uint32 { return inst & 0xFFFFF000 }
65
66// immJ is J-type (JAL), the most scattered of the six.
67func immJ(inst uint32) uint32 {
68 imm := uint32(int32(inst)>>31) << 20 // sign, bit 20
69 imm |= ((inst >> 12) & 0xFF) << 12 // bits 19:12
70 imm |= ((inst >> 20) & 0x1) << 11 // bit 11
71 imm |= ((inst >> 21) & 0x3FF) << 1 // bits 10:1
72 return imm
73}