riscv.c 12.0 KiB raw
1
/* RISC-V 64-bit (RV64I) instruction builder. */
2
#include <stdlib.h>
3
4
#include "riscv.h"
5
#include "types.h"
6
7
const char *reg_names[] = {
8
    "zero", "ra", "sp", "gp", "tp",  "t0",  "t1", "t2", "fp", "s1", "a0",
9
    "a1",   "a2", "a3", "a4", "a5",  "a6",  "a7", "s2", "s3", "s4", "s5",
10
    "s6",   "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
11
};
12
13
const bool caller_saved_registers[] = {
14
    false, false, false, false, false, true,  true, true,  false, false, true,
15
    true,  true,  true,  true,  true,  true,  true, false, false, false, false,
16
    false, false, false, false, false, false, true, true,  true,  true
17
};
18
19
const reg_t temp_registers[] = { T1, T2, T3, T4, T5, T6 };
20
21
i32 sign_extend(u32 value, int bit_width) {
22
    if ((value >> (bit_width - 1)) & 1) {
23
        /* Sign bit is 1, so extend with 1s. */
24
        return (i32)(value | (~0u << bit_width));
25
    }
26
    return (i32)value;
27
}
28
29
i32 align(i32 size, i32 alignment) {
30
    /* Verify alignment is a power of 2. */
31
    /* This rounds up to the next multiple of alignment. */
32
    return (size + alignment - 1) & ~(alignment - 1);
33
}
34
35
/* Creates an I-type instruction struct.
36
 * Used for immediate operations like ADDI, SLTI and loads like LW. */
37
static instr_t instr_i(
38
    opcode_t opcode, funct3_t fn3, reg_t rd, reg_t rs1, i32 imm
39
) {
40
    return (instr_t){ .i = { .opcode   = opcode,
41
                             .rd       = rd,
42
                             .rs1      = rs1,
43
                             .funct3   = fn3,
44
                             .imm_11_0 = (u32)imm } };
45
}
46
47
/* Creates a U-type instruction struct.
48
 * Used for LUI (Load Upper Immediate) and AUIPC. */
49
static instr_t instr_u(opcode_t opcode, reg_t rd, i32 imm) {
50
    return (instr_t){ .u = {
51
                          .opcode    = opcode,
52
                          .rd        = rd,
53
                          .imm_31_12 = (u32)imm & 0xFFFFF,
54
                      } };
55
}
56
57
/* Creates an R-type instruction struct.
58
 * Used for register-register operations like ADD, SUB, AND, OR. */
59
static instr_t instr_r(
60
    opcode_t opcode, funct3_t fn3, funct7_t fn7, reg_t rd, reg_t rs1, reg_t rs2
61
) {
62
    return (instr_t){ .r = { .opcode = opcode,
63
                             .rd     = rd,
64
                             .rs1    = rs1,
65
                             .rs2    = rs2,
66
                             .funct3 = fn3,
67
                             .funct7 = fn7 } };
68
}
69
70
/* Creates an S-type instruction struct.
71
 * Used for store instructions like SW, SH, SB. */
72
static instr_t instr_s(
73
    opcode_t opcode, funct3_t fn3, reg_t rs1, reg_t rs2, i32 imm
74
) {
75
    return (instr_t){ .s = { .opcode   = opcode,
76
                             .rs1      = rs1,
77
                             .rs2      = rs2,
78
                             .funct3   = fn3,
79
                             .imm_4_0  = (u32)imm & 0x1F,
80
                             .imm_11_5 = ((u32)imm >> 5) & 0x7F } };
81
}
82
83
/* Creates an SB-type (branch) instruction struct.
84
 * Modified S-type used for conditional branches like BEQ, BNE. */
85
static instr_t instr_sb(
86
    opcode_t opcode, funct3_t fn3, reg_t rs1, reg_t rs2, i32 imm
87
) {
88
    return (instr_t){ .b = { .opcode   = opcode,
89
                             .rs1      = rs1,
90
                             .rs2      = rs2,
91
                             .funct3   = fn3,
92
                             .imm_11   = (imm >> 11) & 0x1,
93
                             .imm_4_1  = (imm >> 1) & 0xF,
94
                             .imm_10_5 = (imm >> 5) & 0x3F,
95
                             .imm_12   = (imm >> 12) & 0x1 } };
96
}
97
98
/* Creates a UJ-type (jump) instruction struct.
99
 * Modified U-type used for JAL instruction. */
