exec.c (10063B)
1 #include <stdint.h> 2 #include <inttypes.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 7 #include "exec.h" 8 #include "mem.h" 9 #include "decode.h" 10 #include "disasm.h" 11 #include "trap.h" 12 13 static inline void print_bin16(uint16_t x) { 14 for (int i = 15; i >= 0; i--) { 15 putchar((x >> i) & 1 ? '1' : '0'); 16 /*if (i % 4 == 0) putchar(' '); grouping */ 17 } 18 } 19 20 static inline void print_bin32(uint32_t x) { 21 for (int i = 31; i >= 0; i--) { 22 putchar((x >> i) & 1 ? '1' : '0'); 23 } 24 } 25 26 static inline void print_binless(uint32_t x, int a) { 27 for (int i = a-1; i >= 0; i--) { 28 putchar((x >> i) & 1 ? '1' : '0'); 29 } 30 } 31 32 static void 33 raise_trap(cpu_state_t *cpu, trap_cause_t trapinfo, uint32_t tval) 34 { 35 cpu->status = CPU_TRAPPED; 36 cpu->trap.cause = trapinfo; 37 cpu->trap.pc = cpu->pc; 38 cpu->trap.tval = tval; 39 return; 40 } 41 42 static inline trap_cause_t 43 validate_instruction_address(int64_t addr) 44 { 45 if (addr < 0 || addr > (int64_t)MEMSIZE - INT64_C(4)) 46 return TRAP_INSTRUCTION_ACCESS_FAULT; 47 48 if ((addr & INT64_C(3)) != 0) 49 return TRAP_INSTRUCTION_MISALIGNED; 50 51 return TRAP_NONE; 52 } 53 54 static trap_cause_t 55 get_valid_branch_target(uint32_t pc, int32_t offset, uint32_t *target) 56 { 57 int64_t candidate = (int64_t)pc + (int64_t)offset; 58 59 trap_cause_t valid = validate_instruction_address(candidate); 60 if (valid != TRAP_NONE) 61 return valid; 62 63 *target = (uint32_t)candidate; 64 return valid; 65 } 66 67 static trap_cause_t 68 get_valid_jalr_target(uint32_t base, int32_t offset, uint32_t *target) 69 { 70 int64_t candidate = ((int64_t)base + (int64_t)offset) 71 & ~INT64_C(1); 72 trap_cause_t valid = validate_instruction_address(candidate); 73 if (valid != TRAP_NONE) 74 return valid; 75 76 *target = (uint32_t)candidate; 77 return valid; 78 } 79 80 static void exec_itype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { 81 (void)mem; /* suppressing unused parameters warning */ 82 itype_t i; 83 decode_itype(&i, inst); 84 85 uint32_t x; uint32_t shamt; 86 uint32_t uimm = (uint32_t)i.imm; 87 switch(i.funct3) { 88 case 0x0: 89 x = cpu->x[i.rs1]+uimm; 90 break; 91 case 0x1: 92 if (i.funct7 == 0x00) { 93 shamt = i.imm & 31; 94 x = cpu->x[i.rs1] << shamt; 95 } else { 96 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 97 return; 98 } 99 break; 100 case 0x2: 101 x = ((int32_t)cpu->x[i.rs1] < (int32_t)i.imm) ? 1 : 0; 102 break; 103 case 0x3: 104 x = (cpu->x[i.rs1] < uimm) ? 1 : 0; 105 break; 106 case 0x4: 107 x = cpu->x[i.rs1]^uimm; 108 break; 109 case 0x5: 110 shamt = i.imm & 31; 111 switch(i.funct7) { 112 case 0x00: 113 x = cpu->x[i.rs1] >> shamt; 114 break; 115 case 0x20: 116 x = (uint32_t)((int32_t)cpu->x[i.rs1] >> shamt); 117 break; 118 default: 119 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 120 return; 121 } 122 break; 123 case 0x6: 124 x = cpu->x[i.rs1]|uimm; 125 break; 126 case 0x7: 127 x = cpu->x[i.rs1]&uimm; 128 break; 129 default: 130 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 131 return; 132 } 133 134 if (i.rd != 0) { 135 cpu->x[i.rd] = x; 136 } 137 138 cpu->pc += 4; 139 } 140 141 static void exec_rtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { 142 (void)mem; /* suppressing unused parameters warning */ 143 rtype_t r; 144 decode_rtype(&r, inst); 145 146 uint32_t x; 147 uint32_t shamt; 148 switch(r.funct3) { 149 case 0x0: 150 switch(r.funct7) { 151 case 0x00: 152 x = cpu->x[r.rs1]+cpu->x[r.rs2]; 153 break; 154 case 0x20: 155 x = cpu->x[r.rs1]-cpu->x[r.rs2]; 156 break; 157 default: 158 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 159 return; 160 } 161 break; 162 case 0x1: 163 shamt = cpu->x[r.rs2] & 31; 164 x = cpu->x[r.rs1] << shamt; 165 break; 166 case 0x2: 167 x = ((int32_t)cpu->x[r.rs1] < (int32_t)cpu->x[r.rs2]) ? 1 : 0; 168 break; 169 case 0x3: 170 x = (cpu->x[r.rs1] < cpu->x[r.rs2]) ? 1 : 0; 171 break; 172 case 0x4: 173 x = cpu->x[r.rs1]^cpu->x[r.rs2]; 174 break; 175 case 0x5: 176 switch(r.funct7) { 177 case 0x00: 178 shamt = cpu->x[r.rs2] & 31; 179 x = cpu->x[r.rs1] >> shamt; 180 break; 181 case 0x20: 182 shamt = cpu->x[r.rs2] & 31; 183 x = (uint32_t)((int32_t)cpu->x[r.rs1] >> shamt); 184 break; 185 default: 186 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 187 return; 188 } 189 break; 190 case 0x6: 191 x = cpu->x[r.rs1]|cpu->x[r.rs2]; 192 break; 193 case 0x7: 194 x = cpu->x[r.rs1]&cpu->x[r.rs2]; 195 break; 196 default: 197 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 198 return; 199 } 200 201 if (r.rd != 0) { 202 cpu->x[r.rd] = x; 203 } 204 205 cpu->pc += 4; 206 } 207 208 static void exec_itype_loads(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { 209 itype_t i; 210 decode_itype(&i, inst); 211 212 uint32_t addr = (uint32_t)i.imm + cpu->x[i.rs1]; 213 214 /* TODO: do I need to check before loading halves and words */ 215 216 uint32_t x; 217 switch(i.funct3) { 218 case 0x0: 219 x = (uint32_t)(int32_t)(int8_t)mem[addr]; 220 break; 221 case 0x1: 222 uint16_t half = (uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8); 223 x = (uint32_t)(int32_t)(int16_t)half; 224 break; 225 case 0x2: 226 /* TODO: clean up */ 227 x = (uint32_t)(int32_t)( 228 ((uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8)) 229 | 230 (((uint16_t)mem[addr+2] | ((uint16_t)mem[addr+3] << 8)) 231 << 16) 232 ); 233 break; 234 case 0x4: 235 x = (uint32_t)(uint8_t)mem[addr]; 236 break; 237 238 case 0x5: 239 int16_t uhalf = (int16_t)mem[addr] | ((int16_t)mem[addr+1] << 8); 240 x = (uint32_t)(int32_t)uhalf; 241 break; 242 default: 243 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 244 return; 245 } 246 247 if (i.rd != 0) { 248 cpu->x[i.rd] = x; 249 } 250 cpu->pc += 4; 251 } 252 253 static void exec_stype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { 254 stype_t s; 255 decode_stype(&s, inst); 256 257 uint32_t addr = s.imm + cpu->x[s.rs1]; 258 259 /* TODO: helpers, better checks */ 260 switch(s.funct3) { 261 case 0x0: 262 mem[addr] = cpu->x[s.rs2] & 255; 263 break; 264 case 0x1: 265 if (addr+1 > MEMSIZE-1) { 266 fprintf(stderr, "Illegal addr\n"); 267 return; 268 } 269 270 if (addr % 2 == 0) { 271 mem[addr] = (uint8_t)cpu->x[s.rs2]; 272 mem[addr+1] = (uint8_t)cpu->x[s.rs2] >> 8; 273 } 274 break; 275 case 0x2: 276 if (addr+3 > MEMSIZE-1) { 277 fprintf(stderr, "Illegal addr\n"); 278 return; 279 } 280 if (addr % 4 == 0) { 281 mem[addr] = (uint8_t)cpu->x[s.rs2]; 282 mem[addr+1] = (uint8_t)cpu->x[s.rs2] >> 8; 283 mem[addr+2] = (uint8_t)cpu->x[s.rs2] >> 16; 284 mem[addr+3] = (uint8_t)cpu->x[s.rs2] >> 24; 285 } 286 break; 287 default: 288 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 289 return; 290 } 291 292 cpu->pc += 4; 293 } 294 295 static void exec_btype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { 296 (void)mem; /* suppressing unused parameters warning */ 297 btype_t b; 298 decode_btype(&b, inst); 299 300 int jump = 0; 301 switch (b.funct3) { 302 case 0x0: 303 if (cpu->x[b.rs1] == cpu->x[b.rs2]) 304 jump = 1; 305 break; 306 case 0x1: 307 if (cpu->x[b.rs1] != cpu->x[b.rs2]) 308 jump = 1; 309 break; 310 case 0x4: 311 if ((int32_t)cpu->x[b.rs1] < (int32_t)cpu->x[b.rs2]) 312 jump = 1; 313 break; 314 case 0x5: 315 if ((int32_t)cpu->x[b.rs1] >= (int32_t)cpu->x[b.rs2]) 316 jump = 1; 317 break; 318 case 0x6: 319 if (cpu->x[b.rs1] < cpu->x[b.rs2]) 320 jump = 1; 321 break; 322 case 0x7: 323 if (cpu->x[b.rs1] >= cpu->x[b.rs2]) 324 jump = 1; 325 break; 326 default: 327 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 328 return; 329 } 330 331 if (jump == 1) { 332 uint32_t target; 333 trap_cause_t cause = get_valid_branch_target(cpu->pc, 334 b.imm, 335 &target); 336 if (cause != TRAP_NONE) { 337 raise_trap(cpu, cause, inst); 338 return; 339 } 340 cpu->pc = target; 341 } else { 342 cpu->pc += 4; 343 } 344 345 } 346 347 static void exec_itype_jalr(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { 348 (void)mem; /* suppressing unused parameters warning */ 349 itype_t i; 350 decode_itype(&i, inst); 351 352 uint32_t x; 353 if (i.funct3 != 0x0) { 354 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 355 return; 356 } 357 358 x = cpu->pc + 4; 359 360 uint32_t target; 361 trap_cause_t cause = get_valid_jalr_target(cpu->x[i.rs1], i.imm, 362 &target); 363 if (cause != TRAP_NONE) { 364 raise_trap(cpu, cause, inst); 365 return; 366 } 367 368 cpu->pc = target; 369 370 if (i.rd != 0) { 371 cpu->x[i.rd] = x; 372 } 373 } 374 375 static void exec_jtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { 376 (void)mem; /* suppressing unused parameters warning */ 377 jtype_t j; 378 decode_jtype(&j, inst); 379 380 uint32_t x; 381 x = cpu->pc + 4; 382 cpu->pc += j.imm; 383 384 uint32_t target; 385 trap_cause_t cause = get_valid_branch_target(cpu->pc, 386 (int32_t)j.imm, 387 &target); 388 if (cause != TRAP_NONE) { 389 raise_trap(cpu, cause, inst); 390 return; 391 } 392 393 cpu->pc = target; 394 395 if (j.rd != 0) { 396 cpu->x[j.rd] = x; 397 } 398 } 399 400 static void 401 exec_utype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) 402 { 403 (void)mem; /* suppressing unused parameters warning */ 404 utype_t u; 405 decode_utype(&u, inst); 406 407 if (u.rd != 0) 408 cpu->x[u.rd] = u.imm << 12; 409 410 cpu->pc += 4; 411 } 412 413 static void 414 exec_utype_auipc(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) 415 { 416 (void)mem; /* suppressing unused parameters warning */ 417 utype_t u; 418 decode_utype(&u, inst); 419 420 if (u.rd != 0) 421 cpu->x[u.rd] = cpu->pc + (u.imm << 12); 422 423 cpu->pc += 4; 424 } 425 426 static void 427 exec_itype_control(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) 428 { 429 (void)mem; /* suppressing unused parameters warning */ 430 itype_t i; 431 decode_itype(&i, inst); 432 433 if (i.funct3 != 0x0) { 434 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 435 return; 436 } 437 438 if (i.imm == 0x0) { 439 raise_trap(cpu, TRAP_ECALL, inst); 440 return; 441 } else if (i.imm == 0x1) { 442 raise_trap(cpu, TRAP_EBREAK, inst); 443 return; 444 } else { 445 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 446 return; 447 } 448 449 } 450 451 static void 452 exec_itype_fence(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) 453 { 454 (void)mem; /* suppressing unused parameters warning */ 455 itype_t i; 456 decode_itype(&i, inst); 457 458 if (i.funct3 != 0x0) { 459 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 460 return; 461 } 462 463 cpu->pc += 4; 464 } 465 466 /* Function pointer dispatch */ 467 typedef void (*exec_fn)(cpu_state_t *, uint32_t inst, uint8_t *mem); 468 469 static exec_fn opcode_table[128] = { 470 [0x33] = exec_rtype, 471 [0x13] = exec_itype, 472 [0x03] = exec_itype_loads, 473 [0x67] = exec_itype_jalr, 474 [0x73] = exec_itype_control, 475 [0x0f] = exec_itype_fence, 476 [0x23] = exec_stype, 477 [0x63] = exec_btype, 478 [0x6f] = exec_jtype, 479 [0x37] = exec_utype, 480 [0x17] = exec_utype_auipc 481 }; 482 /***/ 483 484 void 485 cpu_step(cpu_state_t *cpu, uint8_t *mem) 486 { 487 uint32_t inst = load_u32_le(mem, cpu->pc); /* load little endian */ 488 489 /* decode opcode */ 490 uint16_t opcode = inst & 127; 491 492 exec_fn fn = opcode_table[opcode]; 493 494 if (!fn) { 495 raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); 496 return; 497 } 498 499 fn(cpu,inst,mem); 500 }