lib/std/arch/rv64/encode.rad 23.3 KiB raw
1
//! RISC-V RV64I+M+A instruction encoding.
2
//!
3
//! Provides type-safe functions for encoding RV64 instructions.
4
5
use std::lang::gen;
6
7
//////////////////////
8
// Opcode Constants //
9
//////////////////////
10
11
export constant OP_LOAD:   u32 = 0x03;
12
export constant OP_STORE:  u32 = 0x23;
13
/// RV64A LR, SC, and atomic read-modify-write opcode.
14
export constant OP_AMO:    u32 = 0x2F;
15
export constant OP_BRANCH: u32 = 0x63;
16
export constant OP_JALR:   u32 = 0x67;
17
export constant OP_JAL:    u32 = 0x6F;
18
export constant OP_OP:     u32 = 0x33;
19
export constant OP_IMM:    u32 = 0x13;
20
export constant OP_AUIPC:  u32 = 0x17;
21
export constant OP_LUI:    u32 = 0x37;
22
export constant OP_SYSTEM: u32 = 0x73;
23
export constant OP_OP32:   u32 = 0x3B;  // RV64: 32-bit operations
24
export constant OP_IMM32:  u32 = 0x1B;  // RV64: 32-bit immediate operations
25
26
//////////////////////
27
// Funct3 Constants //
28
//////////////////////
29
30
// Memory operations
31
32
export constant F3_BYTE:   u32 = 0x0;  // LB/SB
33
export constant F3_HALF:   u32 = 0x1;  // LH/SH
34
export constant F3_WORD:   u32 = 0x2;  // LW/SW
35
export constant F3_DWORD:  u32 = 0x3;  // LD/SD (RV64)
36
export constant F3_BYTE_U: u32 = 0x4;  // LBU
37
export constant F3_HALF_U: u32 = 0x5;  // LHU
38
export constant F3_WORD_U: u32 = 0x6;  // LWU (RV64)
39
40
// ALU operations
41
42
export constant F3_ADD:  u32 = 0x0;  // ADD/SUB/ADDI
43
export constant F3_SLL:  u32 = 0x1;  // SLL/SLLI
44
export constant F3_SLT:  u32 = 0x2;  // SLT/SLTI
45
export constant F3_SLTU: u32 = 0x3;  // SLTU/SLTIU
46
export constant F3_XOR:  u32 = 0x4;  // XOR/XORI
47
export constant F3_SRL:  u32 = 0x5;  // SRL/SRA/SRLI/SRAI
48
export constant F3_OR:   u32 = 0x6;  // OR/ORI
49
export constant F3_AND:  u32 = 0x7;  // AND/ANDI
50
51
// Branch operations
52
53
export constant F3_BEQ:  u32 = 0x0;
54
export constant F3_BNE:  u32 = 0x1;
55
export constant F3_BLT:  u32 = 0x4;
56
export constant F3_BGE:  u32 = 0x5;
57
export constant F3_BLTU: u32 = 0x6;
58
export constant F3_BGEU: u32 = 0x7;
59
60
// CSR/system operations
61
62
export constant F3_CSRRW: u32 = 0x1;
63
export constant F3_CSRRS: u32 = 0x2;
64
export constant F3_CSRRC: u32 = 0x3;
65
export constant F3_CSRRWI: u32 = 0x5;
66
export constant F3_CSRRSI: u32 = 0x6;
67
export constant F3_CSRRCI: u32 = 0x7;
68
69
//////////////////////
70
// Funct7 Constants //
71
//////////////////////
72
73
export constant F7_NORMAL: u32 = 0b0000000;
74
export constant F7_SUB:    u32 = 0b0100000;  // Bit 5 set
75
export constant F7_SRA:    u32 = 0b0100000;  // Bit 5 set
76
export constant F7_MUL:    u32 = 0b0000001;  // Bit 0 set
77
78
//////////////////////
79
// Funct5 Constants //
80
//////////////////////
81
82
/// Atomic addition function field.
83
export constant F5_AMOADD:  u32 = 0b00000;
84
/// Atomic swap function field.
85
export constant F5_AMOSWAP: u32 = 0b00001;
86
/// Load-reserved function field; the source register field must be zero.
87
export constant F5_LR:      u32 = 0b00010;
88
/// Store-conditional function field.
89
export constant F5_SC:      u32 = 0b00011;
90
/// Atomic XOR function field.
91
export constant F5_AMOXOR:  u32 = 0b00100;
92
/// Atomic OR function field.
93
export constant F5_AMOOR:   u32 = 0b01000;
94
/// Atomic AND function field.
95
export constant F5_AMOAND:  u32 = 0b01100;
96
/// Atomic signed minimum function field.
97
export constant F5_AMOMIN:  u32 = 0b10000;
98
/// Atomic signed maximum function field.
99
export constant F5_AMOMAX:  u32 = 0b10100;
100
/// Atomic unsigned minimum function field.
101
export constant F5_AMOMINU: u32 = 0b11000;
102
/// Atomic unsigned maximum function field.
103
export constant F5_AMOMAXU: u32 = 0b11100;
104
105
/////////////////////////
106
// Validation Helpers  //
107
/////////////////////////
108
109
/// Returns `true` if the value fits in a signed 12-bit immediate.
110
export fn isSmallImm(value: i32) -> bool {
111
    return value >= super::MIN_IMM and value <= super::MAX_IMM;
112
}
113
114
/// Returns `true` if a 64-bit value fits in a 12-bit signed immediate.
115
export fn isSmallImm64(value: i64) -> bool {
116
    return value >= (super::MIN_IMM as i64) and value <= (super::MAX_IMM as i64);
117
}
118
119
/// Returns `true` if the value is valid for branch immediates.
120
/// Branch immediates are 13-bit signed, even aligned.
121
export fn isBranchImm(value: i32) -> bool {
122
    return value >= -(1 << 12) and value <= ((1 << 12) - 2) and (value & 1) == 0;
123
}
124
125
/// Returns `true` if the value is valid for jump immediates (JAL).
126
/// Jump immediates are 21-bit signed, even aligned.
127
export fn isJumpImm(value: i32) -> bool {
128
    return value >= -(1 << 20) and value <= ((1 << 20) - 2) and (value & 1) == 0;
129
}
130
131
//////////////////////
132
// Format Encoders  //
133
//////////////////////
134
135
/// Encode an R-type instruction.
136
fn encodeR(opcode: u32, rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, funct7: u32) -> u32 {
137
    return (opcode        & 0x7F)
138
         | ((*rd  as u32  & 0x1F) << 7)
139
         | ((funct3       & 0x07) << 12)
140
         | ((*rs1 as u32  & 0x1F) << 15)
141
         | ((*rs2 as u32  & 0x1F) << 20)
142
         | ((funct7       & 0x7F) << 25);
143
}
144
145
/// Encode an I-type instruction.
146
fn encodeI(opcode: u32, rd: gen::Reg, rs1: gen::Reg, funct3: u32, imm: i32) -> u32 {
147
    assert isSmallImm(imm);
148
149
    return (opcode        & 0x7F)
150
         | ((*rd  as u32  & 0x1F)  << 7)
151
         | ((funct3       & 0x07)  << 12)
152
         | ((*rs1 as u32  & 0x1F)  << 15)
153
         | ((imm as u32   & 0xFFF) << 20);
154
}
155
156
/// Encode an S-type instruction.
157
fn encodeS(opcode: u32, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, imm: i32) -> u32 {
158
    assert isSmallImm(imm);
159
160
    return (opcode  & 0x7F)
161
         | ((imm as u32       & 0x1F) << 7)
162
         | ((funct3           & 0x07) << 12)
163
         | ((*rs1 as u32      & 0x1F) << 15)
164
         | ((*rs2 as u32      & 0x1F) << 20)
165
         | ((imm as u32 >> 5  & 0x7F) << 25);
166
}
167
168
/// Encode a B-type (branch) instruction.
169
fn encodeB(opcode: u32, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, imm: i32) -> u32 {
170
    assert isBranchImm(imm);
171
172
    let imm11   = (imm as u32 >> 11) & 0x1;
173
    let imm4_1  = (imm as u32 >> 1)  & 0xF;
174
    let imm10_5 = (imm as u32 >> 5)  & 0x3F;
175
    let imm12   = (imm as u32 >> 12) & 0x1;
176
177
    return (opcode        &  0x7F)
178
         | (imm11         << 7)
179
         | (imm4_1        << 8)
180
         | ((funct3       & 0x07) << 12)
181
         | ((*rs1 as u32  & 0x1F) << 15)
182
         | ((*rs2 as u32  & 0x1F) << 20)
183
         | (imm10_5       << 25)
184
         | (imm12         << 31);
185
}
186
187
/// Encode a U-type instruction.
188
fn encodeU(opcode: u32, rd: gen::Reg, imm: i32) -> u32 {
189
    return (opcode       & 0x7F)
190
         | ((*rd as u32  & 0x1F)    << 7)
191
         | ((imm as u32  & 0xFFFFF) << 12);
192
}
193
194
/// Encode a J-type (jump) instruction.
195
fn encodeJ(opcode: u32, rd: gen::Reg, imm: i32) -> u32 {
196
    assert isJumpImm(imm);
197
198
    let imm20    = (imm as u32 >> 20) & 0x1;
199
    let imm10_1  = (imm as u32 >> 1)  & 0x3FF;
200
    let imm11    = (imm as u32 >> 11) & 0x1;
201
    let imm19_12 = (imm as u32 >> 12) & 0xFF;
202
203
    return (opcode       & 0x7F)
204
         | ((*rd as u32  & 0x1F) << 7)
205
         | (imm19_12     << 12)
206
         | (imm11        << 20)
207
         | (imm10_1      << 21)
208
         | (imm20        << 31);
209
}
210
211
/// Encode an RV64A operation with a standard F5_* function and zero byte offset.
212
/// Width is F3_WORD or F3_DWORD; ordering holds aq in bit 1 and rl in bit 0.
213
/// LR requires rs2 = x0; rs1 is always the address register.
214
export fn atomic(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg, funct5: u32, width: u32, ordering: u32) -> u32 {
215
    assert funct5 < 32;
216
    assert width == F3_WORD or width == F3_DWORD;
217
    assert ordering < 4;
218
    assert funct5 <> F5_LR or *rs2 == 0;
219
220
    return encodeR(OP_AMO, rd, rs1, rs2, width, (funct5 << 2) | ordering);
221
}
222
223
////////////////////////////
224
// ALU Immediate (I-type) //
225
////////////////////////////
226
227
/// Add immediate: `rd = rs1 + imm`.
228
export fn addi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
229
    return encodeI(OP_IMM, rd, rs1, F3_ADD, imm);
