/* * RISCV emulator * * Copyright (c) 2016 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #if XLEN == 32 #define uintx_t uint32_t #define intx_t int32_t #elif XLEN == 64 #define uintx_t uint64_t #define intx_t int64_t #elif XLEN == 128 #define uintx_t uint128_t #define intx_t int128_t #else #error unsupported XLEN #endif static inline intx_t glue(div, XLEN)(intx_t a, intx_t b) { if (b == 0) { return -1; } else if (a == ((intx_t)1 << (XLEN - 1)) && b == -1) { return a; } else { return a / b; } } static inline uintx_t glue(divu, XLEN)(uintx_t a, uintx_t b) { if (b == 0) { return -1; } else { return a / b; } } static inline intx_t glue(rem, XLEN)(intx_t a, intx_t b) { if (b == 0) { return a; } else if (a == ((intx_t)1 << (XLEN - 1)) && b == -1) { return 0; } else { return a % b; } } static inline uintx_t glue(remu, XLEN)(uintx_t a, uintx_t b) { if (b == 0) { return a; } else { return a % b; } } #if XLEN == 32 static inline uint32_t mulh32(int32_t a, int32_t b) { return ((int64_t)a * (int64_t)b) >> 32; } static inline uint32_t mulhsu32(int32_t a, uint32_t b) { return ((int64_t)a * (int64_t)b) >> 32; } static inline uint32_t mulhu32(uint32_t a, uint32_t b) { return ((int64_t)a * (int64_t)b) >> 32; } #elif XLEN == 64 && defined(HAVE_INT128) static inline uint64_t mulh64(int64_t a, int64_t b) { return ((int128_t)a * (int128_t)b) >> 64; } static inline uint64_t mulhsu64(int64_t a, uint64_t b) { return ((int128_t)a * (int128_t)b) >> 64; } static inline uint64_t mulhu64(uint64_t a, uint64_t b) { return ((int128_t)a * (int128_t)b) >> 64; } #else #if XLEN == 64 #define UHALF uint32_t #define UHALF_LEN 32 #elif XLEN == 128 #define UHALF uint64_t #define UHALF_LEN 64 #else #error unsupported XLEN #endif static uintx_t glue(mulhu, XLEN)(uintx_t a, uintx_t b) { UHALF a0, a1, b0, b1, r2, r3; uintx_t r00, r01, r10, r11, c; a0 = a; a1 = a >> UHALF_LEN; b0 = b; b1 = b >> UHALF_LEN; r00 = (uintx_t)a0 * (uintx_t)b0; r01 = (uintx_t)a0 * (uintx_t)b1; r10 = (uintx_t)a1 * (uintx_t)b0; r11 = (uintx_t)a1 * (uintx_t)b1; // r0 = r00; c = (r00 >> UHALF_LEN) + (UHALF)r01 + (UHALF)r10; // r1 = c; c = (c >> UHALF_LEN) + (r01 >> UHALF_LEN) + (r10 >> UHALF_LEN) + (UHALF)r11; r2 = c; r3 = (c >> UHALF_LEN) + (r11 >> UHALF_LEN); // *plow = ((uintx_t)r1 << UHALF_LEN) | r0; return ((uintx_t)r3 << UHALF_LEN) | r2; } #undef UHALF static inline uintx_t glue(mulh, XLEN)(intx_t a, intx_t b) { uintx_t r1; r1 = glue(mulhu, XLEN)(a, b); if (a < 0) r1 -= a; if (b < 0) r1 -= b; return r1; } static inline uintx_t glue(mulhsu, XLEN)(intx_t a, uintx_t b) { uintx_t r1; r1 = glue(mulhu, XLEN)(a, b); if (a < 0) r1 -= a; return r1; } #endif #define DUP2(F, n) F(n) F(n+1) #define DUP4(F, n) DUP2(F, n) DUP2(F, n + 2) #define DUP8(F, n) DUP4(F, n) DUP4(F, n + 4) #define DUP16(F, n) DUP8(F, n) DUP8(F, n + 8) #define DUP32(F, n) DUP16(F, n) DUP16(F, n + 16) #define C_QUADRANT(n) \ case n+(0 << 2): case n+(1 << 2): case n+(2 << 2): case n+(3 << 2): \ case n+(4 << 2): case n+(5 << 2): case n+(6 << 2): case n+(7 << 2): \ case n+(8 << 2): case n+(9 << 2): case n+(10 << 2): case n+(11 << 2): \ case n+(12 << 2): case n+(13 << 2): case n+(14 << 2): case n+(15 << 2): \ case n+(16 << 2): case n+(17 << 2): case n+(18 << 2): case n+(19 << 2): \ case n+(20 << 2): case n+(21 << 2): case n+(22 << 2): case n+(23 << 2): \ case n+(24 << 2): case n+(25 << 2): case n+(26 << 2): case n+(27 << 2): \ case n+(28 << 2): case n+(29 << 2): case n+(30 << 2): case n+(31 << 2): #define GET_PC() (target_ulong)((uintptr_t)code_ptr + code_to_pc_addend) #define GET_INSN_COUNTER() (insn_counter_addend - s->n_cycles) #define C_NEXT_INSN code_ptr += 2; break #define NEXT_INSN code_ptr += 4; break #define JUMP_INSN do { \ code_ptr = NULL; \ code_end = NULL; \ code_to_pc_addend = s->pc; \ goto jump_insn; \ } while (0) static void no_inline glue(riscv_cpu_interp_x, XLEN)(RISCVCPUState *s, int n_cycles1) { uint32_t opcode, insn, rd, rs1, rs2, funct3; int32_t imm, cond, err; target_ulong addr, val, val2; #ifndef USE_GLOBAL_VARIABLES uint8_t *code_ptr, *code_end; target_ulong code_to_pc_addend; #endif uint64_t insn_counter_addend; #if FLEN > 0 uint32_t rs3; int32_t rm; #endif if (n_cycles1 == 0) return; insn_counter_addend = s->insn_counter + n_cycles1; s->n_cycles = n_cycles1; /* check pending interrupts */ if (unlikely((s->mip & s->mie) != 0)) { if (raise_interrupt(s)) { s->n_cycles--; goto done_interp; } } s->pending_exception = -1; /* Note: we assume NULL is represented as a zero number */ code_ptr = NULL; code_end = NULL; code_to_pc_addend = s->pc; /* we use a single execution loop to keep a simple control flow for emscripten */ for(;;) { if (unlikely(code_ptr >= code_end)) { uint32_t tlb_idx; uint16_t insn_high; target_ulong addr; uint8_t *ptr; s->pc = GET_PC(); /* we test n_cycles only between blocks so that timer interrupts only happen between the blocks. It is important to reduce the translated code size. */ if (unlikely(s->n_cycles <= 0)) goto the_end; /* check pending interrupts */ if (unlikely((s->mip & s->mie) != 0)) { if (raise_interrupt(s)) { s->n_cycles--; goto the_end; } } addr = s->pc; tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1); if (likely(s->tlb_code[tlb_idx].vaddr == (addr & ~PG_MASK))) { /* TLB match */ ptr = (uint8_t *)(s->tlb_code[tlb_idx].mem_addend + (uintptr_t)addr); } else { if (unlikely(target_read_insn_slow(s, &ptr, addr))) goto mmu_exception; } code_ptr = ptr; code_end = ptr + (PG_MASK - 1 - (addr & PG_MASK)); code_to_pc_addend = addr - (uintptr_t)code_ptr; if (unlikely(code_ptr >= code_end)) { /* instruction is potentially half way between two pages ? */ insn = *(uint16_t *)code_ptr; if ((insn & 3) == 3) { /* instruction is half way between two pages */ if (unlikely(target_read_insn_u16(s, &insn_high, addr + 2))) goto mmu_exception; insn |= insn_high << 16; } } else { insn = get_insn32(code_ptr); } } else { /* fast path */ insn = get_insn32(code_ptr); } s->n_cycles--; #if 0 if (1) { #ifdef CONFIG_LOGFILE log_printf("pc=0x"); fprint_target_ulong(log_file, GET_PC()); log_printf(" insn=%08x\n", insn); fflush(log_file); #else printf("pc=0x"); print_target_ulong(GET_PC()); printf(" insn=%08x\n", insn); // dump_regs(s); #endif } #endif opcode = insn & 0x7f; rd = (insn >> 7) & 0x1f; rs1 = (insn >> 15) & 0x1f; rs2 = (insn >> 20) & 0x1f; switch(opcode) { #ifdef CONFIG_EXT_C C_QUADRANT(0) funct3 = (insn >> 13) & 7; rd = ((insn >> 2) & 7) | 8; switch(funct3) { case 0: /* c.addi4spn */ imm = get_field1(insn, 11, 4, 5) | get_field1(insn, 7, 6, 9) | get_field1(insn, 6, 2, 2) | get_field1(insn, 5, 3, 3); if (imm == 0) goto illegal_insn; s->reg[rd] = (intx_t)(s->reg[2] + imm); break; #if XLEN >= 128 case 1: /* c.lq */ imm = get_field1(insn, 11, 4, 5) | get_field1(insn, 10, 8, 8) | get_field1(insn, 5, 6, 7); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); if (target_read_u128(s, &val, addr)) goto mmu_exception; s->reg[rd] = val; break; #elif FLEN >= 64 case 1: /* c.fld */ { uint64_t rval; if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 5, 6, 7); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); if (target_read_u64(s, &rval, addr)) goto mmu_exception; s->fp_reg[rd] = rval | F64_HIGH; s->fs = 3; } break; #endif case 2: /* c.lw */ { uint32_t rval; imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 6, 2, 2) | get_field1(insn, 5, 6, 6); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); if (target_read_u32(s, &rval, addr)) goto mmu_exception; s->reg[rd] = (int32_t)rval; } break; #if XLEN >= 64 case 3: /* c.ld */ { uint64_t rval; imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 5, 6, 7); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); if (target_read_u64(s, &rval, addr)) goto mmu_exception; s->reg[rd] = (int64_t)rval; } break; #elif FLEN >= 32 case 3: /* c.flw */ { uint32_t rval; if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 6, 2, 2) | get_field1(insn, 5, 6, 6); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); if (target_read_u32(s, &rval, addr)) goto mmu_exception; s->fp_reg[rd] = rval | F32_HIGH; s->fs = 3; } break; #endif #if XLEN >= 128 case 5: /* c.sq */ imm = get_field1(insn, 11, 4, 5) | get_field1(insn, 10, 8, 8) | get_field1(insn, 5, 6, 7); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); val = s->reg[rd]; if (target_write_u128(s, addr, val)) goto mmu_exception; break; #elif FLEN >= 64 case 5: /* c.fsd */ if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 5, 6, 7); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); if (target_write_u64(s, addr, s->fp_reg[rd])) goto mmu_exception; break; #endif case 6: /* c.sw */ imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 6, 2, 2) | get_field1(insn, 5, 6, 6); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); val = s->reg[rd]; if (target_write_u32(s, addr, val)) goto mmu_exception; break; #if XLEN >= 64 case 7: /* c.sd */ imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 5, 6, 7); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); val = s->reg[rd]; if (target_write_u64(s, addr, val)) goto mmu_exception; break; #elif FLEN >= 32 case 7: /* c.fsw */ if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 6, 2, 2) | get_field1(insn, 5, 6, 6); rs1 = ((insn >> 7) & 7) | 8; addr = (intx_t)(s->reg[rs1] + imm); if (target_write_u32(s, addr, s->fp_reg[rd])) goto mmu_exception; break; #endif default: goto illegal_insn; } C_NEXT_INSN; C_QUADRANT(1) funct3 = (insn >> 13) & 7; switch(funct3) { case 0: /* c.addi/c.nop */ if (rd != 0) { imm = sext(get_field1(insn, 12, 5, 5) | get_field1(insn, 2, 0, 4), 6); s->reg[rd] = (intx_t)(s->reg[rd] + imm); } break; #if XLEN == 32 case 1: /* c.jal */ imm = sext(get_field1(insn, 12, 11, 11) | get_field1(insn, 11, 4, 4) | get_field1(insn, 9, 8, 9) | get_field1(insn, 8, 10, 10) | get_field1(insn, 7, 6, 6) | get_field1(insn, 6, 7, 7) | get_field1(insn, 3, 1, 3) | get_field1(insn, 2, 5, 5), 12); s->reg[1] = GET_PC() + 2; s->pc = (intx_t)(GET_PC() + imm); JUMP_INSN; #else case 1: /* c.addiw */ if (rd != 0) { imm = sext(get_field1(insn, 12, 5, 5) | get_field1(insn, 2, 0, 4), 6); s->reg[rd] = (int32_t)(s->reg[rd] + imm); } break; #endif case 2: /* c.li */ if (rd != 0) { imm = sext(get_field1(insn, 12, 5, 5) | get_field1(insn, 2, 0, 4), 6); s->reg[rd] = imm; } break; case 3: if (rd == 2) { /* c.addi16sp */ imm = sext(get_field1(insn, 12, 9, 9) | get_field1(insn, 6, 4, 4) | get_field1(insn, 5, 6, 6) | get_field1(insn, 3, 7, 8) | get_field1(insn, 2, 5, 5), 10); if (imm == 0) goto illegal_insn; s->reg[2] = (intx_t)(s->reg[2] + imm); } else if (rd != 0) { /* c.lui */ imm = sext(get_field1(insn, 12, 17, 17) | get_field1(insn, 2, 12, 16), 18); s->reg[rd] = imm; } break; case 4: funct3 = (insn >> 10) & 3; rd = ((insn >> 7) & 7) | 8; switch(funct3) { case 0: /* c.srli */ case 1: /* c.srai */ imm = get_field1(insn, 12, 5, 5) | get_field1(insn, 2, 0, 4); #if XLEN == 32 if (imm & 0x20) goto illegal_insn; #elif XLEN == 128 if (imm == 0) imm = 64; else if (imm >= 32) imm = 128 - imm; #endif if (funct3 == 0) s->reg[rd] = (intx_t)((uintx_t)s->reg[rd] >> imm); else s->reg[rd] = (intx_t)s->reg[rd] >> imm; break; case 2: /* c.andi */ imm = sext(get_field1(insn, 12, 5, 5) | get_field1(insn, 2, 0, 4), 6); s->reg[rd] &= imm; break; case 3: rs2 = ((insn >> 2) & 7) | 8; funct3 = ((insn >> 5) & 3) | ((insn >> (12 - 2)) & 4); switch(funct3) { case 0: /* c.sub */ s->reg[rd] = (intx_t)(s->reg[rd] - s->reg[rs2]); break; case 1: /* c.xor */ s->reg[rd] = s->reg[rd] ^ s->reg[rs2]; break; case 2: /* c.or */ s->reg[rd] = s->reg[rd] | s->reg[rs2]; break; case 3: /* c.and */ s->reg[rd] = s->reg[rd] & s->reg[rs2]; break; #if XLEN >= 64 case 4: /* c.subw */ s->reg[rd] = (int32_t)(s->reg[rd] - s->reg[rs2]); break; case 5: /* c.addw */ s->reg[rd] = (int32_t)(s->reg[rd] + s->reg[rs2]); break; #endif default: goto illegal_insn; } break; } break; case 5: /* c.j */ imm = sext(get_field1(insn, 12, 11, 11) | get_field1(insn, 11, 4, 4) | get_field1(insn, 9, 8, 9) | get_field1(insn, 8, 10, 10) | get_field1(insn, 7, 6, 6) | get_field1(insn, 6, 7, 7) | get_field1(insn, 3, 1, 3) | get_field1(insn, 2, 5, 5), 12); s->pc = (intx_t)(GET_PC() + imm); JUMP_INSN; case 6: /* c.beqz */ rs1 = ((insn >> 7) & 7) | 8; imm = sext(get_field1(insn, 12, 8, 8) | get_field1(insn, 10, 3, 4) | get_field1(insn, 5, 6, 7) | get_field1(insn, 3, 1, 2) | get_field1(insn, 2, 5, 5), 9); if (s->reg[rs1] == 0) { s->pc = (intx_t)(GET_PC() + imm); JUMP_INSN; } break; case 7: /* c.bnez */ rs1 = ((insn >> 7) & 7) | 8; imm = sext(get_field1(insn, 12, 8, 8) | get_field1(insn, 10, 3, 4) | get_field1(insn, 5, 6, 7) | get_field1(insn, 3, 1, 2) | get_field1(insn, 2, 5, 5), 9); if (s->reg[rs1] != 0) { s->pc = (intx_t)(GET_PC() + imm); JUMP_INSN; } break; default: goto illegal_insn; } C_NEXT_INSN; C_QUADRANT(2) funct3 = (insn >> 13) & 7; rs2 = (insn >> 2) & 0x1f; switch(funct3) { case 0: /* c.slli */ imm = get_field1(insn, 12, 5, 5) | rs2; #if XLEN == 32 if (imm & 0x20) goto illegal_insn; #elif XLEN == 128 if (imm == 0) imm = 64; #endif if (rd != 0) s->reg[rd] = (intx_t)(s->reg[rd] << imm); break; #if XLEN == 128 case 1: /* c.lqsp */ imm = get_field1(insn, 12, 5, 5) | (rs2 & (1 << 4)) | get_field1(insn, 2, 6, 9); addr = (intx_t)(s->reg[2] + imm); if (target_read_u128(s, &val, addr)) goto mmu_exception; if (rd != 0) s->reg[rd] = val; break; #elif FLEN >= 64 case 1: /* c.fldsp */ { uint64_t rval; if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 12, 5, 5) | (rs2 & (3 << 3)) | get_field1(insn, 2, 6, 8); addr = (intx_t)(s->reg[2] + imm); if (target_read_u64(s, &rval, addr)) goto mmu_exception; s->fp_reg[rd] = rval | F64_HIGH; s->fs = 3; } break; #endif case 2: /* c.lwsp */ { uint32_t rval; imm = get_field1(insn, 12, 5, 5) | (rs2 & (7 << 2)) | get_field1(insn, 2, 6, 7); addr = (intx_t)(s->reg[2] + imm); if (target_read_u32(s, &rval, addr)) goto mmu_exception; if (rd != 0) s->reg[rd] = (int32_t)rval; } break; #if XLEN >= 64 case 3: /* c.ldsp */ { uint64_t rval; imm = get_field1(insn, 12, 5, 5) | (rs2 & (3 << 3)) | get_field1(insn, 2, 6, 8); addr = (intx_t)(s->reg[2] + imm); if (target_read_u64(s, &rval, addr)) goto mmu_exception; if (rd != 0) s->reg[rd] = (int64_t)rval; } break; #elif FLEN >= 32 case 3: /* c.flwsp */ { uint32_t rval; if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 12, 5, 5) | (rs2 & (7 << 2)) | get_field1(insn, 2, 6, 7); addr = (intx_t)(s->reg[2] + imm); if (target_read_u32(s, &rval, addr)) goto mmu_exception; s->fp_reg[rd] = rval | F32_HIGH; s->fs = 3; } break; #endif case 4: if (((insn >> 12) & 1) == 0) { if (rs2 == 0) { /* c.jr */ if (rd == 0) goto illegal_insn; s->pc = s->reg[rd] & ~1; JUMP_INSN; } else { /* c.mv */ if (rd != 0) s->reg[rd] = s->reg[rs2]; } } else { if (rs2 == 0) { if (rd == 0) { /* c.ebreak */ s->pending_exception = CAUSE_BREAKPOINT; goto exception; } else { /* c.jalr */ val = GET_PC() + 2; s->pc = s->reg[rd] & ~1; s->reg[1] = val; JUMP_INSN; } } else { if (rd != 0) { s->reg[rd] = (intx_t)(s->reg[rd] + s->reg[rs2]); } } } break; #if XLEN == 128 case 5: /* c.sqsp */ imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 7, 6, 8); addr = (intx_t)(s->reg[2] + imm); if (target_write_u128(s, addr, s->reg[rs2])) goto mmu_exception; break; #elif FLEN >= 64 case 5: /* c.fsdsp */ if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 7, 6, 8); addr = (intx_t)(s->reg[2] + imm); if (target_write_u64(s, addr, s->fp_reg[rs2])) goto mmu_exception; break; #endif case 6: /* c.swsp */ imm = get_field1(insn, 9, 2, 5) | get_field1(insn, 7, 6, 7); addr = (intx_t)(s->reg[2] + imm); if (target_write_u32(s, addr, s->reg[rs2])) goto mmu_exception; break; #if XLEN >= 64 case 7: /* c.sdsp */ imm = get_field1(insn, 10, 3, 5) | get_field1(insn, 7, 6, 8); addr = (intx_t)(s->reg[2] + imm); if (target_write_u64(s, addr, s->reg[rs2])) goto mmu_exception; break; #elif FLEN >= 32 case 7: /* c.swsp */ if (s->fs == 0) goto illegal_insn; imm = get_field1(insn, 9, 2, 5) | get_field1(insn, 7, 6, 7); addr = (intx_t)(s->reg[2] + imm); if (target_write_u32(s, addr, s->fp_reg[rs2])) goto mmu_exception; break; #endif default: goto illegal_insn; } C_NEXT_INSN; #endif /* CONFIG_EXT_C */ case 0x37: /* lui */ if (rd != 0) s->reg[rd] = (int32_t)(insn & 0xfffff000); NEXT_INSN; case 0x17: /* auipc */ if (rd != 0) s->reg[rd] = (intx_t)(GET_PC() + (int32_t)(insn & 0xfffff000)); NEXT_INSN; case 0x6f: /* jal */ imm = ((insn >> (31 - 20)) & (1 << 20)) | ((insn >> (21 - 1)) & 0x7fe) | ((insn >> (20 - 11)) & (1 << 11)) | (insn & 0xff000); imm = (imm << 11) >> 11; if (rd != 0) s->reg[rd] = GET_PC() + 4; s->pc = (intx_t)(GET_PC() + imm); JUMP_INSN; case 0x67: /* jalr */ imm = (int32_t)insn >> 20; val = GET_PC() + 4; s->pc = (intx_t)(s->reg[rs1] + imm) & ~1; if (rd != 0) s->reg[rd] = val; JUMP_INSN; case 0x63: funct3 = (insn >> 12) & 7; switch(funct3 >> 1) { case 0: /* beq/bne */ cond = (s->reg[rs1] == s->reg[rs2]); break; case 2: /* blt/bge */ cond = ((target_long)s->reg[rs1] < (target_long)s->reg[rs2]); break; case 3: /* bltu/bgeu */ cond = (s->reg[rs1] < s->reg[rs2]); break; default: goto illegal_insn; } cond ^= (funct3 & 1); if (cond) { imm = ((insn >> (31 - 12)) & (1 << 12)) | ((insn >> (25 - 5)) & 0x7e0) | ((insn >> (8 - 1)) & 0x1e) | ((insn << (11 - 7)) & (1 << 11)); imm = (imm << 19) >> 19; s->pc = (intx_t)(GET_PC() + imm); JUMP_INSN; } NEXT_INSN; case 0x03: /* load */ funct3 = (insn >> 12) & 7; imm = (int32_t)insn >> 20; addr = s->reg[rs1] + imm; switch(funct3) { case 0: /* lb */ { uint8_t rval; if (target_read_u8(s, &rval, addr)) goto mmu_exception; val = (int8_t)rval; } break; case 1: /* lh */ { uint16_t rval; if (target_read_u16(s, &rval, addr)) goto mmu_exception; val = (int16_t)rval; } break; case 2: /* lw */ { uint32_t rval; if (target_read_u32(s, &rval, addr)) goto mmu_exception; val = (int32_t)rval; } break; case 4: /* lbu */ { uint8_t rval; if (target_read_u8(s, &rval, addr)) goto mmu_exception; val = rval; } break; case 5: /* lhu */ { uint16_t rval; if (target_read_u16(s, &rval, addr)) goto mmu_exception; val = rval; } break; #if XLEN >= 64 case 3: /* ld */ { uint64_t rval; if (target_read_u64(s, &rval, addr)) goto mmu_exception; val = (int64_t)rval; } break; case 6: /* lwu */ { uint32_t rval; if (target_read_u32(s, &rval, addr)) goto mmu_exception; val = rval; } break; #endif #if XLEN >= 128 case 7: /* ldu */ { uint64_t rval; if (target_read_u64(s, &rval, addr)) goto mmu_exception; val = rval; } break; #endif default: goto illegal_insn; } if (rd != 0) s->reg[rd] = val; NEXT_INSN; case 0x23: /* store */ funct3 = (insn >> 12) & 7; imm = rd | ((insn >> (25 - 5)) & 0xfe0); imm = (imm << 20) >> 20; addr = s->reg[rs1] + imm; val = s->reg[rs2]; switch(funct3) { case 0: /* sb */ if (target_write_u8(s, addr, val)) goto mmu_exception; break; case 1: /* sh */ if (target_write_u16(s, addr, val)) goto mmu_exception; break; case 2: /* sw */ if (target_write_u32(s, addr, val)) goto mmu_exception; break; #if XLEN >= 64 case 3: /* sd */ if (target_write_u64(s, addr, val)) goto mmu_exception; break; #endif #if XLEN >= 128 case 4: /* sq */ if (target_write_u128(s, addr, val)) goto mmu_exception; break; #endif default: goto illegal_insn; } NEXT_INSN; case 0x13: funct3 = (insn >> 12) & 7; imm = (int32_t)insn >> 20; switch(funct3) { case 0: /* addi */ val = (intx_t)(s->reg[rs1] + imm); break; case 1: /* slli */ if ((imm & ~(XLEN - 1)) != 0) goto illegal_insn; val = (intx_t)(s->reg[rs1] << (imm & (XLEN - 1))); break; case 2: /* slti */ val = (target_long)s->reg[rs1] < (target_long)imm; break; case 3: /* sltiu */ val = s->reg[rs1] < (target_ulong)imm; break; case 4: /* xori */ val = s->reg[rs1] ^ imm; break; case 5: /* srli/srai */ if ((imm & ~((XLEN - 1) | 0x400)) != 0) goto illegal_insn; if (imm & 0x400) val = (intx_t)s->reg[rs1] >> (imm & (XLEN - 1)); else val = (intx_t)((uintx_t)s->reg[rs1] >> (imm & (XLEN - 1))); break; case 6: /* ori */ val = s->reg[rs1] | imm; break; default: case 7: /* andi */ val = s->reg[rs1] & imm; break; } if (rd != 0) s->reg[rd] = val; NEXT_INSN; #if XLEN >= 64 case 0x1b:/* OP-IMM-32 */ funct3 = (insn >> 12) & 7; imm = (int32_t)insn >> 20; val = s->reg[rs1]; switch(funct3) { case 0: /* addiw */ val = (int32_t)(val + imm); break; case 1: /* slliw */ if ((imm & ~31) != 0) goto illegal_insn; val = (int32_t)(val << (imm & 31)); break; case 5: /* srliw/sraiw */ if ((imm & ~(31 | 0x400)) != 0) goto illegal_insn; if (imm & 0x400) val = (int32_t)val >> (imm & 31); else val = (int32_t)((uint32_t)val >> (imm & 31)); break; default: goto illegal_insn; } if (rd != 0) s->reg[rd] = val; NEXT_INSN; #endif #if XLEN >= 128 case 0x5b: /* OP-IMM-64 */ funct3 = (insn >> 12) & 7; imm = (int32_t)insn >> 20; val = s->reg[rs1]; switch(funct3) { case 0: /* addid */ val = (int64_t)(val + imm); break; case 1: /* sllid */ if ((imm & ~63) != 0) goto illegal_insn; val = (int64_t)(val << (imm & 63)); break; case 5: /* srlid/sraid */ if ((imm & ~(63 | 0x400)) != 0) goto illegal_insn; if (imm & 0x400) val = (int64_t)val >> (imm & 63); else val = (int64_t)((uint64_t)val >> (imm & 63)); break; default: goto illegal_insn; } if (rd != 0) s->reg[rd] = val; NEXT_INSN; #endif case 0x33: imm = insn >> 25; val = s->reg[rs1]; val2 = s->reg[rs2]; if (imm == 1) { funct3 = (insn >> 12) & 7; switch(funct3) { case 0: /* mul */ val = (intx_t)((intx_t)val * (intx_t)val2); break; case 1: /* mulh */ val = (intx_t)glue(mulh, XLEN)(val, val2); break; case 2:/* mulhsu */ val = (intx_t)glue(mulhsu, XLEN)(val, val2); break; case 3:/* mulhu */ val = (intx_t)glue(mulhu, XLEN)(val, val2); break; case 4:/* div */ val = glue(div, XLEN)(val, val2); break; case 5:/* divu */ val = (intx_t)glue(divu, XLEN)(val, val2); break; case 6:/* rem */ val = glue(rem, XLEN)(val, val2); break; case 7:/* remu */ val = (intx_t)glue(remu, XLEN)(val, val2); break; default: goto illegal_insn; } } else { if (imm & ~0x20) goto illegal_insn; funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3)); switch(funct3) { case 0: /* add */ val = (intx_t)(val + val2); break; case 0 | 8: /* sub */ val = (intx_t)(val - val2); break; case 1: /* sll */ val = (intx_t)(val << (val2 & (XLEN - 1))); break; case 2: /* slt */ val = (target_long)val < (target_long)val2; break; case 3: /* sltu */ val = val < val2; break; case 4: /* xor */ val = val ^ val2; break; case 5: /* srl */ val = (intx_t)((uintx_t)val >> (val2 & (XLEN - 1))); break; case 5 | 8: /* sra */ val = (intx_t)val >> (val2 & (XLEN - 1)); break; case 6: /* or */ val = val | val2; break; case 7: /* and */ val = val & val2; break; default: goto illegal_insn; } } if (rd != 0) s->reg[rd] = val; NEXT_INSN; #if XLEN >= 64 case 0x3b: /* OP-32 */ imm = insn >> 25; val = s->reg[rs1]; val2 = s->reg[rs2]; if (imm == 1) { funct3 = (insn >> 12) & 7; switch(funct3) { case 0: /* mulw */ val = (int32_t)((int32_t)val * (int32_t)val2); break; case 4:/* divw */ val = div32(val, val2); break; case 5:/* divuw */ val = (int32_t)divu32(val, val2); break; case 6:/* remw */ val = rem32(val, val2); break; case 7:/* remuw */ val = (int32_t)remu32(val, val2); break; default: goto illegal_insn; } } else { if (imm & ~0x20) goto illegal_insn; funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3)); switch(funct3) { case 0: /* addw */ val = (int32_t)(val + val2); break; case 0 | 8: /* subw */ val = (int32_t)(val - val2); break; case 1: /* sllw */ val = (int32_t)((uint32_t)val << (val2 & 31)); break; case 5: /* srlw */ val = (int32_t)((uint32_t)val >> (val2 & 31)); break; case 5 | 8: /* sraw */ val = (int32_t)val >> (val2 & 31); break; default: goto illegal_insn; } } if (rd != 0) s->reg[rd] = val; NEXT_INSN; #endif #if XLEN >= 128 case 0x7b: /* OP-64 */ imm = insn >> 25; val = s->reg[rs1]; val2 = s->reg[rs2]; if (imm == 1) { funct3 = (insn >> 12) & 7; switch(funct3) { case 0: /* muld */ val = (int64_t)((int64_t)val * (int64_t)val2); break; case 4:/* divd */ val = div64(val, val2); break; case 5:/* divud */ val = (int64_t)divu64(val, val2); break; case 6:/* remd */ val = rem64(val, val2); break; case 7:/* remud */ val = (int64_t)remu64(val, val2); break; default: goto illegal_insn; } } else { if (imm & ~0x20) goto illegal_insn; funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3)); switch(funct3) { case 0: /* addd */ val = (int64_t)(val + val2); break; case 0 | 8: /* subd */ val = (int64_t)(val - val2); break; case 1: /* slld */ val = (int64_t)((uint64_t)val << (val2 & 63)); break; case 5: /* srld */ val = (int64_t)((uint64_t)val >> (val2 & 63)); break; case 5 | 8: /* srad */ val = (int64_t)val >> (val2 & 63); break; default: goto illegal_insn; } } if (rd != 0) s->reg[rd] = val; NEXT_INSN; #endif case 0x73: funct3 = (insn >> 12) & 7; imm = insn >> 20; if (funct3 & 4) val = rs1; else val = s->reg[rs1]; funct3 &= 3; switch(funct3) { case 1: /* csrrw */ s->insn_counter = GET_INSN_COUNTER(); if (csr_read(s, &val2, imm, TRUE)) goto illegal_insn; val2 = (intx_t)val2; err = csr_write(s, imm, val); if (err < 0) goto illegal_insn; if (rd != 0) s->reg[rd] = val2; if (err > 0) { s->pc = GET_PC() + 4; if (err == 2) JUMP_INSN; else goto done_interp; } break; case 2: /* csrrs */ case 3: /* csrrc */ s->insn_counter = GET_INSN_COUNTER(); if (csr_read(s, &val2, imm, (rs1 != 0))) goto illegal_insn; val2 = (intx_t)val2; if (rs1 != 0) { if (funct3 == 2) val = val2 | val; else val = val2 & ~val; err = csr_write(s, imm, val); if (err < 0) goto illegal_insn; } else { err = 0; } if (rd != 0) s->reg[rd] = val2; if (err > 0) { s->pc = GET_PC() + 4; if (err == 2) JUMP_INSN; else goto done_interp; } break; case 0: switch(imm) { case 0x000: /* ecall */ if (insn & 0x000fff80) goto illegal_insn; s->pending_exception = CAUSE_USER_ECALL + s->priv; goto exception; case 0x001: /* ebreak */ if (insn & 0x000fff80) goto illegal_insn; s->pending_exception = CAUSE_BREAKPOINT; goto exception; case 0x102: /* sret */ { if (insn & 0x000fff80) goto illegal_insn; if (s->priv < PRV_S) goto illegal_insn; s->pc = GET_PC(); handle_sret(s); goto done_interp; } break; case 0x302: /* mret */ { if (insn & 0x000fff80) goto illegal_insn; if (s->priv < PRV_M) goto illegal_insn; s->pc = GET_PC(); handle_mret(s); goto done_interp; } break; case 0x105: /* wfi */ if (insn & 0x00007f80) goto illegal_insn; if (s->priv == PRV_U) goto illegal_insn; /* go to power down if no enabled interrupts are pending */ if ((s->mip & s->mie) == 0) { s->power_down_flag = TRUE; s->pc = GET_PC() + 4; goto done_interp; } break; default: if ((imm >> 5) == 0x09) { /* sfence.vma */ if (insn & 0x00007f80) goto illegal_insn; if (s->priv == PRV_U) goto illegal_insn; if (rs1 == 0) { tlb_flush_all(s); } else { tlb_flush_vaddr(s, s->reg[rs1]); } /* the current code TLB may have been flushed */ s->pc = GET_PC() + 4; JUMP_INSN; } else { goto illegal_insn; } break; } break; default: goto illegal_insn; } NEXT_INSN; case 0x0f: /* misc-mem */ funct3 = (insn >> 12) & 7; switch(funct3) { case 0: /* fence */ if (insn & 0xf00fff80) goto illegal_insn; break; case 1: /* fence.i */ if (insn != 0x0000100f) goto illegal_insn; break; #if XLEN >= 128 case 2: /* lq */ imm = (int32_t)insn >> 20; addr = s->reg[rs1] + imm; if (target_read_u128(s, &val, addr)) goto mmu_exception; if (rd != 0) s->reg[rd] = val; break; #endif default: goto illegal_insn; } NEXT_INSN; case 0x2f: funct3 = (insn >> 12) & 7; #define OP_A(size) \ { \ uint ## size ##_t rval; \ \ addr = s->reg[rs1]; \ funct3 = insn >> 27; \ switch(funct3) { \ case 2: /* lr.w */ \ if (rs2 != 0) \ goto illegal_insn; \ if (target_read_u ## size(s, &rval, addr)) \ goto mmu_exception; \ val = (int## size ## _t)rval; \ s->load_res = addr; \ break; \ case 3: /* sc.w */ \ if (s->load_res == addr) { \ if (target_write_u ## size(s, addr, s->reg[rs2])) \ goto mmu_exception; \ val = 0; \ } else { \ val = 1; \ } \ break; \ case 1: /* amiswap.w */ \ case 0: /* amoadd.w */ \ case 4: /* amoxor.w */ \ case 0xc: /* amoand.w */ \ case 0x8: /* amoor.w */ \ case 0x10: /* amomin.w */ \ case 0x14: /* amomax.w */ \ case 0x18: /* amominu.w */ \ case 0x1c: /* amomaxu.w */ \ if (target_read_u ## size(s, &rval, addr)) \ goto mmu_exception; \ val = (int## size ## _t)rval; \ val2 = s->reg[rs2]; \ switch(funct3) { \ case 1: /* amiswap.w */ \ break; \ case 0: /* amoadd.w */ \ val2 = (int## size ## _t)(val + val2); \ break; \ case 4: /* amoxor.w */ \ val2 = (int## size ## _t)(val ^ val2); \ break; \ case 0xc: /* amoand.w */ \ val2 = (int## size ## _t)(val & val2); \ break; \ case 0x8: /* amoor.w */ \ val2 = (int## size ## _t)(val | val2); \ break; \ case 0x10: /* amomin.w */ \ if ((int## size ## _t)val < (int## size ## _t)val2) \ val2 = (int## size ## _t)val; \ break; \ case 0x14: /* amomax.w */ \ if ((int## size ## _t)val > (int## size ## _t)val2) \ val2 = (int## size ## _t)val; \ break; \ case 0x18: /* amominu.w */ \ if ((uint## size ## _t)val < (uint## size ## _t)val2) \ val2 = (int## size ## _t)val; \ break; \ case 0x1c: /* amomaxu.w */ \ if ((uint## size ## _t)val > (uint## size ## _t)val2) \ val2 = (int## size ## _t)val; \ break; \ default: \ goto illegal_insn; \ } \ if (target_write_u ## size(s, addr, val2)) \ goto mmu_exception; \ break; \ default: \ goto illegal_insn; \ } \ } switch(funct3) { case 2: OP_A(32); break; #if XLEN >= 64 case 3: OP_A(64); break; #endif #if XLEN >= 128 case 4: OP_A(128); break; #endif default: goto illegal_insn; } if (rd != 0) s->reg[rd] = val; NEXT_INSN; #if FLEN > 0 /* FPU */ case 0x07: /* fp load */ if (s->fs == 0) goto illegal_insn; funct3 = (insn >> 12) & 7; imm = (int32_t)insn >> 20; addr = s->reg[rs1] + imm; switch(funct3) { case 2: /* flw */ { uint32_t rval; if (target_read_u32(s, &rval, addr)) goto mmu_exception; s->fp_reg[rd] = rval | F32_HIGH; } break; #if FLEN >= 64 case 3: /* fld */ { uint64_t rval; if (target_read_u64(s, &rval, addr)) goto mmu_exception; s->fp_reg[rd] = rval | F64_HIGH; } break; #endif #if FLEN >= 128 case 4: /* flq */ { uint128_t rval; if (target_read_u128(s, &rval, addr)) goto mmu_exception; s->fp_reg[rd] = rval; } break; #endif default: goto illegal_insn; } s->fs = 3; NEXT_INSN; case 0x27: /* fp store */ if (s->fs == 0) goto illegal_insn; funct3 = (insn >> 12) & 7; imm = rd | ((insn >> (25 - 5)) & 0xfe0); imm = (imm << 20) >> 20; addr = s->reg[rs1] + imm; switch(funct3) { case 2: /* fsw */ if (target_write_u32(s, addr, s->fp_reg[rs2])) goto mmu_exception; break; #if FLEN >= 64 case 3: /* fsd */ if (target_write_u64(s, addr, s->fp_reg[rs2])) goto mmu_exception; break; #endif #if FLEN >= 128 case 4: /* fsq */ if (target_write_u128(s, addr, s->fp_reg[rs2])) goto mmu_exception; break; #endif default: goto illegal_insn; } NEXT_INSN; case 0x43: /* fmadd */ if (s->fs == 0) goto illegal_insn; funct3 = (insn >> 25) & 3; rs3 = insn >> 27; rm = get_insn_rm(s, (insn >> 12) & 7); if (rm < 0) goto illegal_insn; switch(funct3) { case 0: s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1], s->fp_reg[rs2], s->fp_reg[rs3], rm, &s->fflags) | F32_HIGH; break; #if FLEN >= 64 case 1: s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1], s->fp_reg[rs2], s->fp_reg[rs3], rm, &s->fflags) | F64_HIGH; break; #endif #if FLEN >= 128 case 3: s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1], s->fp_reg[rs2], s->fp_reg[rs3], rm, &s->fflags); break; #endif default: goto illegal_insn; } s->fs = 3; NEXT_INSN; case 0x47: /* fmsub */ if (s->fs == 0) goto illegal_insn; funct3 = (insn >> 25) & 3; rs3 = insn >> 27; rm = get_insn_rm(s, (insn >> 12) & 7); if (rm < 0) goto illegal_insn; switch(funct3) { case 0: s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1], s->fp_reg[rs2], s->fp_reg[rs3] ^ FSIGN_MASK32, rm, &s->fflags) | F32_HIGH; break; #if FLEN >= 64 case 1: s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1], s->fp_reg[rs2], s->fp_reg[rs3] ^ FSIGN_MASK64, rm, &s->fflags) | F64_HIGH; break; #endif #if FLEN >= 128 case 3: s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1], s->fp_reg[rs2], s->fp_reg[rs3] ^ FSIGN_MASK128, rm, &s->fflags); break; #endif default: goto illegal_insn; } s->fs = 3; NEXT_INSN; case 0x4b: /* fnmsub */ if (s->fs == 0) goto illegal_insn; funct3 = (insn >> 25) & 3; rs3 = insn >> 27; rm = get_insn_rm(s, (insn >> 12) & 7); if (rm < 0) goto illegal_insn; switch(funct3) { case 0: s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1] ^ FSIGN_MASK32, s->fp_reg[rs2], s->fp_reg[rs3], rm, &s->fflags) | F32_HIGH; break; #if FLEN >= 64 case 1: s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1] ^ FSIGN_MASK64, s->fp_reg[rs2], s->fp_reg[rs3], rm, &s->fflags) | F64_HIGH; break; #endif #if FLEN >= 128 case 3: s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1] ^ FSIGN_MASK128, s->fp_reg[rs2], s->fp_reg[rs3], rm, &s->fflags); break; #endif default: goto illegal_insn; } s->fs = 3; NEXT_INSN; case 0x4f: /* fnmadd */ if (s->fs == 0) goto illegal_insn; funct3 = (insn >> 25) & 3; rs3 = insn >> 27; rm = get_insn_rm(s, (insn >> 12) & 7); if (rm < 0) goto illegal_insn; switch(funct3) { case 0: s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1] ^ FSIGN_MASK32, s->fp_reg[rs2], s->fp_reg[rs3] ^ FSIGN_MASK32, rm, &s->fflags) | F32_HIGH; break; #if FLEN >= 64 case 1: s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1] ^ FSIGN_MASK64, s->fp_reg[rs2], s->fp_reg[rs3] ^ FSIGN_MASK64, rm, &s->fflags) | F64_HIGH; break; #endif #if FLEN >= 128 case 3: s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1] ^ FSIGN_MASK128, s->fp_reg[rs2], s->fp_reg[rs3] ^ FSIGN_MASK128, rm, &s->fflags); break; #endif default: goto illegal_insn; } s->fs = 3; NEXT_INSN; case 0x53: if (s->fs == 0) goto illegal_insn; imm = insn >> 25; rm = (insn >> 12) & 7; switch(imm) { #define F_SIZE 32 #include "riscv_cpu_fp_template.h" #if FLEN >= 64 #define F_SIZE 64 #include "riscv_cpu_fp_template.h" #endif #if FLEN >= 128 #define F_SIZE 128 #include "riscv_cpu_fp_template.h" #endif default: goto illegal_insn; } NEXT_INSN; #endif default: goto illegal_insn; } /* update PC for next instruction */ jump_insn: ; } /* end of main loop */ illegal_insn: s->pending_exception = CAUSE_ILLEGAL_INSTRUCTION; s->pending_tval = insn; mmu_exception: exception: s->pc = GET_PC(); if (s->pending_exception >= 0) { /* Note: the idea is that one exception counts for one cycle. */ s->n_cycles--; raise_exception2(s, s->pending_exception, s->pending_tval); } /* we exit because XLEN may have changed */ done_interp: the_end: s->insn_counter = GET_INSN_COUNTER(); #if 0 printf("done interp %lx int=%x mstatus=%lx prv=%d\n", (uint64_t)s->insn_counter, s->mip & s->mie, (uint64_t)s->mstatus, s->priv); #endif } #undef uintx_t #undef intx_t #undef XLEN #undef OP_A