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