230
}
231
232
/// Set less than immediate (signed): `rd = (rs1 < imm) ? 1 : 0`.
233
export fn slti(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
234
    return encodeI(OP_IMM, rd, rs1, F3_SLT, imm);
235
}
236
237
/// Set less than immediate unsigned: `rd = (rs1 < imm) ? 1 : 0`.
238
export fn sltiu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
239
    return encodeI(OP_IMM, rd, rs1, F3_SLTU, imm);
240
}
241
242
/// XOR immediate: `rd = rs1 ^ imm`.
243
export fn xori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
244
    return encodeI(OP_IMM, rd, rs1, F3_XOR, imm);
245
}
246
247
/// OR immediate: `rd = rs1 | imm`.
248
export fn ori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
249
    return encodeI(OP_IMM, rd, rs1, F3_OR, imm);
250
}
251
252
/// AND immediate: `rd = rs1 & imm`.
253
export fn andi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
254
    return encodeI(OP_IMM, rd, rs1, F3_AND, imm);
255
}
256
257
/// Shift left logical immediate: `rd = rs1 << shamt`.
258
export fn slli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
259
    assert shamt >= 0 and shamt < 64;
260
    return encodeI(OP_IMM, rd, rs1, F3_SLL, shamt & 0x3F);
261
}
262
263
/// Shift right logical immediate: `rd = rs1 >> shamt` (zero-extend).
264
export fn srli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
265
    assert shamt >= 0 and shamt < 64;