100
static instr_t instr_uj(opcode_t opcode, reg_t rd, i32 imm) {
101
    return (instr_t){ .j = { .opcode    = opcode,
102
                             .rd        = rd,
103
                             .imm_20    = (imm >> 20) & 0x1,
104
                             .imm_10_1  = (imm >> 1) & 0x3FF,
105
                             .imm_11    = (imm >> 11) & 0x1,
106
                             .imm_19_12 = (imm >> 12) & 0xFF } };
107
}
108
109
/* Instruction definitions table. */
110
/* Maps each instruction to its parameters for easy lookup. */
111
typedef struct {
112
    ifmt_t   type;    /* Instruction type */
113
    opcode_t opcode;  /* Opcode value */
114
    funct3_t funct3;  /* Function3 value (if applicable) */
115
    funct7_t funct7;  /* Function7 value (if applicable) */
116
    bool     special; /* Special handling flag */
117
} idef_t;
118
119
/* Instruction definition table */
120
static const idef_t idefs[] = {
121
    /* Upper immediate instructions. */
122
    [I_LUI]   = { IFMT_U, OP_LUI, 0, 0, true },
123
    [I_AUIPC] = { IFMT_U, OP_AUIPC, 0, 0, true },
124
    /* Jump instructions. */
125
    [I_JAL]  = { IFMT_J, OP_JAL, 0, 0, true },
126
    [I_JALR] = { IFMT_I, OP_JALR, 0, 0, false },
127
    /* Branch instructions. */
128
    [I_BEQ]  = { IFMT_B, OP_BRANCH, FUNCT3_BYTE, 0, false },
129
    [I_BNE]  = { IFMT_B, OP_BRANCH, FUNCT3_HALF, 0, false },
130
    [I_BLT]  = { IFMT_B, OP_BRANCH, FUNCT3_BYTE_U, 0, false },
131
    [I_BGE]  = { IFMT_B, OP_BRANCH, FUNCT3_HALF_U, 0, false },
132
    [I_BLTU] = { IFMT_B, OP_BRANCH, FUNCT3_OR, 0, false },
133
    [I_BGEU] = { IFMT_B, OP_BRANCH, FUNCT3_AND, 0, false },
134
    /* Load instructions. */
135
    [I_LB]  = { IFMT_I, OP_LOAD, FUNCT3_BYTE, 0, false },
136
    [I_LH]  = { IFMT_I, OP_LOAD, FUNCT3_HALF, 0, false },
137
    [I_LW]  = { IFMT_I, OP_LOAD, FUNCT3_WORD, 0, false },
138
    [I_LBU] = { IFMT_I, OP_LOAD, FUNCT3_BYTE_U, 0, false },
139
    [I_LHU] = { IFMT_I, OP_LOAD, FUNCT3_HALF_U, 0, false },
140
    /* Store instructions. */
141
    [I_SB] = { IFMT_S, OP_STORE, FUNCT3_BYTE, 0, false },
142
    [I_SH] = { IFMT_S, OP_STORE, FUNCT3_HALF, 0, false },
143
    [I_SW] = { IFMT_S, OP_STORE, FUNCT3_WORD, 0, false },
144
    /* ALU immediate operations. */
145
    [I_ADDI]  = { IFMT_I, OP_IMM, FUNCT3_ADD, 0, false },
146
    [I_SLTI]  = { IFMT_I, OP_IMM, FUNCT3_SLT, 0, false },
147
    [I_SLTIU] = { IFMT_I, OP_IMM, FUNCT3_SLTU, 0, false },
148
    [I_XORI]  = { IFMT_I, OP_IMM, FUNCT3_XOR, 0, false },
149
    [I_ORI]   = { IFMT_I, OP_IMM, FUNCT3_OR, 0, false },
150
    [I_ANDI]  = { IFMT_I, OP_IMM, FUNCT3_AND, 0, false },
151
    [I_SLLI]  = { IFMT_I, OP_IMM, FUNCT3_SLL, 0, true },
152
    [I_SRLI]  = { IFMT_I, OP_IMM, FUNCT3_SRL, 0, true },
153
    [I_SRAI]  = { IFMT_I, OP_IMM, FUNCT3_SRL, 0, true },
154
    /* ALU register operations. */
155
    [I_ADD]  = { IFMT_R, OP_OP, FUNCT3_ADD, FUNCT7_NORMAL, false },
156
    [I_SUB]  = { IFMT_R, OP_OP, FUNCT3_ADD, FUNCT7_SUB, false },
157
    [I_SLL]  = { IFMT_R, OP_OP, FUNCT3_SLL, FUNCT7_NORMAL, false },
158
    [I_SLT]  = { IFMT_R, OP_OP, FUNCT3_SLT, FUNCT7_NORMAL, false },
159
    [I_SLTU] = { IFMT_R, OP_OP, FUNCT3_SLTU, FUNCT7_NORMAL, false },
160
    [I_XOR]  = { IFMT_R, OP_OP, FUNCT3_XOR, FUNCT7_NORMAL, false },
161
    [I_SRL]  = { IFMT_R, OP_OP, FUNCT3_SRL, FUNCT7_NORMAL, false },
162
    [I_AND]  = { IFMT_R, OP_OP, FUNCT3_AND, FUNCT7_NORMAL, false },
163
    [I_OR]   = { IFMT_R, OP_OP, FUNCT3_OR, FUNCT7_NORMAL, false },
164
    /* M extension - multiply and divide. */
165
    [I_MUL]    = { IFMT_R, OP_OP, FUNCT3_ADD, FUNCT7_MUL, false },
166
    [I_MULH]   = { IFMT_R, OP_OP, FUNCT3_SLL, FUNCT7_MUL, false },
167
    [I_MULHSU] = { IFMT_R, OP_OP, FUNCT3_SLT, FUNCT7_MUL, false },
168
    [I_MULHU]  = { IFMT_R, OP_OP, FUNCT3_SLTU, FUNCT7_MUL, false },
169
    [I_DIV]    = { IFMT_R, OP_OP, FUNCT3_XOR, FUNCT7_MUL, false },
170
    [I_DIVU]   = { IFMT_R, OP_OP, FUNCT3_SRL, FUNCT7_MUL, false },
171
    [I_REM]    = { IFMT_R, OP_OP, FUNCT3_OR, FUNCT7_MUL, false },
172
    [I_REMU]   = { IFMT_R, OP_OP, FUNCT3_AND, FUNCT7_MUL, false },
173
    /* Pseudo-instructions. */
174
    [I_MV]  = { IFMT_I, OP_IMM, FUNCT3_ADD, 0, true },
175
    [I_JMP] = { IFMT_J, OP_JAL, 0, 0, true },
176
    [I_NOP] = { IFMT_I, OP_IMM, FUNCT3_ADD, 0, true },
177
    [I_NOT] = { IFMT_I, OP_IMM, FUNCT3_XOR, 0, true },
178
    [I_NEG] = { IFMT_R, OP_OP, FUNCT3_ADD, FUNCT7_SUB, true },
179
    /* System instructions */
180
    [I_EBREAK] = { IFMT_I, OP_SYSTEM, 0, 0, true },
181
    [I_ECALL]  = { IFMT_I, OP_SYSTEM, 0, 0, true },
182
    /* F Extension floating-point instructions */
183
    [I_FADD_S] = { IFMT_R, OP_OP_FP, FUNCT3_ADD, FUNCT7_FADD_S, false },
184
    [I_FSUB_S] = { IFMT_R, OP_OP_FP, FUNCT3_ADD, FUNCT7_FSUB_S, false },
185
    [I_FMUL_S] = { IFMT_R, OP_OP_FP, FUNCT3_ADD, FUNCT7_FMUL_S, false },
186
    [I_FDIV_S] = { IFMT_R, OP_OP_FP, FUNCT3_ADD, FUNCT7_FDIV_S, false },
187
    [I_FEQ_S]  = { IFMT_R, OP_OP_FP, FUNCT3_FEQ, FUNCT7_FEQ_S, false },
188
    [I_FLT_S]  = { IFMT_R, OP_OP_FP, FUNCT3_FLT, FUNCT7_FLT_S, false },
189
    [I_FLE_S]  = { IFMT_R, OP_OP_FP, FUNCT3_FLE, FUNCT7_FLE_S, false },
190
    [I_FLW]    = { IFMT_I, OP_LOAD_FP, FUNCT3_WORD_FP, 0, false },
191
    [I_FSW]    = { IFMT_S, OP_STORE_FP, FUNCT3_WORD_FP, 0, false },
192
    /* RV64I load/store */
193
    [I_LWU] = { IFMT_I, OP_LOAD, FUNCT3_WORD_U, 0, false },
194
    [I_LD]  = { IFMT_I, OP_LOAD, FUNCT3_DOUBLE, 0, false },
195
    [I_SD]  = { IFMT_S, OP_STORE, FUNCT3_DOUBLE, 0, false },
196
    /* RV64I immediate W-ops */
197
    [I_ADDIW] = { IFMT_I, OP_IMM_32, FUNCT3_ADD, 0, false },
198
    [I_SLLIW] = { IFMT_I, OP_IMM_32, FUNCT3_SLL, 0, true },
199
    [I_SRLIW] = { IFMT_I, OP_IMM_32, FUNCT3_SRL, 0, true },
200
    [I_SRAIW] = { IFMT_I, OP_IMM_32, FUNCT3_SRL, 0, true },
201
    /* RV64I register W-ops */
202
    [I_ADDW] = { IFMT_R, OP_OP_32, FUNCT3_ADD, FUNCT7_NORMAL, false },
203
    [I_SUBW] = { IFMT_R, OP_OP_32, FUNCT3_ADD, FUNCT7_SUB, false },
204
    [I_SLLW] = { IFMT_R, OP_OP_32, FUNCT3_SLL, FUNCT7_NORMAL, false },
205
    [I_SRLW] = { IFMT_R, OP_OP_32, FUNCT3_SRL, FUNCT7_NORMAL, false },
206
    [I_SRAW] = { IFMT_R, OP_OP_32, FUNCT3_SRL, FUNCT7_SRA, false },
207
    /* RV64M W-ops */
208
    [I_MULW]  = { IFMT_R, OP_OP_32, FUNCT3_ADD, FUNCT7_MUL, false },
209
    [I_DIVW]  = { IFMT_R, OP_OP_32, FUNCT3_XOR, FUNCT7_MUL, false },
210
    [I_DIVUW] = { IFMT_R, OP_OP_32, FUNCT3_SRL, FUNCT7_MUL, false },
211
    [I_REMW]  = { IFMT_R, OP_OP_32, FUNCT3_OR, FUNCT7_MUL, false },
212
    [I_REMUW] = { IFMT_R, OP_OP_32, FUNCT3_AND, FUNCT7_MUL, false },
213
};
214
215
/* Generates a RISC-V instruction based on the instruction definition */
216
instr_t instr(iname_t iop, reg_t rd, reg_t rs1, reg_t rs2, i32 imm) {
217
    const idef_t *def = &idefs[iop];
218
219
    /* Handle special cases that need specific processing. */
220
    if (def->special) {
221
        switch (iop) {
222
        case I_LUI:
223
            return instr_u(OP_LUI, rd, imm);
224
        case I_AUIPC:
225
            return instr_u(OP_AUIPC, rd, imm);
226
        case I_JAL:
227
            return instr_uj(OP_JAL, rd, imm);
228
        case I_SLLI:
229
            return instr_i(OP_IMM, FUNCT3_SLL, rd, rs1, imm & 0x3F);
230
        case I_SRLI:
231
            return instr_i(OP_IMM, FUNCT3_SRL, rd, rs1, imm & 0x3F);
232
        case I_SRAI:
233
            return instr_i(OP_IMM, FUNCT3_SRL, rd, rs1, (imm & 0x3F) + 0x400);
234
        case I_SLLIW:
235
            return instr_i(OP_IMM_32, FUNCT3_SLL, rd, rs1, imm & 0x1F);
236
        case I_SRLIW:
237
            return instr_i(OP_IMM_32, FUNCT3_SRL, rd, rs1, imm & 0x1F);
238
        case I_SRAIW:
239
            return instr_i(
240
                OP_IMM_32, FUNCT3_SRL, rd, rs1, (imm & 0x1F) + 0x400
241
            );
242
        case I_MV:
243
            return instr_i(OP_IMM, FUNCT3_ADD, rd, rs1, 0);
244
        case I_JMP:
245
            return instr_uj(OP_JAL, ZERO, imm);
246
        case I_NOP:
247
            return instr_i(OP_IMM, FUNCT3_ADD, ZERO, ZERO, 0);
248
        case I_NOT:
249
            return instr_i(OP_IMM, FUNCT3_XOR, rd, rs1, 1);
250
        case I_NEG:
251
            return instr_r(OP_OP, FUNCT3_ADD, FUNCT7_SUB, rd, ZERO, rs1);
252
        case I_EBREAK:
253
            /* EBREAK is encoded as all zeros except for the opcode */
254
            return instr_i(OP_SYSTEM, 0, 0, 0, 1);
255
        case I_ECALL:
256
            /* ECALL is encoded as all zeros including immediate */
257
            return instr_i(OP_SYSTEM, 0, 0, 0, 0);
258
        default:
259
            break;
260
        }
261
    }
262
263
    /* Regular instructions by type. */
264
    switch (def->type) {
265
    case IFMT_I:
266
        return instr_i(def->opcode, def->funct3, rd, rs1, imm);
267
    case IFMT_R:
268
        return instr_r(def->opcode, def->funct3, def->funct7, rd, rs1, rs2);
269
    case IFMT_S:
270
        return instr_s(def->opcode, def->funct3, rs1, rs2, imm);
271
    case IFMT_B:
272
        return instr_sb(def->opcode, def->funct3, rs1, rs2, imm);
273
    case IFMT_U:
274
        return instr_u(def->opcode, rd, imm);
275
    case IFMT_J:
276
        return instr_uj(def->opcode, rd, imm);
277
    default:
278
        abort();
279
    }
280
}