rvsim.c (10494B)
1 // Copyright 2019, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0. 3 4 #include <stdio.h> 5 #include <stdint.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include "riscv.h" 10 #include "rvsim.h" 11 12 #define DO_TRACE_INS 0 13 #define DO_TRACE_TRAPS 0 14 #define DO_TRACE_MEM_WR 0 15 #define DO_TRACE_REG_WR 0 16 17 #define DO_ABORT_INVAL 0 18 19 #define RVMEMBASE 0x80000000 20 #define RVMEMSIZE 0x01000000 21 #define RVMEMMASK (RVMEMSIZE - 1) 22 23 typedef struct rvstate { 24 uint32_t x[32]; 25 void* memory; 26 uint32_t mscratch; 27 uint32_t mtvec; 28 uint32_t mtval; 29 uint32_t mepc; 30 uint32_t mcause; 31 void* ctx; 32 } rvstate_t; 33 34 void* rvsim_dma(rvstate_t* s, uint32_t va, uint32_t len) { 35 if (va < RVMEMBASE) return NULL; 36 va -= RVMEMBASE; 37 if (va >= RVMEMSIZE) return NULL; 38 if (len > (RVMEMSIZE - va)) return NULL; 39 return s->memory + va; 40 } 41 42 static uint32_t rd32(uint8_t* memory, uint32_t addr) { 43 if (addr < RVMEMBASE) { 44 return ior32(addr); 45 } else { 46 addr &= RVMEMMASK; 47 return ((uint32_t*) memory)[addr >> 2]; 48 } 49 } 50 static void wr32(uint8_t* memory, uint32_t addr, uint32_t val) { 51 if (addr < RVMEMBASE) { 52 iow32(addr, val); 53 } else { 54 addr &= RVMEMMASK; 55 ((uint32_t*) memory)[addr >> 2] = val; 56 } 57 } 58 static uint32_t rd16(uint8_t* memory, uint32_t addr) { 59 if (addr < RVMEMBASE) { 60 return 0xffff; 61 } else { 62 addr &= RVMEMMASK; 63 return ((uint16_t*) memory)[addr >> 1]; 64 } 65 } 66 static void wr16(uint8_t* memory, uint32_t addr, uint32_t val) { 67 if (addr >= RVMEMBASE) { 68 addr &= RVMEMMASK; 69 ((uint16_t*) memory)[addr >> 1] = val; 70 } 71 } 72 static uint32_t rd8(uint8_t* memory, uint32_t addr) { 73 if (addr < RVMEMBASE) { 74 return 0xff; 75 } else { 76 addr &= RVMEMMASK; 77 return memory[addr]; 78 } 79 } 80 static void wr8(uint8_t* memory, uint32_t addr, uint32_t val) { 81 if (addr >= RVMEMBASE) { 82 addr &= RVMEMMASK; 83 memory[addr] = val; 84 } 85 } 86 87 uint32_t rvsim_rd32(rvstate_t* s, uint32_t addr) { 88 return rd32(s->memory, addr); 89 } 90 91 int rvsim_init(rvstate_t** _s, void* ctx) { 92 rvstate_t *s; 93 if ((s = malloc(sizeof(rvstate_t))) == NULL) { 94 return -1; 95 } 96 memset(s, 0, sizeof(rvstate_t)); 97 if ((s->memory = malloc(RVMEMSIZE)) == NULL) { 98 free(s); 99 return -1; 100 } 101 memset(s->memory, 0, RVMEMSIZE); 102 s->mtvec = 0x80000000; 103 s->ctx = ctx ? ctx : s; 104 *_s = s; 105 return 0; 106 } 107 108 static inline uint32_t rreg(rvstate_t* s, uint32_t n) { 109 return n ? s->x[n] : 0; 110 } 111 static inline void wreg(rvstate_t* s, uint32_t n, uint32_t v) { 112 s->x[n] = v; 113 } 114 115 static void put_csr(rvstate_t* s, uint32_t csr, uint32_t v) { 116 switch (csr) { 117 case CSR_MSCRATCH: s->mscratch = v; break; 118 case CSR_MTVEC: s->mtvec = v & 0xFFFFFFFC; break; 119 case CSR_MTVAL: s->mtval = v; break; 120 case CSR_MEPC: s->mepc = v; break; 121 case CSR_MCAUSE: s->mcause = v; break; 122 } 123 } 124 static uint32_t get_csr(rvstate_t* s, uint32_t csr) { 125 switch (csr) { 126 case CSR_MISA: return 0x40000100; // RV32I 127 case CSR_MVENDORID: return 0; // NONE 128 case CSR_MARCHID: return 0; // NONE 129 case CSR_MIMPID: return 0; // NONE 130 case CSR_MHARTID: return 0; // Thread Zero 131 case CSR_MSCRATCH: return s->mscratch; 132 case CSR_MTVEC: return s->mtvec; 133 case CSR_MTVAL: return s->mtval; 134 case CSR_MEPC: return s->mepc; 135 case CSR_MCAUSE: return s->mcause; 136 default: 137 return 0; 138 } 139 } 140 141 #define RdR1() rreg(s, get_r1(ins)) 142 #define RdR2() rreg(s, get_r2(ins)) 143 #define RdRd() rreg(s, get_rd(ins)) 144 #define WrRd(v) wreg(s, get_rd(ins), v) 145 146 #if DO_TRACE_REG_WR 147 #define trace_reg_wr(v) do {\ 148 uint32_t r = get_rd(ins); \ 149 if (r) { \ 150 fprintf(stderr, " (%s = %08x)\n", \ 151 rvregname(get_rd(ins)), v); \ 152 }} while (0) 153 #else 154 #define trace_reg_wr(v) do {} while (0) 155 #endif 156 157 #if DO_TRACE_MEM_WR 158 #define trace_mem_wr(a, v) do {\ 159 fprintf(stderr, " ([%08x] = %08x)\n", a, v);\ 160 } while (0) 161 #else 162 #define trace_mem_wr(a, v) do {} while (0) 163 #endif 164 165 int rvsim_exec(rvstate_t* s, uint32_t _pc) { 166 uint32_t pc = _pc; 167 uint32_t next = _pc; 168 uint32_t ins; 169 uint64_t ccount = 0; 170 for (;;) { 171 ccount++; 172 pc = next; 173 ins = rd32(s->memory, pc); 174 #if DO_TRACE_INS 175 char dis[128]; 176 rvdis(pc, ins, dis); 177 fprintf(stderr, "%08x: %08x %s\n", pc, ins, dis); 178 #endif 179 next = pc + 4; 180 switch (get_oc(ins)) { 181 case OC_LOAD: { 182 uint32_t a = RdR1() + get_ii(ins); 183 uint32_t v; 184 switch (get_fn3(ins)) { 185 case F3_LW: 186 if (a & 3) goto trap_load_align; 187 v = rd32(s->memory, a); break; 188 case F3_LHU: 189 if (a & 1) goto trap_load_align; 190 v = rd16(s->memory, a); break; 191 case F3_LBU: 192 v = rd8(s->memory, a); break; 193 case F3_LH: 194 if (a & 1) goto trap_load_align; 195 v = rd16(s->memory, a); 196 if (v & 0x8000) { v |= 0xFFFF0000; } break; 197 case F3_LB: 198 v = rd8(s->memory, a); 199 if (v & 0x80) { v |= 0xFFFFFF00; } break; 200 default: 201 goto inval; 202 } 203 WrRd(v); 204 trace_reg_wr(v); 205 break; 206 trap_load_align: 207 s->mcause = EC_L_ALIGN; 208 s->mtval = a; 209 goto trap_common; 210 } 211 case OC_CUSTOM_0: 212 switch (get_fn3(ins)) { 213 case 0b000: // _exiti 214 fprintf(stderr, "CCOUNT %lu\n", ccount); 215 return get_ii(ins); 216 case 0b100: // _exit 217 fprintf(stderr, "CCOUNT %lu\n", ccount); 218 return RdR1(); 219 case 0b001: // _iocall 220 s->x[10] = iocall(s->ctx, get_ii(ins), s->x + 10); 221 break; 222 default: 223 goto inval; 224 } 225 break; 226 case OC_MISC_MEM: 227 switch (get_fn3(ins)) { 228 case F3_FENCE: 229 case F3_FENCE_I: 230 break; 231 default: 232 goto inval; 233 } 234 break; 235 case OC_OP_IMM: { 236 uint32_t a = RdR1(); 237 uint32_t b = get_ii(ins); 238 uint32_t n = 0xe1e1e1e1; 239 switch (get_fn3(ins)) { 240 case F3_ADDI: n = a + b; break; 241 case F3_SLLI: 242 if (b & 0b111111100000) goto inval; 243 n = a << b; break; 244 case F3_SLTI: n = ((int32_t)a) < ((int32_t)b); break; 245 case F3_SLTIU: n = a < b; break; 246 case F3_XORI: n = a ^ b; break; 247 case F3_SRLI: 248 if (b & 0b101111100000) goto inval; 249 if (b & 0b010000000000) { 250 n = ((int32_t)a) >> b; 251 } else { 252 n = a >> b; 253 } 254 break; 255 case F3_ORI: n = a | b; break; 256 case F3_ANDI: n = a & b; break; 257 } 258 WrRd(n); 259 trace_reg_wr(n); 260 break; 261 } 262 case OC_AUIPC: { 263 uint32_t v = pc + get_iu(ins); 264 WrRd(v); 265 trace_reg_wr(v); 266 break; 267 } 268 case OC_STORE: { 269 uint32_t a = RdR1() + get_is(ins); 270 uint32_t v = RdR2(); 271 switch (get_fn3(ins)) { 272 case F3_SW: 273 if (a & 3) goto trap_store_align; 274 wr32(s->memory, a, v); break; 275 case F3_SH: 276 if (a & 1) goto trap_store_align; 277 wr16(s->memory, a, v); break; 278 case F3_SB: 279 wr8(s->memory, a, v); break; 280 default: 281 goto inval; 282 } 283 trace_mem_wr(a, v); 284 break; 285 trap_store_align: 286 s->mcause = EC_S_ALIGN; 287 s->mtval = a; 288 goto trap_common; 289 } 290 case OC_OP: { 291 uint32_t a = RdR1(); 292 uint32_t b = RdR2(); 293 uint32_t n = get_fn3(ins); 294 switch (ins >> 25) { 295 case 0b0000000: 296 switch (n) { 297 case F3_ADD: n = a + b; break; 298 case F3_SLL: n = a << (b & 31); break; 299 case F3_SLT: n = ((int32_t)a) < ((int32_t)b); break; 300 case F3_SLTU: n = a < b; break; 301 case F3_XOR: n = a ^ b; break; 302 case F3_SRL: n = a >> (b & 31); break; 303 case F3_OR: n = a | b; break; 304 case F3_AND: n = a & b; break; 305 } 306 break; 307 case 0b0000001: 308 switch (n) { 309 case F3_MUL: n = a * b; break; 310 case F3_MULH: n = ((int64_t)(int32_t)a * (int64_t)(int32_t)b) >> 32; break; 311 case F3_MULHSU: n = ((int64_t)(int32_t)a * (uint64_t)b) >> 32; break; 312 case F3_MULHU: n = ((uint64_t)a * (uint64_t)b) >> 32; break; 313 case F3_DIV: 314 if (b == 0) { n = 0xffffffff; } 315 else if ((a == 0x80000000) && (b == 0xffffffff)) { n = a; } 316 else { n = ((int32_t)a / (int32_t)b); } 317 break; 318 case F3_DIVU: 319 if (b == 0) { n = 0xffffffff; } 320 else { n = a / b; } 321 break; 322 case F3_REM: 323 if (b == 0) { n = a; } 324 else if ((a == 0x80000000) && (b == 0xffffffff)) { n = 0; } 325 else { n = ((int32_t)a % (int32_t)b); } 326 break; 327 case F3_REMU: 328 if (b == 0) { n = a; } 329 else { n = a % b; } 330 break; 331 } 332 break; 333 case 0b0100000: 334 switch (n) { 335 case F3_SUB: n = a - b; break; 336 case F3_SRA: n = ((int32_t)a) >> (b & 31); break; 337 default: goto inval; 338 } 339 break; 340 } 341 WrRd(n); 342 trace_reg_wr(n); 343 break; 344 } 345 case OC_LUI: { 346 uint32_t v = get_iu(ins); 347 WrRd(v); 348 trace_reg_wr(v); 349 break; 350 } 351 case OC_BRANCH: { 352 uint32_t a = RdR1(); 353 uint32_t b = RdR2(); 354 int32_t p; 355 switch (get_fn3(ins)) { 356 case F3_BEQ: p = (a == b); break; 357 case F3_BNE: p = (a != b); break; 358 case F3_BLT: p = (((int32_t)a) < ((int32_t)b)); break; 359 case F3_BGE: p = (((int32_t)a) >= ((int32_t)b)); break; 360 case F3_BLTU: p = (a < b); break; 361 case F3_BGEU: p = (a >= b); break; 362 default: 363 goto inval; 364 } 365 if (p) { 366 next = pc + get_ib(ins); 367 if (next & 3) goto trap_pc_align; 368 } 369 break; 370 } 371 case OC_JALR: { 372 uint32_t a = (RdR1() + get_ii(ins)) & 0xFFFFFFFE; 373 if (get_fn3(ins) != 0) goto inval; 374 WrRd(next); 375 trace_reg_wr(next); 376 next = a; 377 if (next & 3) goto trap_pc_align; 378 break; 379 } 380 case OC_JAL: 381 WrRd(next); 382 trace_reg_wr(next); 383 next = pc + get_ij(ins); 384 if (next & 3) goto trap_pc_align; 385 break; 386 case OC_SYSTEM: { 387 uint32_t fn = get_fn3(ins); 388 if (fn == 0) { 389 switch(ins >> 7) { 390 case 0b0000000000000000000000000: // ecall 391 s->mcause = EC_ECALL_FROM_M; 392 s->mtval = 0; 393 goto trap_common; 394 case 0b0000000000010000000000000: // ebreak 395 s->mcause = EC_BREAKPOINT; 396 s->mtval = 0; 397 goto trap_common; 398 case 0b0011000000100000000000000: // mret 399 next = s->mepc; 400 break; 401 default: 402 goto inval; 403 } 404 break; 405 } 406 uint32_t c = get_iC(ins); 407 uint32_t nv = (fn & 4) ? get_ic(ins) : RdR1(); 408 uint32_t ov = 0; 409 switch (fn & 3) { 410 case F3_CSRRW: 411 // only reads of Rd != x0 412 if (get_rd(ins)) ov = get_csr(s, c); 413 put_csr(s, c, nv); 414 break; 415 case F3_CSRRS: 416 // only writes if nv != 0 417 ov = get_csr(s, c); 418 if (nv) put_csr(s, c, ov | nv); 419 WrRd(ov); 420 break; 421 case F3_CSRRC: 422 // only writes if nv != 0 423 ov = get_csr(s, c); 424 if (nv) put_csr(s, c, ov & (~nv)); 425 break; 426 } 427 WrRd(ov); 428 break; 429 } 430 trap_pc_align: 431 s->mcause = EC_I_ALIGN; 432 s->mtval = next; 433 goto trap_common; 434 default: 435 inval: 436 s->mcause = EC_I_ILLEGAL; 437 s->mtval = ins; 438 #if DO_ABORT_INVAL 439 fprintf(stderr," (TRAP ILLEGAL %08x)\n", ins); 440 return -1; 441 #endif 442 trap_common: 443 s->mepc = pc; 444 next = s->mtvec & 0xFFFFFFFD; 445 #if DO_TRACE_TRAPS 446 fprintf(stderr, " (TRAP C=%08x V=%08x)\n", s->mcause, s->mtval); 447 #endif 448 break; 449 } 450 } 451 } 452