266
    return encodeI(OP_IMM, rd, rs1, F3_SRL, shamt & 0x3F);
267
}
268
269
/// Shift right arithmetic immediate: `rd = rs1 >> shamt` (sign-extend).
270
export fn srai(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
271
    assert shamt >= 0 and shamt < 64;
272
    // SRAI has bit 10 set in immediate field (becomes bit 30 in instruction)
273
    return encodeI(OP_IMM, rd, rs1, F3_SRL, (shamt & 0x3F) | 0b10000000000);
274
}
275
276
///////////////////////////
277
// ALU Register (R-type) //
278
///////////////////////////
279
280
/// Add: `rd = rs1 + rs2`.
281
export fn add(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
282
    return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_NORMAL);
283
}
284
285
/// Subtract: `rd = rs1 - rs2`.
286
export fn sub(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
287
    return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_SUB);
288
}
289
290
/// Shift left logical: `rd = rs1 << rs2[5:0]`.
291
export fn sll(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
292
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_NORMAL);
293
}
294
295
/// Set less than (signed): `rd = (rs1 < rs2) ? 1 : 0`.
296
export fn slt(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
297
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_NORMAL);
298
}
299
300
/// Set less than unsigned: `rd = (rs1 < rs2) ? 1 : 0`.
301
export fn sltu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
302
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_NORMAL);
303
}
304
305
/// XOR: `rd = rs1 ^ rs2`.
306
export fn xor(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
307
    return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_NORMAL);
