From 7b817165629e6a990eeb5ecde2343bee9959a3f3 Mon Sep 17 00:00:00 2001 From: ccs100203 Date: Wed, 12 Jan 2022 18:52:41 +0800 Subject: [PATCH 1/2] Implement Preliminary RV32C support There has a variable inst_len(2 or 4) to calculate the next PC, RV32C related exception handler and all RV32C implementation exclude RV32C.F. Note: c.ebreak does'nt pass compliance test. Co-authored-by: ccs100203 Co-authored-by: Uduru0522 --- Makefile | 1 + io.c | 2 +- riscv.c | 606 +++++++++++++++++++++++++++++++++++++++++++++--- riscv_private.h | 92 ++++++++ 4 files changed, 666 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 8876aa75..83249697 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ CFLAGS += -D ENABLE_Zicsr CFLAGS += -D ENABLE_Zifencei CFLAGS += -D ENABLE_RV32A CFLAGS += -D DEFAULT_STACK_ADDR=0xFFFFF000 +CFLAGS += -D ENABLE_RV32C # Experimental SDL oriented system calls CFLAGS += -D ENABLE_SDL diff --git a/io.c b/io.c index b1142aa7..88b5f29d 100644 --- a/io.c +++ b/io.c @@ -70,7 +70,7 @@ uint32_t memory_read_str(memory_t *m, uint8_t *dst, uint32_t addr, uint32_t max) uint32_t memory_read_ifetch(memory_t *m, uint32_t addr) { const uint32_t addr_lo = addr & mask_lo; - assert((addr_lo & 3) == 0); + assert((addr_lo & 1) == 0); chunk_t *c = m->chunks[addr >> 16]; assert(c); diff --git a/riscv.c b/riscv.c index 5644ccac..c28ae326 100644 --- a/riscv.c +++ b/riscv.c @@ -21,13 +21,32 @@ static void rv_except_inst_misaligned(struct riscv_t *rv, uint32_t old_pc) rv->PC = base; break; case 1: // VECTORED - rv->PC = base + 4 * code; + rv->PC = base + rv->inst_len * code; break; } rv->csr_mcause = code; } +static void rv_breakpoint(struct riscv_t *rv, uint32_t old_pc) +{ + const uint32_t base = rv->csr_mtvec & ~0x3; + const uint32_t mode = rv->csr_mtvec & 0x3; + + const uint32_t code = 3; // breakpoint exception + + rv->csr_mepc = old_pc; + rv->csr_mtval = rv->PC; + + switch (mode) { + case 0: // DIRECT + rv->PC = base; + break; + case 1: // VECTORED + rv->PC = base + 4 * code; + break; + } + static void rv_except_load_misaligned(struct riscv_t *rv, uint32_t addr) { const uint32_t base = rv->csr_mtvec & ~0x3; @@ -43,7 +62,7 @@ static void rv_except_load_misaligned(struct riscv_t *rv, uint32_t addr) rv->PC = base; break; case 1: // VECTORED - rv->PC = base + 4 * code; + rv->PC = base + rv->inst_len * code; break; } @@ -65,17 +84,34 @@ static void rv_except_store_misaligned(struct riscv_t *rv, uint32_t addr) rv->PC = base; break; case 1: // VECTORED - rv->PC = base + 4 * code; + rv->PC = base + rv->inst_len * code; break; } rv->csr_mcause = code; } -static void rv_except_illegal_inst(struct riscv_t *rv UNUSED) +static void rv_except_illegal_inst(struct riscv_t *rv, uint32_t inst) { - /* TODO: dump more information */ - assert(!"illegal instruction"); + /* Jump to the base and record information to mcause */ + const uint32_t base = rv->csr_mtvec & ~0x3; + const uint32_t mode = rv->csr_mtvec & 0x3; + + const uint32_t code = 2; // Illegal instruction code + + rv->csr_mepc = rv->PC; + rv->csr_mtval = inst; + + switch (mode) { + case 0: // DIRECT + rv->PC = base; + break; + case 1: // VECTORED + rv->PC = base + 4 * code; + break; + } + + rv->csr_mcause = code; } static bool op_load(struct riscv_t *rv, uint32_t inst UNUSED) @@ -119,11 +155,11 @@ static bool op_load(struct riscv_t *rv, uint32_t inst UNUSED) rv->X[rd] = rv->io.mem_read_s(rv, addr); break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } // step over instruction - rv->PC += 4; + rv->PC += rv->inst_len; // enforce zero register if (rd == rv_reg_zero) rv->X[rv_reg_zero] = 0; @@ -182,12 +218,12 @@ static bool op_op_imm(struct riscv_t *rv, uint32_t inst) rv->X[rd] = rv->X[rs1] & imm; break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } // step over instruction - rv->PC += 4; + rv->PC += rv->inst_len; // enforce zero register if (rd == rv_reg_zero) @@ -204,7 +240,7 @@ static bool op_auipc(struct riscv_t *rv, uint32_t inst) rv->X[rd] = val; // step over instruction - rv->PC += 4; + rv->PC += rv->inst_len; // enforce zero register if (rd == rv_reg_zero) @@ -244,7 +280,7 @@ static bool op_store(struct riscv_t *rv, uint32_t inst) rv->io.mem_write_w(rv, addr, data); break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } @@ -292,7 +328,7 @@ static bool op_op(struct riscv_t *rv, uint32_t inst) rv->X[rd] = rv->X[rs1] & rv->X[rs2]; break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } break; @@ -357,7 +393,7 @@ static bool op_op(struct riscv_t *rv, uint32_t inst) } } break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } break; @@ -371,16 +407,16 @@ static bool op_op(struct riscv_t *rv, uint32_t inst) rv->X[rd] = ((int32_t) rv->X[rs1]) >> (rv->X[rs2] & 0x1f); break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } // step over instruction - rv->PC += 4; + rv->PC += rv->inst_len; // enforce zero register if (rd == rv_reg_zero) rv->X[rv_reg_zero] = 0; @@ -395,7 +431,7 @@ static bool op_lui(struct riscv_t *rv, uint32_t inst) rv->X[rd] = val; // step over instruction - rv->PC += 4; + rv->PC += rv->inst_len; // enforce zero register if (rd == rv_reg_zero) @@ -436,7 +472,7 @@ static bool op_branch(struct riscv_t *rv, uint32_t inst) taken = (rv->X[rs1] >= rv->X[rs2]); break; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } // perform branch action @@ -446,7 +482,7 @@ static bool op_branch(struct riscv_t *rv, uint32_t inst) rv_except_inst_misaligned(rv, pc); } else { // step over instruction - rv->PC += 4; + rv->PC += rv->inst_len; } // can branch return false; @@ -462,7 +498,7 @@ static bool op_jalr(struct riscv_t *rv, uint32_t inst) const int32_t imm = dec_itype_imm(inst); // compute return address - const uint32_t ra = rv->PC + 4; + const uint32_t ra = rv->PC + rv->inst_len; // jump rv->PC = (rv->X[rs1] + imm) & ~1u; @@ -488,7 +524,7 @@ static bool op_jal(struct riscv_t *rv, uint32_t inst) const int32_t rel = dec_jtype_imm(inst); // compute return address - const uint32_t ra = rv->PC + 4; + const uint32_t ra = rv->PC + rv->inst_len; rv->PC += rel; // link @@ -597,20 +633,20 @@ static bool op_system(struct riscv_t *rv, uint32_t inst) rv->io.on_ecall(rv); break; case 1: // EBREAK - rv->io.on_ebreak(rv); + rv_breakpoint(rv,rv->PC); break; case 0x002: // URET case 0x102: // SRET case 0x202: // HRET case 0x105: // WFI - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; case 0x302: // MRET rv->PC = rv->csr_mepc; // this is a branch return false; default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } break; @@ -649,12 +685,12 @@ static bool op_system(struct riscv_t *rv, uint32_t inst) } #endif // ENABLE_Zicsr default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } // step over instruction - rv->PC += 4; + rv->PC += rv->inst_len; // enforce zero register if (rd == rv_reg_zero) @@ -745,7 +781,7 @@ static bool op_amo(struct riscv_t *rv, uint32_t inst) break; } default: - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); return false; } @@ -773,12 +809,491 @@ static bool op_amo(struct riscv_t *rv, uint32_t inst) // handler for all unimplemented opcodes static bool op_unimp(struct riscv_t *rv, uint32_t inst UNUSED) { - rv_except_illegal_inst(rv); + rv_except_illegal_inst(rv, inst); + return false; +} + +#define ENABLE_RV32C 1 +#ifdef ENABLE_RV32C +static bool c_op_addi(struct riscv_t *rv, uint16_t inst) +{ + uint16_t tmp = + (uint16_t)(((inst & FCI_IMM_12) >> 5) | (inst & FCI_IMM_6_2)) >> 2; + const int32_t imm = (0x20 & tmp) ? 0xffffffc0 | tmp : tmp; + const uint16_t rd = c_dec_rd(inst); + + // dispatch operation type + if (rd != 0) { + // C.ADDI + rv->X[rd] += imm; + } else { + // C.NOP + } + + // step over instruction + rv->PC += rv->inst_len; + // enforce zero register + if (rd == rv_reg_zero) + rv->X[rv_reg_zero] = 0; + return true; +} + +static bool c_op_addi4spn(struct riscv_t *rv, uint16_t inst) +{ + uint16_t temp = 0; + temp |= (inst & 0x1800) >> 7; + temp |= (inst & 0x780) >> 1; + temp |= (inst & 0x40) >> 4; + temp |= (inst & 0x20) >> 2; + + const uint16_t imm = temp; + const uint16_t rd = c_dec_rdc(inst) | 0x08; + rv->X[rd] = rv->X[2] + imm; + + rv->PC += rv->inst_len; + return true; +} + +static bool c_op_li(struct riscv_t *rv, uint16_t inst) +{ + uint16_t tmp = (uint16_t)((inst & 0x1000) >> 7 | (inst & 0x7c) >> 2); + const int32_t imm = (tmp & 0x20) ? 0xffffffc0 | tmp : tmp; + const uint16_t rd = c_dec_rd(inst); + rv->X[rd] = imm; + + rv->PC += rv->inst_len; + return true; +} + +static bool c_op_lui(struct riscv_t *rv, uint16_t inst) +{ + const uint16_t rd = c_dec_rd(inst); + if (rd == 2) { + // C.ADDI16SP + uint32_t tmp = (inst & 0x1000) >> 3; + tmp |= (inst & 0x40) >> 2; + tmp |= (inst & 0x20) << 1; + tmp |= (inst & 0x18) << 4; + tmp |= (inst & 0x4) << 3; + const uint32_t imm = (tmp & 0x200) ? (0xfffffc00 | tmp) : tmp; + + if (imm != 0) + rv->X[rd] += imm; + else { /*imm==0 is reserved */ + } + } else if (rd != 0) { + // C.LUI + uint32_t tmp = (inst & 0x1000) << 5 | (inst & 0x7c) << 10; + const int32_t imm = (tmp & 0x20000) ? (0xfffc0000 | tmp) : tmp; + if (imm != 0) + rv->X[rd] = imm; + else { /*imm==0 is reserved*/ + } + } else { + // HINTS + } + + rv->PC += rv->inst_len; + return true; +} + +// static bool c_op_XX(struct riscv_t *rv, uint16_t inst) +static bool c_op_srli(struct riscv_t *rv, uint16_t inst) +{ + uint32_t temp = 0; + temp |= (inst & 0x1000) >> 7; + temp |= (inst & 0x007C) >> 2; + + const uint32_t shamt = temp; + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + + // shamt[t]==1 are reserved + if (shamt & 0x20) + return true; + // HINTS + if (rs1 == 0) + return true; + // shamt ==0 is HINT + if (shamt == 0) + return true; + + rv->X[rs1] >>= shamt; + + return true; +} + +static bool c_op_srai(struct riscv_t *rv, uint16_t inst) +{ + uint32_t temp = 0; + temp |= (inst & 0x1000) >> 7; + temp |= (inst & 0x007C) >> 2; + + const uint32_t shamt = temp; + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + + // shamt[5]=1 Reserved + if (shamt & 0x20) + return true; + // shame ==0 is HINT + if (shamt == 0) + return true; + // HINT + if (rs1 == rv_reg_zero) + return true; + + const uint32_t mask = 0x80000000 | rv->X[rs1]; + rv->X[rs1] >>= shamt; + + for (unsigned int i = 0; i < shamt; ++i) { + rv->X[rs1] |= mask >> i; + } + + return true; +} + +static bool c_op_andi(struct riscv_t *rv, uint16_t inst) +{ + const uint16_t mask = (0x1000 & inst) << 3; + + uint16_t temp = 0; + for (int i = 0; i < 10; ++i) { + temp |= (mask >> i); + } + temp |= (inst & 0x007C) >> 2; + + const uint32_t imm = sign_extend_h(temp); + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + + rv->X[rs1] &= imm; + + return true; +} + +static bool c_op_misc_alu(struct riscv_t *rv, uint16_t inst) +{ + bool exec_result; + + // Find actual instruction + switch ((inst & 0x0C00) >> 10) { + case 0: // C.SRLI + exec_result = c_op_srli(rv, inst); + break; + case 1: // C.SRAI + exec_result = c_op_srai(rv, inst); + break; + case 2: // C.ANDI + exec_result = c_op_andi(rv, inst); + break; + case 3: // Arithmistic + uint32_t temp = 0; + temp |= (inst & 0x1000) >> 10; + temp |= (inst & 0x0060) >> 5; + + const uint32_t funct = temp; + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + const uint32_t rs2 = c_dec_rs2c(inst) | 0x08; + const uint32_t rd = rs1; + + switch (funct) { + case 0: // SUB + rv->X[rd] = rv->X[rs1] - rv->X[rs2]; + break; + case 1: // XOR + rv->X[rd] = rv->X[rs1] ^ rv->X[rs2]; + break; + case 2: // OR + rv->X[rd] = rv->X[rs1] | rv->X[rs2]; + break; + case 3: // AND + rv->X[rd] = rv->X[rs1] & rv->X[rs2]; + break; + case 4: + assert(!"RV32F instructions"); + break; + case 5: + assert(!"RV32F instructions"); + break; + case 6: + case 7: + assert(!"Instruction preserved"); + break; + default: + assert(!"Should not be reachable"); + break; + } + break; + default: + assert(!"Should not be reachable"); + break; + } + + if (!exec_result) { + return false; + } + + rv->PC += rv->inst_len; + return true; +} + +static bool c_op_slli(struct riscv_t *rv, uint16_t inst) +{ + uint32_t temp = 0; + temp |= (inst & FCI_IMM_12) >> 7; + temp |= (inst & FCI_IMM_6_2) >> 2; + + const uint32_t shamt = temp; + const uint32_t rd = c_dec_rd(inst); + + if (rd) { + rv->X[rd] <<= shamt; + } + + rv->PC += rv->inst_len; + return true; +} + +// CJ-type +static bool c_op_j(struct riscv_t *rv, uint16_t inst) +{ + uint32_t temp = 0; + const uint32_t imm = sign_extend_h(c_dec_cjtype_imm(inst)); + + rv->PC += imm; + return false; +} + +static bool c_op_jal(struct riscv_t *rv, uint16_t inst) +{ + const uint32_t imm = sign_extend_h(c_dec_cjtype_imm(inst)); + + rv->X[1] = rv->PC + 2; + rv->PC += imm; + + return false; +} +// CB-type + +// CB-type +static bool c_op_beqz(struct riscv_t *rv, uint16_t inst) +{ + const uint32_t imm = sign_extend_h(c_dec_cbtype_imm(inst)); + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + + if (!rv->X[rs1]) { + rv->PC += imm; + } else { + rv->PC += rv->inst_len; + } + + return false; +} + +static bool c_op_bnez(struct riscv_t *rv, uint16_t inst) +{ + const uint32_t imm = sign_extend_h(c_dec_cbtype_imm(inst)); + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + + if (rv->X[rs1]) { + rv->PC += imm; + } else { + rv->PC += rv->inst_len; + } + + return false +} +#else +#define c_op_addi4spn NULL +#define c_op_addi NULL + +static bool c_op_jal(struct riscv_t *rv, uint16_t inst) +{ + uint32_t temp = 0; + // ....xxxx....xxxx + temp |= (inst & 0b0000000000111000) >> 2; + temp |= (inst & 0b0000100000000000) >> 7; + temp |= (inst & 0b0000000000000100) << 3; + temp |= (inst & 0b0000000010000000) >> 1; + temp |= (inst & 0b0000000001000000) << 1; + temp |= (inst & 0b0000011000000000) >> 1; + temp |= (inst & 0b0000000100000000) << 2; + temp |= (inst & 0b0001000000000000) >> 1; + + const uint32_t imm = sign_extend_h(temp); + + rv->X[1] = rv->PC + 2; + rv->PC += imm; + + return true; +} + +// CR-type +static bool c_op_slli(struct riscv_t *rv, uint16_t inst) +{ + uint32_t temp = 0; + temp |= (inst & FCI_IMM_12) >> 7; + temp |= (inst & FCI_IMM_6_2) >> 2; + + const uint32_t shamt = temp; + const uint32_t rd = c_dec_rd(inst); + + if (rd) { + rv->X[rd] <<= shamt; + } + + rv->PC += rv->inst_len; + return true; +} + +static bool c_op_lwsp(struct riscv_t *rv, uint16_t inst) +{ + uint16_t temp = 0; + temp |= ((inst & FCI_IMM_6_2) | 0b1110000) >> 2; + temp |= (inst & FCI_IMM_12) >> 7; + temp |= ((inst & FCI_IMM_6_2) | 0b0001100) << 4; + + const uint16_t imm = temp; + const uint16_t rd = c_dec_rd(inst); + const uint16_t addr = rv->X[2] + imm; + + if (addr & 3) { + rv_except_load_misaligned(rv, addr); + return false; + } + rv->X[rd] = rv->io.mem_read_w(rv, addr); + + rv->PC += rv->inst_len; + return true; +} + +static bool c_op_cr(struct riscv_t *rv, uint16_t inst) +{ + const uint32_t rs1 = c_dec_rs1(inst); + const uint32_t rs2 = c_dec_rs2(inst); + const uint32_t rd = rs1; + + switch ((inst & 0x1000) >> 12) { + case 0: + if (rs2) { + rv->X[rd] = rv->X[rs2]; + rv->PC += rv->inst_len; + } else { + rv->PC = rv->X[rs1]; + + return false; + } + break; + case 1: + if (rs1) { + if (rs2) { + rv->X[rd] = rv->X[rs1] + rv->X[rs2]; + rv->PC += rv->inst_len; + } else { + rv->X[1] = rv->PC + 2; + rv->PC = rv->X[rs1]; + + if (rv->PC & 1) { + rv_except_inst_misaligned(rv, rv->PC); + return false; + } + + return false; + } + } else { + rv->io.on_ebreak(rv); + } + break; + default: + assert(!"Should be unreachbale."); + break; + } + + return true; +} + +// CSS-type +static bool c_op_swsp(struct riscv_t *rv, uint16_t inst) +{ + const uint16_t imm = (inst & 0x1e00) >> 7 | (inst & 0x180) >> 1; + const uint16_t rs2 = c_dec_rs2(inst); + const uint32_t addr = rv->X[2] + imm; + const uint32_t data = rv->X[rs2]; + + if (addr & 3) { + rv_except_store_misaligned(rv, addr); + return false; + } + rv->io.mem_write_w(rv, addr, data); + + rv->PC += rv->inst_len; + return true; +} + +// CL-type +static bool c_op_lw(struct riscv_t *rv, uint16_t inst) +{ + uint16_t temp = 0; + temp |= (inst & 0b0000000001000000) >> 4; + temp |= (inst & FC_IMM_12_10) >> 7; + temp |= (inst & 0b0000000000100000) << 1; + + const uint16_t imm = temp; + const uint16_t rd = c_dec_rdc(inst) | 0x08; + const uint16_t rs1 = c_dec_rs1c(inst) | 0x08; + const uint32_t addr = rv->X[rs1] + imm; + + if (addr & 3) { + rv_except_load_misaligned(rv, addr); + return false; + } + rv->X[rd] = rv->io.mem_read_w(rv, addr); + + rv->PC += rv->inst_len; + return true; +} + +// CS-type +static bool c_op_sw(struct riscv_t *rv, uint16_t inst) +{ + + uint32_t temp = 0; + // ....xxxx....xxxx + temp |= (inst & 0b0000000001000000) >> 4; + temp |= (inst & FC_IMM_12_10) >> 7; + temp |= (inst & 0b0000000000100000) << 1; + + const uint32_t imm = temp; + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + const uint32_t rs2 = c_dec_rs2c(inst) | 0x08; + const uint32_t addr = rv->X[rs1] + imm; + const uint32_t data = rv->X[rs2]; + + if (addr & 3) { + rv_except_store_misaligned(rv, addr); + return false; + } + rv->io.mem_write_w(rv, addr, data); + + rv->PC += rv->inst_len; + return true; +} + +static bool c_op_flw(struct riscv_t *rv, uint16_t inst) +{ + rv->PC += rv->inst_len; + return true; +} +#endif // ENABLE_RV32C + +// handler for all unimplemented opcodes +static bool op_unimp(struct riscv_t *rv, uint32_t inst UNUSED) +{ + rv_except_illegal_inst(rv, inst); return false; } // opcode handler type typedef bool (*opcode_t)(struct riscv_t *rv, uint32_t inst); +typedef bool (*c_opcode_t)(struct riscv_t *rv, uint16_t inst); void rv_step(struct riscv_t *rv, int32_t cycles) { @@ -803,7 +1318,21 @@ void rv_step(struct riscv_t *rv, int32_t cycles) OP(madd), OP(msub), OP(nmsub), OP(nmadd), OP(fp), OP(unimp), OP(unimp), OP(unimp), // 10 OP(branch), OP(jalr), OP(unimp), OP(jal), OP(system), OP(unimp), OP(unimp), OP(unimp), // 11 }; -// clang-format on + + + static const c_opcode_t c_opcodes[] = { + // 00 01 10 11 + c_op_addi4spn, c_op_addi, c_op_slli, NULL, // 000 + c_op_fld, c_op_jal, c_op_fldsp, NULL, // 001 + c_op_lw, c_op_li, c_op_lwsp, NULL, // 010 + c_op_flw, c_op_lui, c_op_flwsp, NULL, // 011 + NULL, c_op_misc_alu, c_op_cr, NULL, // 100 + c_op_fsd, c_op_j, c_op_fsdsp, NULL, // 101 + c_op_sw, c_op_beqz, c_op_swsp, NULL, // 110 + c_op_fsw, c_op_bnez, c_op_fswsp, NULL, // 111 + }; + + // clang-format on #ifdef ENABLE_COMPUTED_GOTO #define DISPATCH() \ @@ -874,6 +1403,7 @@ void rv_step(struct riscv_t *rv, int32_t cycles) // dispatch this opcode TABLE_TYPE op = jump_table[index]; assert(op); + rv->inst_len = INST_32; if (!op(rv, inst)) break; @@ -881,11 +1411,18 @@ void rv_step(struct riscv_t *rv, int32_t cycles) rv->csr_cycle++; } else { // TODO: compressed instruction - assert(!"Unreachable"); - } - } -#endif // ENABLE_COMPUTED_GOTO + const uint16_t c_index = (inst & FR_C_15_13 >> 11) | (inst & FR_C_1_0); + // TODO: table implement + const c_opcode_t op = c_opcodes[c_index]; + assert(op); + rv->inst_len = INST_16; + if (!op(rv, inst)) + break; + + // increment the cycles csr + rv->csr_cycle++; } +#endif // ENABLE_COMPUTED_GOTO riscv_user_t rv_userdata(struct riscv_t *rv) { @@ -966,6 +1503,7 @@ void rv_reset(struct riscv_t *rv, riscv_word_t pc) // set the reset address rv->PC = pc; + rv->inst_len = INST_UNKNOWN; // set the default stack pointer rv->X[rv_reg_sp] = DEFAULT_STACK_ADDR; diff --git a/riscv_private.h b/riscv_private.h index 99d84bf5..448236a9 100644 --- a/riscv_private.h +++ b/riscv_private.h @@ -77,6 +77,27 @@ enum { FR4_FMT = 0b00000110000000000000000000000000, // r4-type FR4_RS3 = 0b11111000000000000000000000000000, // ....xxxx....xxxx....xxxx....xxxx + FC_OPCODE = 0b00000000000000000000000000000011, // compressed-instuction + FC_FUNC3 = 0b00000000000000001110000000000000, + // ....xxxx....xxxx....xxxx....xxxx + FC_RS1C = 0b00000000000000000000001110000000, + FC_RS2C = 0b00000000000000000000000000011100, + FC_RS1 = 0b00000000000000000000111110000000, + FC_RS2 = 0b00000000000000000000000001111100, + // ....xxxx....xxxx....xxxx....xxxx + FC_RDC = 0b00000000000000000000000000011100, + FC_RD = 0b00000000000000000000111110000000, + // ....xxxx....xxxx....xxxx....xxxx + FC_IMM_12_10 = 0b00000000000000000001110000000000, // CL,CS,CB + FC_IMM_6_5 = 0b00000000000000000000000001100000, + // ....xxxx....xxxx....xxxx....xxxx + FCI_IMM_12 = 0b00000000000000000001000000000000, + FCI_IMM_6_2 = 0b00000000000000000000000001111100, + // ....xxxx....xxxx....xxxx....xxxx + FCSS_IMM = 0b00000000000000000001111110000000, + // ....xxxx....xxxx....xxxx....xxxx + FCJ_IMM = 0b00000000000000000001111111111100, + // ....xxxx....xxxx....xxxx....xxxx }; // clang-format off @@ -104,6 +125,13 @@ struct riscv_t { uint32_t csr_mepc; uint32_t csr_mip; uint32_t csr_mbadaddr; + + // current instruction length + enum { + INST_UNKNOWN = 0, + INST_16 = 0x02, + INST_32 = 0x04, + }inst_len; }; // decode rd field @@ -210,3 +238,67 @@ static inline uint32_t sign_extend_b(uint32_t x) { return (int32_t)((int8_t) x); } + +// decode rs1 field +static inline uint16_t c_dec_rs1(uint16_t x){ + return (uint16_t)((x & FC_RS1) >> 7U); +} + +// decode rs2 field +static inline uint16_t c_dec_rs2(uint16_t x){ + return (uint16_t)((x & FC_RS2) >> 2U); +} + +// decode rd field +static inline uint16_t c_dec_rd(uint16_t x){ + return (uint16_t)((x & FC_RD) >> 7U); +} + +// decode rs1' field +static inline uint16_t c_dec_rs1c(uint16_t x){ + return (uint16_t)((x & FC_RS1C) >> 7U); +} + +// decode rs2' field +static inline uint16_t c_dec_rs2c(uint16_t x){ + return (uint16_t)((x & FC_RS2C) >> 2U); +} + +// decode rd' field +static inline uint16_t c_dec_rdc(uint16_t x){ + return (uint16_t)((x & FC_RDC) >> 2U); +} + +static inline uint16_t c_dec_cjtype_imm(uint16_t x){ + uint16_t temp = 0; + // ....xxxx....xxxx + temp |= (x & 0b0000000000111000) >> 2; + temp |= (x & 0b0000100000000000) >> 7; + temp |= (x & 0b0000000000000100) << 3; + temp |= (x & 0b0000000010000000) >> 1; + temp |= (x & 0b0000000001000000) << 1; + temp |= (x & 0b0000011000000000) >> 1; + temp |= (x & 0b0000000100000000) << 2; + temp |= (x & 0b0001000000000000) >> 1; + // extend to 16 bit + for(int i = 1; i < 4; ++i){ + temp |= (0x0800 & temp) << i; + } + + return temp; +} + +static inline uint16_t c_dec_cbtype_imm(uint16_t x){ + uint16_t temp = 0; + // ....xxxx....xxxx + temp |= (x & 0b0000000000011000) >> 2; + temp |= (x & 0b0000110000000000) >> 7; + temp |= (x & 0b0000000000000100) << 3; + temp |= (x & 0b0000000001100000) << 1; + temp |= (x & 0b0001000000000000) >> 4; + // extend to 16 bit + for(int i = 1; i < 8; ++i){ + temp |= (0x0100 & temp) << i; + } + return temp; +} From 807aa7350cd328000307207efa50b5d050fcbd33 Mon Sep 17 00:00:00 2001 From: xiaohan484 Date: Wed, 12 Jan 2022 22:09:38 +0800 Subject: [PATCH 2/2] Implement computed-goto path for RV32C This work use TABLE_TYPE_RVC type for RV32C's opcode handler type to dispatch function.All RV32C related function rename to op_c* to cooperate with computed-goto for standard uncompressed function. --- README.md | 3 +- riscv.c | 535 ++++++++++++++++++++++++------------------------ riscv_private.h | 77 ++++--- 3 files changed, 324 insertions(+), 291 deletions(-) diff --git a/README.md b/README.md index 98771349..41219d7d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RISC-V RV32I[MA] emulator with ELF support +# RISC-V RV32I[MAC] emulator with ELF support `rv32emu` is an instruction set architecture (ISA) emulator implementing the 32 bit RISC-V processor model. @@ -35,6 +35,7 @@ should appear when Doom is loaded and executed. `rv32emu` is configurable, and you can modify `Makefile` to fit your expectations: * `ENABLE_RV32M`: Standard Extension for Integer Multiplication and Division * `ENABLE_RV32A`: Standard Extension for Atomic Instructions +* `ENABLE_RV32C`: Standard Extension for Compressed Instructions (RV32C.F excluded) * `Zicsr`: Control and Status Register (CSR) * `Zifencei`: Instruction-Fetch Fence diff --git a/riscv.c b/riscv.c index c28ae326..939a7e15 100644 --- a/riscv.c +++ b/riscv.c @@ -8,109 +8,117 @@ static void rv_except_inst_misaligned(struct riscv_t *rv, uint32_t old_pc) { + // mtvec (Machine Trap-Vector Base Address Register) + // mtvec[MXLEN-1:2]: vector base address + // mtvec[1:0] : vector mode const uint32_t base = rv->csr_mtvec & ~0x3; const uint32_t mode = rv->csr_mtvec & 0x3; - const uint32_t code = 0; // instruction address misaligned + // Exception Code: Instruction Address Misaligned + const uint32_t code = 0; + // mepc (Machine Exception Program Counter) + // mtval (Machine Trap Value Register) : Misaligned Instruction rv->csr_mepc = old_pc; rv->csr_mtval = rv->PC; switch (mode) { - case 0: // DIRECT + case 0: // DIRECT: All exceptions set PC to base rv->PC = base; break; - case 1: // VECTORED - rv->PC = base + rv->inst_len * code; + case 1: // VECTORED: Asynchronous interrupts set PC to base + 4 * code + rv->PC = base + 4 * code; break; } + // mcause (Machine Cause Register): store exception code rv->csr_mcause = code; } -static void rv_breakpoint(struct riscv_t *rv, uint32_t old_pc) -{ - const uint32_t base = rv->csr_mtvec & ~0x3; - const uint32_t mode = rv->csr_mtvec & 0x3; - - const uint32_t code = 3; // breakpoint exception - - rv->csr_mepc = old_pc; - rv->csr_mtval = rv->PC; - - switch (mode) { - case 0: // DIRECT - rv->PC = base; - break; - case 1: // VECTORED - rv->PC = base + 4 * code; - break; - } - static void rv_except_load_misaligned(struct riscv_t *rv, uint32_t addr) { + // mtvec (Machine Trap-Vector Base Address Register) + // mtvec[MXLEN-1:2]: vector base address + // mtvec[1:0] : vector mode const uint32_t base = rv->csr_mtvec & ~0x3; const uint32_t mode = rv->csr_mtvec & 0x3; - const uint32_t code = 4; // load address misaligned + // Exception Code: Load Address Misaligned + const uint32_t code = 4; + // mepc (Machine Exception Program Counter) + // mtval(Machine Trap Value Register) : Misaligned Load Address rv->csr_mepc = rv->PC; rv->csr_mtval = addr; switch (mode) { - case 0: // DIRECT + case 0: // DIRECT: All exceptions set PC to base rv->PC = base; break; - case 1: // VECTORED - rv->PC = base + rv->inst_len * code; + case 1: // VECTORED: Asynchronous interrupts set PC to base + 4 * code + rv->PC = base + 4 * code; break; } + // mcause (Machine Cause Register): store exception code rv->csr_mcause = code; } static void rv_except_store_misaligned(struct riscv_t *rv, uint32_t addr) { + // mtvec (Machine Trap-Vector Base Address Register) + // mtvec[MXLEN-1:2]: vector base address + // mtvec[1:0] : vector mode const uint32_t base = rv->csr_mtvec & ~0x3; const uint32_t mode = rv->csr_mtvec & 0x3; - const uint32_t code = 6; // store address misaligned + // Exception Code: Store Address Misaligned + const uint32_t code = 6; + // mepc (Machine Exception Program Counter) + // mtval(Machine Trap Value Register) : Misaligned Store Address rv->csr_mepc = rv->PC; rv->csr_mtval = addr; switch (mode) { - case 0: // DIRECT + case 0: // DIRECT: All exceptions set PC to base rv->PC = base; break; - case 1: // VECTORED - rv->PC = base + rv->inst_len * code; + case 1: // VECTORED: Asynchronous interrupts set PC to base + 4 * code + rv->PC = base + 4 * code; break; } + // mcause (Machine Cause Register): store exception code rv->csr_mcause = code; } static void rv_except_illegal_inst(struct riscv_t *rv, uint32_t inst) { - /* Jump to the base and record information to mcause */ + // mtvec (Machine Trap-Vector Base Address Register) + // mtvec[MXLEN-1:2]: vector base address + // mtvec[1:0] : vector mode const uint32_t base = rv->csr_mtvec & ~0x3; const uint32_t mode = rv->csr_mtvec & 0x3; - const uint32_t code = 2; // Illegal instruction code + // Exception Code: Illegal Instruction + const uint32_t code = 2; + // mepc (Machine Exception Program Counter) + // mtval(Machine Trap Value Register) : Illegal Instruction rv->csr_mepc = rv->PC; rv->csr_mtval = inst; switch (mode) { - case 0: // DIRECT + case 0: // DIRECT: All exceptions set PC to base rv->PC = base; break; - case 1: // VECTORED + case 1: // VECTORED: Asynchronous interrupts set PC to base + 4 * code rv->PC = base + 4 * code; break; } + // mcause (Machine Cause Register): store exception code rv->csr_mcause = code; } @@ -507,8 +515,12 @@ static bool op_jalr(struct riscv_t *rv, uint32_t inst) if (rd != rv_reg_zero) rv->X[rd] = ra; - // check for exception + // check for exception +#ifdef ENABLE_RV32C + if (rv->PC & 0x1) { +#else if (rv->PC & 0x3) { +#endif rv_except_inst_misaligned(rv, pc); return false; } @@ -531,8 +543,11 @@ static bool op_jal(struct riscv_t *rv, uint32_t inst) if (rd != rv_reg_zero) rv->X[rd] = ra; - // check alignment of PC +#ifdef ENABLE_RV32C + if (rv->PC & 0x1) { +#else if (rv->PC & 0x3) { +#endif rv_except_inst_misaligned(rv, pc); return false; } @@ -633,7 +648,7 @@ static bool op_system(struct riscv_t *rv, uint32_t inst) rv->io.on_ecall(rv); break; case 1: // EBREAK - rv_breakpoint(rv,rv->PC); + rv->io.on_ebreak(rv); break; case 0x002: // URET case 0x102: // SRET @@ -806,16 +821,9 @@ static bool op_amo(struct riscv_t *rv, uint32_t inst) #define op_nmsub OP_UNIMP #define op_nmadd OP_UNIMP -// handler for all unimplemented opcodes -static bool op_unimp(struct riscv_t *rv, uint32_t inst UNUSED) -{ - rv_except_illegal_inst(rv, inst); - return false; -} -#define ENABLE_RV32C 1 #ifdef ENABLE_RV32C -static bool c_op_addi(struct riscv_t *rv, uint16_t inst) +static bool op_caddi(struct riscv_t *rv, uint16_t inst) { uint16_t tmp = (uint16_t)(((inst & FCI_IMM_12) >> 5) | (inst & FCI_IMM_6_2)) >> 2; @@ -838,7 +846,7 @@ static bool c_op_addi(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_addi4spn(struct riscv_t *rv, uint16_t inst) +static bool op_caddi4spn(struct riscv_t *rv, uint16_t inst) { uint16_t temp = 0; temp |= (inst & 0x1800) >> 7; @@ -854,7 +862,7 @@ static bool c_op_addi4spn(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_li(struct riscv_t *rv, uint16_t inst) +static bool op_cli(struct riscv_t *rv, uint16_t inst) { uint16_t tmp = (uint16_t)((inst & 0x1000) >> 7 | (inst & 0x7c) >> 2); const int32_t imm = (tmp & 0x20) ? 0xffffffc0 | tmp : tmp; @@ -865,7 +873,7 @@ static bool c_op_li(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_lui(struct riscv_t *rv, uint16_t inst) +static bool op_clui(struct riscv_t *rv, uint16_t inst) { const uint16_t rd = c_dec_rd(inst); if (rd == 2) { @@ -879,7 +887,7 @@ static bool c_op_lui(struct riscv_t *rv, uint16_t inst) if (imm != 0) rv->X[rd] += imm; - else { /*imm==0 is reserved */ + else { // Code point: nzimm == 0 is reserved } } else if (rd != 0) { // C.LUI @@ -887,18 +895,17 @@ static bool c_op_lui(struct riscv_t *rv, uint16_t inst) const int32_t imm = (tmp & 0x20000) ? (0xfffc0000 | tmp) : tmp; if (imm != 0) rv->X[rd] = imm; - else { /*imm==0 is reserved*/ + else { // Code point 1: nzimm == 0 is reserved } } else { - // HINTS + // Code point 2: rd==x0 is HINTS } rv->PC += rv->inst_len; return true; } -// static bool c_op_XX(struct riscv_t *rv, uint16_t inst) -static bool c_op_srli(struct riscv_t *rv, uint16_t inst) +static bool op_csrli(struct riscv_t *rv, uint16_t inst) { uint32_t temp = 0; temp |= (inst & 0x1000) >> 7; @@ -907,13 +914,13 @@ static bool c_op_srli(struct riscv_t *rv, uint16_t inst) const uint32_t shamt = temp; const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; - // shamt[t]==1 are reserved + // Code point 1: shamt[t]==1 are reserved if (shamt & 0x20) return true; - // HINTS + // Code point 2: rd == x0 is HINTS if (rs1 == 0) return true; - // shamt ==0 is HINT + // Code point 3: shamt == 0 is HINT if (shamt == 0) return true; @@ -922,7 +929,7 @@ static bool c_op_srli(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_srai(struct riscv_t *rv, uint16_t inst) +static bool op_csrai(struct riscv_t *rv, uint16_t inst) { uint32_t temp = 0; temp |= (inst & 0x1000) >> 7; @@ -931,17 +938,17 @@ static bool c_op_srai(struct riscv_t *rv, uint16_t inst) const uint32_t shamt = temp; const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; - // shamt[5]=1 Reserved + // Code point 1: shamt[5] == 1 is reserved if (shamt & 0x20) return true; - // shame ==0 is HINT + // Code point 2: shame == 0 is HINT if (shamt == 0) return true; - // HINT + // Code point 3: rs1 == x0 is HINT if (rs1 == rv_reg_zero) return true; - const uint32_t mask = 0x80000000 | rv->X[rs1]; + const uint32_t mask = 0x80000000 & rv->X[rs1]; rv->X[rs1] >>= shamt; for (unsigned int i = 0; i < shamt; ++i) { @@ -951,12 +958,12 @@ static bool c_op_srai(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_andi(struct riscv_t *rv, uint16_t inst) +static bool op_candi(struct riscv_t *rv, uint16_t inst) { const uint16_t mask = (0x1000 & inst) << 3; uint16_t temp = 0; - for (int i = 0; i < 10; ++i) { + for (int i = 0; i <= 10; ++i) { temp |= (mask >> i); } temp |= (inst & 0x007C) >> 2; @@ -969,22 +976,20 @@ static bool c_op_andi(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_misc_alu(struct riscv_t *rv, uint16_t inst) +static bool op_cmisc_alu(struct riscv_t *rv, uint16_t inst) { - bool exec_result; - // Find actual instruction switch ((inst & 0x0C00) >> 10) { case 0: // C.SRLI - exec_result = c_op_srli(rv, inst); + op_csrli(rv, inst); break; case 1: // C.SRAI - exec_result = c_op_srai(rv, inst); + op_csrai(rv, inst); break; case 2: // C.ANDI - exec_result = c_op_andi(rv, inst); + op_candi(rv, inst); break; - case 3: // Arithmistic + case 3:; // Arithmistic uint32_t temp = 0; temp |= (inst & 0x1000) >> 10; temp |= (inst & 0x0060) >> 5; @@ -1008,10 +1013,8 @@ static bool c_op_misc_alu(struct riscv_t *rv, uint16_t inst) rv->X[rd] = rv->X[rs1] & rv->X[rs2]; break; case 4: - assert(!"RV32F instructions"); - break; case 5: - assert(!"RV32F instructions"); + assert(!"RV64/128C instructions"); break; case 6: case 7: @@ -1027,107 +1030,11 @@ static bool c_op_misc_alu(struct riscv_t *rv, uint16_t inst) break; } - if (!exec_result) { - return false; - } - - rv->PC += rv->inst_len; - return true; -} - -static bool c_op_slli(struct riscv_t *rv, uint16_t inst) -{ - uint32_t temp = 0; - temp |= (inst & FCI_IMM_12) >> 7; - temp |= (inst & FCI_IMM_6_2) >> 2; - - const uint32_t shamt = temp; - const uint32_t rd = c_dec_rd(inst); - - if (rd) { - rv->X[rd] <<= shamt; - } - rv->PC += rv->inst_len; return true; } -// CJ-type -static bool c_op_j(struct riscv_t *rv, uint16_t inst) -{ - uint32_t temp = 0; - const uint32_t imm = sign_extend_h(c_dec_cjtype_imm(inst)); - - rv->PC += imm; - return false; -} - -static bool c_op_jal(struct riscv_t *rv, uint16_t inst) -{ - const uint32_t imm = sign_extend_h(c_dec_cjtype_imm(inst)); - - rv->X[1] = rv->PC + 2; - rv->PC += imm; - - return false; -} -// CB-type - -// CB-type -static bool c_op_beqz(struct riscv_t *rv, uint16_t inst) -{ - const uint32_t imm = sign_extend_h(c_dec_cbtype_imm(inst)); - const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; - - if (!rv->X[rs1]) { - rv->PC += imm; - } else { - rv->PC += rv->inst_len; - } - - return false; -} - -static bool c_op_bnez(struct riscv_t *rv, uint16_t inst) -{ - const uint32_t imm = sign_extend_h(c_dec_cbtype_imm(inst)); - const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; - - if (rv->X[rs1]) { - rv->PC += imm; - } else { - rv->PC += rv->inst_len; - } - - return false -} -#else -#define c_op_addi4spn NULL -#define c_op_addi NULL - -static bool c_op_jal(struct riscv_t *rv, uint16_t inst) -{ - uint32_t temp = 0; - // ....xxxx....xxxx - temp |= (inst & 0b0000000000111000) >> 2; - temp |= (inst & 0b0000100000000000) >> 7; - temp |= (inst & 0b0000000000000100) << 3; - temp |= (inst & 0b0000000010000000) >> 1; - temp |= (inst & 0b0000000001000000) << 1; - temp |= (inst & 0b0000011000000000) >> 1; - temp |= (inst & 0b0000000100000000) << 2; - temp |= (inst & 0b0001000000000000) >> 1; - - const uint32_t imm = sign_extend_h(temp); - - rv->X[1] = rv->PC + 2; - rv->PC += imm; - - return true; -} - -// CR-type -static bool c_op_slli(struct riscv_t *rv, uint16_t inst) +static bool op_cslli(struct riscv_t *rv, uint16_t inst) { uint32_t temp = 0; temp |= (inst & FCI_IMM_12) >> 7; @@ -1144,74 +1051,34 @@ static bool c_op_slli(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_lwsp(struct riscv_t *rv, uint16_t inst) +// CI-type +static bool op_clwsp(struct riscv_t *rv, uint16_t inst) { uint16_t temp = 0; - temp |= ((inst & FCI_IMM_6_2) | 0b1110000) >> 2; - temp |= (inst & FCI_IMM_12) >> 7; - temp |= ((inst & FCI_IMM_6_2) | 0b0001100) << 4; + temp |= (inst & 0x70) >> 2; + temp |= (inst & 0x0c) << 4; + temp |= (inst & 0x1000) >> 7; const uint16_t imm = temp; const uint16_t rd = c_dec_rd(inst); - const uint16_t addr = rv->X[2] + imm; + const uint32_t addr = rv->X[rv_reg_sp] + imm; + + // reserved for rd == 0 + if (rd == 0) + return true; if (addr & 3) { rv_except_load_misaligned(rv, addr); return false; } - rv->X[rd] = rv->io.mem_read_w(rv, addr); + rv->X[rd] = rv->io.mem_read_w(rv, addr); rv->PC += rv->inst_len; return true; } -static bool c_op_cr(struct riscv_t *rv, uint16_t inst) -{ - const uint32_t rs1 = c_dec_rs1(inst); - const uint32_t rs2 = c_dec_rs2(inst); - const uint32_t rd = rs1; - - switch ((inst & 0x1000) >> 12) { - case 0: - if (rs2) { - rv->X[rd] = rv->X[rs2]; - rv->PC += rv->inst_len; - } else { - rv->PC = rv->X[rs1]; - - return false; - } - break; - case 1: - if (rs1) { - if (rs2) { - rv->X[rd] = rv->X[rs1] + rv->X[rs2]; - rv->PC += rv->inst_len; - } else { - rv->X[1] = rv->PC + 2; - rv->PC = rv->X[rs1]; - - if (rv->PC & 1) { - rv_except_inst_misaligned(rv, rv->PC); - return false; - } - - return false; - } - } else { - rv->io.on_ebreak(rv); - } - break; - default: - assert(!"Should be unreachbale."); - break; - } - - return true; -} - // CSS-type -static bool c_op_swsp(struct riscv_t *rv, uint16_t inst) +static bool op_cswsp(struct riscv_t *rv, uint16_t inst) { const uint16_t imm = (inst & 0x1e00) >> 7 | (inst & 0x180) >> 1; const uint16_t rs2 = c_dec_rs2(inst); @@ -1229,7 +1096,7 @@ static bool c_op_swsp(struct riscv_t *rv, uint16_t inst) } // CL-type -static bool c_op_lw(struct riscv_t *rv, uint16_t inst) +static bool op_clw(struct riscv_t *rv, uint16_t inst) { uint16_t temp = 0; temp |= (inst & 0b0000000001000000) >> 4; @@ -1252,9 +1119,8 @@ static bool c_op_lw(struct riscv_t *rv, uint16_t inst) } // CS-type -static bool c_op_sw(struct riscv_t *rv, uint16_t inst) +static bool op_csw(struct riscv_t *rv, uint16_t inst) { - uint32_t temp = 0; // ....xxxx....xxxx temp |= (inst & 0b0000000001000000) >> 4; @@ -1277,13 +1143,135 @@ static bool c_op_sw(struct riscv_t *rv, uint16_t inst) return true; } -static bool c_op_flw(struct riscv_t *rv, uint16_t inst) +// CJ-type +static bool op_cj(struct riscv_t *rv, uint16_t inst) { - rv->PC += rv->inst_len; + const int32_t imm = (c_dec_cjtype_imm(inst)); + rv->PC += imm; + if (rv->PC & 0x1) { + rv_except_inst_misaligned(rv, rv->PC); + return false; + } + // can branch + return false; +} + +static bool op_cjal(struct riscv_t *rv, uint16_t inst) +{ + const int32_t imm = sign_extend_h(c_dec_cjtype_imm(inst)); + rv->X[1] = rv->PC + 2; + rv->PC += imm; + if (rv->PC & 0x1) { + rv_except_inst_misaligned(rv, rv->PC); + return false; + } + // can branch + return false; +} + +// CR-type +static bool op_ccr(struct riscv_t *rv, uint16_t inst) +{ + const uint32_t rs1 = c_dec_rs1(inst); + const uint32_t rs2 = c_dec_rs2(inst); + const uint32_t rd = rs1; + + switch ((inst & 0x1000) >> 12) { + case 0: + if (rs2) { + // C.MV + rv->X[rd] = rv->X[rs2]; + rv->PC += rv->inst_len; + if (rd == rv_reg_zero) + rv->X[rv_reg_zero] = 0; + } else { + // C.JR + rv->PC = rv->X[rs1]; + return false; + } + break; + case 1: + if (rs1 == 0 && rs2 == 0) // C.EBREAK + rv->io.on_ebreak(rv); + else if (rs1 && rs2) { // C.ADD + rv->X[rd] = rv->X[rs1] + rv->X[rs2]; + rv->PC += rv->inst_len; + if (rd == rv_reg_zero) + rv->X[rv_reg_zero] = 0; + } else if (rs1 && rs2 == 0) { // rs1 != zero AND rs2 == zero + // C.JALR + // Unconditional jump and store PC+2 to ra + const int32_t jump_to = rv->X[rs1]; + rv->X[rv_reg_ra] = rv->PC + rv->inst_len; + rv->PC = jump_to; + if (rv->PC & 0x1) { + rv_except_inst_misaligned(rv, rv->PC); + return false; + } + // can branch + return false; + } else // rs2 !=zero AND rd == zero + rv->PC += rv->inst_len; // Hint + break; + default: + assert(!"Should be unreachable."); + break; + } + return true; } + +// CB-type +static bool op_cbeqz(struct riscv_t *rv, uint16_t inst) +{ + const uint32_t imm = sign_extend_h(c_dec_cbtype_imm(inst)); + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + rv->PC += (!rv->X[rs1]) ? imm : rv->inst_len; + // can branch + return false; +} + +static bool op_cbnez(struct riscv_t *rv, uint16_t inst) +{ + const uint32_t imm = sign_extend_h(c_dec_cbtype_imm(inst)); + const uint32_t rs1 = c_dec_rs1c(inst) | 0x08; + rv->PC += (rv->X[rs1]) ? imm : rv->inst_len; + // can branch + return false; +} +#else +#define op_caddi4spn OP_UNIMP +#define op_caddi OP_UNIMP +#define op_cswsp OP_UNIMP +#define op_cli OP_UNIMP +#define op_cslli OP_UNIMP +#define op_cjal OP_UNIMP +#define op_clw OP_UNIMP +#define op_clwsp OP_UNIMP +#define op_clui OP_UNIMP +#define op_cmisc_alu OP_UNIMP +#define op_cjalr OP_UNIMP +#define op_cj OP_UNIMP +#define op_cbeqz OP_UNIMP +#define op_cbnez OP_UNIMP +#define op_csw OP_UNIMP #endif // ENABLE_RV32C +/* No RV32C.F support */ +#define op_cfldsp OP_UNIMP +#define op_cflwsp OP_UNIMP +#define op_cfswsp OP_UNIMP +#define op_cfsdsp OP_UNIMP +#define op_cfld OP_UNIMP +#define op_cflw OP_UNIMP +#define op_cfsw OP_UNIMP +#define op_cfsd OP_UNIMP + +// RV32 opcode handler type +typedef bool (*opcode_t)(struct riscv_t *rv, uint32_t inst); +// RV32C opcode handler type +typedef bool (*c_opcode_t)(struct riscv_t *rv, uint16_t inst); + // handler for all unimplemented opcodes static bool op_unimp(struct riscv_t *rv, uint32_t inst UNUSED) { @@ -1291,10 +1279,6 @@ static bool op_unimp(struct riscv_t *rv, uint32_t inst UNUSED) return false; } -// opcode handler type -typedef bool (*opcode_t)(struct riscv_t *rv, uint32_t inst); -typedef bool (*c_opcode_t)(struct riscv_t *rv, uint16_t inst); - void rv_step(struct riscv_t *rv, int32_t cycles) { assert(rv); @@ -1305,9 +1289,11 @@ void rv_step(struct riscv_t *rv, int32_t cycles) #ifdef ENABLE_COMPUTED_GOTO #define OP(instr) &&op_##instr #define TABLE_TYPE const void * +#define TABLE_TYPE_RVC const void * #else // ENABLE_COMPUTED_GOTO = false #define OP(instr) op_##instr #define TABLE_TYPE const opcode_t +#define TABLE_TYPE_RVC const c_opcode_t #endif // clang-format off @@ -1318,37 +1304,38 @@ void rv_step(struct riscv_t *rv, int32_t cycles) OP(madd), OP(msub), OP(nmsub), OP(nmadd), OP(fp), OP(unimp), OP(unimp), OP(unimp), // 10 OP(branch), OP(jalr), OP(unimp), OP(jal), OP(system), OP(unimp), OP(unimp), OP(unimp), // 11 }; - - - static const c_opcode_t c_opcodes[] = { - // 00 01 10 11 - c_op_addi4spn, c_op_addi, c_op_slli, NULL, // 000 - c_op_fld, c_op_jal, c_op_fldsp, NULL, // 001 - c_op_lw, c_op_li, c_op_lwsp, NULL, // 010 - c_op_flw, c_op_lui, c_op_flwsp, NULL, // 011 - NULL, c_op_misc_alu, c_op_cr, NULL, // 100 - c_op_fsd, c_op_j, c_op_fsdsp, NULL, // 101 - c_op_sw, c_op_beqz, c_op_swsp, NULL, // 110 - c_op_fsw, c_op_bnez, c_op_fswsp, NULL, // 111 + TABLE_TYPE_RVC jump_table_rvc[] = { + // 00 01 10 11 + OP(caddi4spn), OP(caddi), OP(cslli), OP(unimp), // 000 + OP(cfld), OP(cjal), OP(cfldsp), OP(unimp), // 001 + OP(clw), OP(cli), OP(clwsp), OP(unimp), // 010 + OP(cflw), OP(clui), OP(cflwsp), OP(unimp), // 011 + OP(unimp), OP(cmisc_alu), OP(ccr), OP(unimp), // 100 + OP(cfsd), OP(cj), OP(cfsdsp), OP(unimp), // 101 + OP(csw), OP(cbeqz), OP(cswsp), OP(unimp), // 110 + OP(cfsw), OP(cbnez), OP(cfswsp), OP(unimp), // 111 }; - // clang-format on #ifdef ENABLE_COMPUTED_GOTO -#define DISPATCH() \ - { \ - if (rv->csr_cycle >= cycles_target || rv->halt) \ - goto quit; \ - /* fetch the next instruction */ \ - inst = rv->io.mem_ifetch(rv, rv->PC); \ - /* standard uncompressed instruction */ \ - if ((inst & 3) == 3) { \ - index = (inst & INST_6_2) >> 2; \ - goto *jump_table[index]; \ - } else { \ - /* TODO: compressed instruction*/ \ - assert(!"Unreachable"); \ - } \ +#define DISPATCH() \ + { \ + if (rv->csr_cycle >= cycles_target || rv->halt) \ + goto quit; \ + /* fetch the next instruction */ \ + inst = rv->io.mem_ifetch(rv, rv->PC); \ + /* standard uncompressed instruction */ \ + if ((inst & 3) == 3) { \ + uint32_t index = (inst & INST_6_2) >> 2; \ + rv->inst_len = INST_32; \ + goto *jump_table[index]; \ + } else { \ + /* Compressed Extension Instruction */ \ + inst &= 0x0000FFFF; \ + int16_t c_index = (inst & FC_FUNC3) >> 11 | (inst & FC_OPCODE); \ + rv->inst_len = INST_16; \ + goto *jump_table_rvc[c_index]; \ + } \ } #define EXEC(instr) \ @@ -1377,6 +1364,23 @@ void rv_step(struct riscv_t *rv, int32_t cycles) TARGET(jalr) TARGET(jal) TARGET(system) +#ifdef ENABLE_RV32C + TARGET(caddi4spn) + TARGET(caddi) + TARGET(cslli) + TARGET(cjal) + TARGET(clw) + TARGET(cli) + TARGET(clwsp) + TARGET(clui) + TARGET(cmisc_alu) + TARGET(ccr) + TARGET(cj) + TARGET(csw) + TARGET(cbeqz) + TARGET(cswsp) + TARGET(cbnez) +#endif #ifdef ENABLE_Zifencei TARGET(misc_mem) #endif @@ -1410,10 +1414,12 @@ void rv_step(struct riscv_t *rv, int32_t cycles) // increment the cycles csr rv->csr_cycle++; } else { - // TODO: compressed instruction - const uint16_t c_index = (inst & FR_C_15_13 >> 11) | (inst & FR_C_1_0); - // TODO: table implement - const c_opcode_t op = c_opcodes[c_index]; + // standard compressed instruction + const uint16_t c_index = + (inst & FR_C_15_13 >> 11) | (inst & FR_C_1_0); + + // dispactch c_opcode (compressed instructions) + TABLE_TYPE_RVC op = jump_tablec[c_index]; assert(op); rv->inst_len = INST_16; if (!op(rv, inst)) @@ -1421,8 +1427,9 @@ void rv_step(struct riscv_t *rv, int32_t cycles) // increment the cycles csr rv->csr_cycle++; -} + } #endif // ENABLE_COMPUTED_GOTO +} riscv_user_t rv_userdata(struct riscv_t *rv) { diff --git a/riscv_private.h b/riscv_private.h index 448236a9..ce443578 100644 --- a/riscv_private.h +++ b/riscv_private.h @@ -101,6 +101,12 @@ enum { }; // clang-format off +enum { + INST_UNKNOWN = 0, + INST_16 = 2, + INST_32 = 4, +}; + struct riscv_t { bool halt; @@ -127,11 +133,7 @@ struct riscv_t { uint32_t csr_mbadaddr; // current instruction length - enum { - INST_UNKNOWN = 0, - INST_16 = 0x02, - INST_32 = 0x04, - }inst_len; + uint8_t inst_len; }; // decode rd field @@ -239,56 +241,78 @@ static inline uint32_t sign_extend_b(uint32_t x) return (int32_t)((int8_t) x); } +#ifdef ENABLE_RV32C +enum { + // ....xxxx....xxxx + CJ_IMM_11 = 0b0001000000000000, + CJ_IMM_4 = 0b0000100000000000, + CJ_IMM_9_8 = 0b0000011000000000, + CJ_IMM_10 = 0b0000000100000000, + CJ_IMM_6 = 0b0000000010000000, + CJ_IMM_7 = 0b0000000001000000, + CJ_IMM_3_1 = 0b0000000000111000, + CJ_IMM_5 = 0b0000000000000100, +}; + // decode rs1 field -static inline uint16_t c_dec_rs1(uint16_t x){ +static inline uint16_t c_dec_rs1(uint16_t x) +{ return (uint16_t)((x & FC_RS1) >> 7U); } // decode rs2 field -static inline uint16_t c_dec_rs2(uint16_t x){ +static inline uint16_t c_dec_rs2(uint16_t x) +{ return (uint16_t)((x & FC_RS2) >> 2U); } // decode rd field -static inline uint16_t c_dec_rd(uint16_t x){ +static inline uint16_t c_dec_rd(uint16_t x) +{ return (uint16_t)((x & FC_RD) >> 7U); } // decode rs1' field -static inline uint16_t c_dec_rs1c(uint16_t x){ +static inline uint16_t c_dec_rs1c(uint16_t x) +{ return (uint16_t)((x & FC_RS1C) >> 7U); } // decode rs2' field -static inline uint16_t c_dec_rs2c(uint16_t x){ +static inline uint16_t c_dec_rs2c(uint16_t x) +{ return (uint16_t)((x & FC_RS2C) >> 2U); } // decode rd' field -static inline uint16_t c_dec_rdc(uint16_t x){ +static inline uint16_t c_dec_rdc(uint16_t x) +{ return (uint16_t)((x & FC_RDC) >> 2U); } -static inline uint16_t c_dec_cjtype_imm(uint16_t x){ +static inline int32_t c_dec_cjtype_imm(uint16_t x) +{ uint16_t temp = 0; - // ....xxxx....xxxx - temp |= (x & 0b0000000000111000) >> 2; - temp |= (x & 0b0000100000000000) >> 7; - temp |= (x & 0b0000000000000100) << 3; - temp |= (x & 0b0000000010000000) >> 1; - temp |= (x & 0b0000000001000000) << 1; - temp |= (x & 0b0000011000000000) >> 1; - temp |= (x & 0b0000000100000000) << 2; - temp |= (x & 0b0001000000000000) >> 1; - // extend to 16 bit - for(int i = 1; i < 4; ++i){ + + temp |= (x & CJ_IMM_3_1) >> 2; + temp |= (x & CJ_IMM_4) >> 7; + temp |= (x & CJ_IMM_5) << 3; + temp |= (x & CJ_IMM_6) >> 1; + temp |= (x & CJ_IMM_7) << 1; + temp |= (x & CJ_IMM_9_8) >> 1; + temp |= (x & CJ_IMM_10) << 2; + temp |= (x & CJ_IMM_11) >> 1; + + for (int i = 1; i <= 4; ++i) { temp |= (0x0800 & temp) << i; } - return temp; + // extend to 16 bit + return (int32_t)(int16_t) temp; } -static inline uint16_t c_dec_cbtype_imm(uint16_t x){ +static inline uint16_t c_dec_cbtype_imm(uint16_t x) +{ uint16_t temp = 0; // ....xxxx....xxxx temp |= (x & 0b0000000000011000) >> 2; @@ -297,8 +321,9 @@ static inline uint16_t c_dec_cbtype_imm(uint16_t x){ temp |= (x & 0b0000000001100000) << 1; temp |= (x & 0b0001000000000000) >> 4; // extend to 16 bit - for(int i = 1; i < 8; ++i){ + for (int i = 1; i <= 8; ++i) { temp |= (0x0100 & temp) << i; } return temp; } +#endif