1739 lines
61 KiB
C
1739 lines
61 KiB
C
/*
|
|
* 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
|