308
}
309
310
/// Shift right logical: `rd = rs1 >> rs2[5:0]` (zero-extend).
311
export fn srl(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
312
    return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_NORMAL);
313
}
314
315
/// Shift right arithmetic: `rd = rs1 >> rs2[5:0]` (sign-extend).
316
export fn sra(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
317
    return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_SRA);
318
}
319
320
/// OR: `rd = rs1 | rs2`.
321
export fn or_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
322
    return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_NORMAL);
323
}
324
325
/// AND: `rd = rs1 & rs2`.
326
export fn and_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
327
    return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_NORMAL);
328
}
329
330
/////////////////
331
// M Extension //
332
/////////////////
333
334
/// Multiply: `rd = (rs1 * rs2)[63:0]`.
335
export fn mul(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
336
    return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_MUL);
337
}
338
339
/// Multiply high (signed x signed): `rd = (rs1 * rs2)[127:64]`.
340
export fn mulh(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
341
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_MUL);
342
}
343
344
/// Multiply high (signed x unsigned): `rd = (rs1 * rs2)[127:64]`.
345
export fn mulhsu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
346
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_MUL);
347
}
348
349
/// Multiply high (unsigned x unsigned): `rd = (rs1 * rs2)[127:64]`.
350
export fn mulhu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
351
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_MUL);
352
}
353
354
/// Divide (signed): `rd = rs1 / rs2`.
355
export fn div(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
356
    return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_MUL);
357
}
358
359
/// Divide (unsigned): `rd = rs1 / rs2`.
360
export fn divu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
361
    return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_MUL);
362
}
363
364
/// Remainder (signed): `rd = rs1 % rs2`.
365
export fn rem(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
366
    return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_MUL);
367
}
368
369
/// Remainder (unsigned): `rd = rs1 % rs2`.
370
export fn remu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
371
    return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_MUL);
372
}
373
374
//////////////////////////
375
// RV64 Word Operations //
376
//////////////////////////
377
378
/// Add immediate word (32-bit, sign-extended): `rd = sign_ext((rs1 + imm)[31:0])`.
379
export fn addiw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
380
    return encodeI(OP_IMM32, rd, rs1, F3_ADD, imm);
381
}
382
383
/// Shift left logical immediate word: `rd = sign_ext((rs1 << shamt)[31:0])`.
384
export fn slliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
385
    assert shamt >= 0 and shamt < 32;
386
    return encodeI(OP_IMM32, rd, rs1, F3_SLL, shamt & 0x1F);
387
}
388
389
/// Shift right logical immediate word: `rd = sign_ext((rs1[31:0] >> shamt))`.
390
export fn srliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
391
    assert shamt >= 0 and shamt < 32;
392
    return encodeI(OP_IMM32, rd, rs1, F3_SRL, shamt & 0x1F);
393
}
394
395
/// Shift right arithmetic immediate word: `rd = sign_ext((rs1[31:0] >> shamt))` (sign-extended).
396
export fn sraiw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
397
    assert shamt >= 0 and shamt < 32;
398
    return encodeI(OP_IMM32, rd, rs1, F3_SRL, (shamt & 0x1F) | 0b10000000000);
399
}
400
401
/// Add word: `rd = sign_ext((rs1 + rs2)[31:0])`.
402
export fn addw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
403
    return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_NORMAL);
404
}
405
406
/// Subtract word: `rd = sign_ext((rs1 - rs2)[31:0])`.
407
export fn subw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
408
    return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_SUB);
409
}
410
411
/// Shift left logical word: `rd = sign_ext((rs1 << rs2[4:0])[31:0])`.
412
export fn sllw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
413
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SLL, F7_NORMAL);
414
}
415
416
/// Shift right logical word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))`.
417
export fn srlw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
418
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_NORMAL);
419
}
420
421
/// Shift right arithmetic word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))` (sign-extended).
422
export fn sraw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
423
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_SRA);
424
}
425
426
/// Multiply word: `rd = sign_ext((rs1 * rs2)[31:0])`.
427
export fn mulw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
428
    return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_MUL);
429
}
430
431
/// Divide word (signed): `rd = sign_ext(rs1[31:0] / rs2[31:0])`.
432
export fn divw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
433
    return encodeR(OP_OP32, rd, rs1, rs2, F3_XOR, F7_MUL);
434
}
435
436
/// Divide word (unsigned): `rd = sign_ext(rs1[31:0] / rs2[31:0])`.
437
export fn divuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
438
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_MUL);
439
}
440
441
/// Remainder word (signed): `rd = sign_ext(rs1[31:0] % rs2[31:0])`.
442
export fn remw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
443
    return encodeR(OP_OP32, rd, rs1, rs2, F3_OR, F7_MUL);
444
}
445
446
/// Remainder word (unsigned): `rd = sign_ext(rs1[31:0] % rs2[31:0])`.
447
export fn remuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
448
    return encodeR(OP_OP32, rd, rs1, rs2, F3_AND, F7_MUL);
449
}
450
451
///////////////////
452
// Load (I-type) //
453
///////////////////
454
455
/// Load byte (sign-extend): `rd = mem[rs1 + imm][7:0]`.
456
export fn lb(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
457
    return encodeI(OP_LOAD, rd, rs1, F3_BYTE, imm);
458
}
459
460
/// Load halfword (sign-extend): `rd = mem[rs1 + imm][15:0]`.
461
export fn lh(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
462
    return encodeI(OP_LOAD, rd, rs1, F3_HALF, imm);
463
}
464
465
/// Load word (sign-extend to 64-bit): `rd = sign_ext(mem[rs1 + imm][31:0])`.
466
export fn lw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
467
    return encodeI(OP_LOAD, rd, rs1, F3_WORD, imm);
468
}
469
470
/// Load byte unsigned (zero-extend): `rd = mem[rs1 + imm][7:0]`.
471
export fn lbu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
472
    return encodeI(OP_LOAD, rd, rs1, F3_BYTE_U, imm);
473
}
474
475
/// Load halfword unsigned (zero-extend): `rd = mem[rs1 + imm][15:0]`.
476
export fn lhu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
477
    return encodeI(OP_LOAD, rd, rs1, F3_HALF_U, imm);
478
}
479
480
/// Load word unsigned (zero-extend to 64-bit): `rd = mem[rs1 + imm][31:0]`.
481
export fn lwu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
482
    return encodeI(OP_LOAD, rd, rs1, F3_WORD_U, imm);
483
}
484
485
/// Load doubleword: `rd = mem[rs1 + imm][63:0]`.
486
export fn ld(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
487
    return encodeI(OP_LOAD, rd, rs1, F3_DWORD, imm);
488
}
489
490
////////////////////
491
// Store (S-type) //
492
////////////////////
493
494
/// Store byte: `mem[rs1 + imm] = rs2[7:0]`.
495
export fn sb(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
496
    return encodeS(OP_STORE, rs1, rs2, F3_BYTE, imm);
497
}
498
499
/// Store halfword: `mem[rs1 + imm] = rs2[15:0]`.
500
export fn sh(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
501
    return encodeS(OP_STORE, rs1, rs2, F3_HALF, imm);
502
}
503
504
/// Store word: `mem[rs1 + imm] = rs2[31:0]`.
505
export fn sw(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
506
    return encodeS(OP_STORE, rs1, rs2, F3_WORD, imm);
507
}
508
509
/// Store doubleword: `mem[rs1 + imm] = rs2[63:0]`.
510
export fn sd(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
511
    return encodeS(OP_STORE, rs1, rs2, F3_DWORD, imm);
512
}
513
514
/////////////////////
515
// Branch (B-type) //
516
/////////////////////
517
518
/// Branch if equal: `if (rs1 == rs2) pc += imm`.
519
export fn beq(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
520
    return encodeB(OP_BRANCH, rs1, rs2, F3_BEQ, imm);
521
}
522
523
/// Branch if not equal: `if (rs1 <> rs2) pc += imm`.
524
export fn bne(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
525
    return encodeB(OP_BRANCH, rs1, rs2, F3_BNE, imm);
526
}
527
528
/// Branch if less than (signed): `if (rs1 < rs2) pc += imm`.
529
export fn blt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
530
    return encodeB(OP_BRANCH, rs1, rs2, F3_BLT, imm);
531
}
532
533
/// Branch if greater or equal (signed): `if (rs1 >= rs2) pc += imm`.
534
export fn bge(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
535
    return encodeB(OP_BRANCH, rs1, rs2, F3_BGE, imm);
536
}
537
538
/// Branch if less than unsigned: `if (rs1 < rs2) pc += imm`.
539
export fn bltu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
540
    return encodeB(OP_BRANCH, rs1, rs2, F3_BLTU, imm);
541
}
542
543
/// Branch if greater or equal unsigned: `if (rs1 >= rs2) pc += imm`.
544
export fn bgeu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
545
    return encodeB(OP_BRANCH, rs1, rs2, F3_BGEU, imm);
546
}
547
548
//////////
549
// Jump //
550
//////////
551
552
/// Jump and link: `rd = pc + 4; pc += imm`.
553
export fn jal(rd: gen::Reg, imm: i32) -> u32 {
554
    return encodeJ(OP_JAL, rd, imm);
555
}
556
557
/// Jump and link register: `rd = pc + 4; pc = rs1 + imm`.
558
export fn jalr(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
559
    return encodeI(OP_JALR, rd, rs1, 0, imm);
560
}
561
562
/////////////////////
563
// Upper Immediate //
564
/////////////////////
565
566
/// Load upper immediate: `rd = imm << 12`.
567
export fn lui(rd: gen::Reg, imm: i32) -> u32 {
568
    return encodeU(OP_LUI, rd, imm);
569
}
570
571
/// Add upper immediate to PC: `rd = pc + (imm << 12)`.
572
export fn auipc(rd: gen::Reg, imm: i32) -> u32 {
573
    return encodeU(OP_AUIPC, rd, imm);
574
}
575
576
////////////
577
// System //
578
////////////
579
580
/// Environment call (system call).
581
export fn ecall() -> u32 {
582
    return encodeI(OP_SYSTEM, super::ZERO, super::ZERO, 0, 0);
583
}
584
585
/// Environment break (debugger breakpoint).
586
export fn ebreak() -> u32 {
587
    return encodeI(OP_SYSTEM, super::ZERO, super::ZERO, 0, 1);
588
}
589
590
/// Full predecessor/successor memory and device-I/O fence (`fence iorw, iorw`).
591
export fn fence() -> u32 {
592
    return 0x0FF0000F;
593
}
594
595
/// Synchronize this hart's instruction fetches with prior instruction stores.
596
export fn fenceI() -> u32 {
597
    return 0x0000100F;
598
}
599
600
/// Encode a CSR instruction with a register source.
601
fn encodeCsr(op: u32, rd: gen::Reg, csr: u32, funct3: u32, rs1: gen::Reg) -> u32 {
602
    return (op             & 0x7F)
603
         | ((*rd as u32    & 0x1F) << 7)
604
         | ((funct3        & 0x07) << 12)
605
         | ((*rs1 as u32   & 0x1F) << 15)
606
         | ((csr           & 0xFFF) << 20);
607
}
608
609
/// Encode a CSR instruction with an immediate source.
610
fn encodeCsrImm(op: u32, rd: gen::Reg, csr: u32, funct3: u32, imm: u32) -> u32 {
611
    assert imm < 32;
612
    return (op             & 0x7F)
613
         | ((*rd as u32    & 0x1F) << 7)
614
         | ((funct3        & 0x07) << 12)
615
         | ((imm           & 0x1F) << 15)
616
         | ((csr           & 0xFFF) << 20);
617
}
618
619
/// Read CSR into `rd`.
620
export fn csrr(rd: gen::Reg, csr: u32) -> u32 {
621
    return encodeCsr(OP_SYSTEM, rd, csr, F3_CSRRS, super::ZERO);
622
}
623
624
/// Read/write CSR: old CSR to `rd`, write `rs1`.
625
export fn csrrw(rd: gen::Reg, csr: u32, rs1: gen::Reg) -> u32 {
626
    return encodeCsr(OP_SYSTEM, rd, csr, F3_CSRRW, rs1);
627
}
628
629
/// Write `rs1` into CSR and discard old value.
630
export fn csrw(csr: u32, rs1: gen::Reg) -> u32 {
631
    return encodeCsr(OP_SYSTEM, super::ZERO, csr, F3_CSRRW, rs1);
632
}
633
634
/// Clear CSR bits from `rs1` and discard old value.
635
export fn csrc(csr: u32, rs1: gen::Reg) -> u32 {
636
    return encodeCsr(OP_SYSTEM, super::ZERO, csr, F3_CSRRC, rs1);
637
}
638
639
/// Set CSR bits from a 5-bit immediate and discard old value.
640
export fn csrsi(csr: u32, imm: u32) -> u32 {
641
    return encodeCsrImm(OP_SYSTEM, super::ZERO, csr, F3_CSRRSI, imm);
642
}
643
644
/// Wait for interrupt.
645
export fn wfi() -> u32 {
646
    return 0x10500073;
647
}
648
649
/// Return from machine mode trap.
650
export fn mret() -> u32 {
651
    return 0x30200073;
652
}
653
654
/////////////////////////
655
// Pseudo-instructions //
656
/////////////////////////
657
658
/// No operation: `addi zero, zero, 0`.
659
export fn nop() -> u32 {
660
    return addi(super::ZERO, super::ZERO, 0);
661
}
662
663
/// Move: `rd = rs` (`addi rd, rs, 0`).
664
export fn mv(rd: gen::Reg, rs: gen::Reg) -> u32 {
665
    return addi(rd, rs, 0);
666
}
667
668
/// Bitwise NOT: `rd = ~rs` (`xori rd, rs, -1`).
669
export fn not_(rd: gen::Reg, rs: gen::Reg) -> u32 {
670
    return xori(rd, rs, -1);
671
}
672
673
/// Negate: `rd = -rs` (`sub rd, zero, rs`).
674
export fn neg(rd: gen::Reg, rs: gen::Reg) -> u32 {
675
    return sub(rd, super::ZERO, rs);
676
}
677
678
/// Return: `jalr zero, ra, 0`.
679
export fn ret() -> u32 {
680
    return jalr(super::ZERO, super::RA, 0);
681
}
682
683
/// Jump (unconditional): `jal zero, imm`.
684
export fn j(imm: i32) -> u32 {
685
    return jal(super::ZERO, imm);
686
}
687
688
/// Branch if less than or equal (signed): `if (rs1 <= rs2) pc += imm`.
689
/// Implemented as `bge rs2, rs1, imm` (swap operands).
690
export fn ble(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
691
    return bge(rs2, rs1, imm);
692
}
693
694
/// Branch if greater than (signed): `if (rs1 > rs2) pc += imm`.
695
/// Implemented as `blt rs2, rs1, imm` (swap operands).
696
export fn bgt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
697
    return blt(rs2, rs1, imm);
698
}
699
700
/// Set if equal to zero: `rd = (rs == 0) ? 1 : 0`.
701
/// Implemented as `sltiu rd, rs, 1`.
702
export fn seqz(rd: gen::Reg, rs: gen::Reg) -> u32 {
703
    return sltiu(rd, rs, 1);
704
}
705
706
/// Set if not equal to zero: `rd = (rs <> 0) ? 1 : 0`.
707
/// Implemented as `sltu rd, zero, rs`.
708
export fn snez(rd: gen::Reg, rs: gen::Reg) -> u32 {
709
    return sltu(rd, super::ZERO, rs);
710
}
711
712
/// Branch if equal to zero: `if (rs == 0) pc += imm`.
713
export fn beqz(rs: gen::Reg, imm: i32) -> u32 {
714
    return beq(rs, super::ZERO, imm);
715
}
716
717
/// Branch if not equal to zero: `if (rs <> 0) pc += imm`.
718
export fn bnez(rs: gen::Reg, imm: i32) -> u32 {
719
    return bne(rs, super::ZERO, imm);
720
}
721
722
/// Call: `jal ra, imm`.
723
export fn call(imm: i32) -> u32 {
724
    return jal(super::RA, imm);
725
}