diff --git a/Makefile b/Makefile index 804e4f6e..1f81a044 100644 --- a/Makefile +++ b/Makefile @@ -96,8 +96,9 @@ all: $(BIN) OBJS := \ map.o \ utils.o \ - decode.o \ emulate.o \ + decode.o \ + riscv.o \ io.o \ elf.o \ main.o \ diff --git a/src/decode.c b/src/decode.c index 88d1aae8..a94619dd 100644 --- a/src/decode.c +++ b/src/decode.c @@ -4,9 +4,10 @@ */ #include -#include +#include #include "decode.h" +#include "riscv_private.h" /* decode rd field * rd = insn[11:7] @@ -1653,19 +1654,18 @@ static inline bool op_cbnez(struct rv_insn_t *ir, const uint32_t insn) #define op_cfsd OP_UNIMP /* handler for all unimplemented opcodes */ -static inline bool op_unimp(struct rv_insn_t *rv_insn UNUSED, - uint32_t insn UNUSED) +static inline bool op_unimp(struct rv_insn_t *ir UNUSED, uint32_t insn UNUSED) { return false; } /* RV32 decode handler type */ -typedef bool (*decode_t)(struct rv_insn_t *rv_insn, uint32_t insn); +typedef bool (*decode_t)(struct rv_insn_t *ir, uint32_t insn); /* decode RISC-V instruction */ -bool rv_decode(struct rv_insn_t *ir, uint32_t insn, uint8_t *insn_len) +bool rv_decode(struct rv_insn_t *ir, uint32_t insn) { - assert(ir && insn_len); + assert(ir); #define OP_UNIMP op_unimp #define OP(insn) op_##insn @@ -1704,7 +1704,7 @@ bool rv_decode(struct rv_insn_t *ir, uint32_t insn, uint8_t *insn_len) if ((insn & FC_OPCODE) != 3) { insn &= 0x0000FFFF; const uint16_t c_index = (insn & FC_FUNC3) >> 11 | (insn & FC_OPCODE); - *insn_len = INSN_16; + ir->insn_len = INSN_16; /* decode instruction (compressed instructions) */ const decode_t op = rvc_jump_table[c_index]; @@ -1715,7 +1715,7 @@ bool rv_decode(struct rv_insn_t *ir, uint32_t insn, uint8_t *insn_len) /* standard uncompressed instruction */ const uint32_t index = (insn & INSN_6_2) >> 2; - *insn_len = INSN_32; + ir->insn_len = INSN_32; /* decode instruction */ const decode_t op = rv_jump_table[index]; @@ -1725,3 +1725,18 @@ bool rv_decode(struct rv_insn_t *ir, uint32_t insn, uint8_t *insn_len) #undef OP_UNIMP #undef OP } + +/* clear all block in the block map */ +void block_map_clear(struct block_map *map) +{ + assert(map); + for (uint32_t i = 0; i < map->block_capacity; i++) { + struct block *block = map->map[i]; + if (block) { + free(block->ir); + free(block); + map->map[i] = NULL; + } + } + map->size = 0; +} diff --git a/src/decode.h b/src/decode.h index 7cf3c7e8..34442a91 100644 --- a/src/decode.h +++ b/src/decode.h @@ -219,7 +219,6 @@ enum { /* clang-format on */ enum { - INSN_UNKNOWN = 0, INSN_16 = 2, INSN_32 = 4, }; @@ -236,21 +235,36 @@ struct rv_insn_t { #if RV32_HAS(EXT_C) uint8_t shamt; #endif + + /* instruction length */ + uint8_t insn_len; +}; + +/* translated basic block */ +struct block { + /* number of instructions encompased */ + uint32_t insn_number; + /* address range of the basic block */ + uint32_t pc_start, pc_end; + /* maximum of instructions encompased */ + uint32_t insn_capacity; + /* block predictoin */ + struct block *predict; + /* memory blocks */ + struct rv_insn_t *ir; }; -/* sign extend a 16 bit value */ -static inline uint32_t sign_extend_h(const uint32_t x) -{ - return (int32_t) ((int16_t) x); -} +struct block_map { + /* max number of entries in the block map */ + uint32_t block_capacity; + /* number of entries currently in the map */ + uint32_t size; + /* block map */ + struct block **map; +}; -/* sign extend an 8 bit value */ -static inline uint32_t sign_extend_b(const uint32_t x) -{ - return (int32_t) ((int8_t) x); -} +/* clear all block in the block map */ +void block_map_clear(struct block_map *map); /* decode the RISC-V instruction */ -bool rv_decode(struct rv_insn_t *rv_insn, - const uint32_t insn, - uint8_t *insn_len); +bool rv_decode(struct rv_insn_t *ir, const uint32_t insn); diff --git a/src/emulate.c b/src/emulate.c index 56105173..2c311077 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -4,9 +4,7 @@ */ #include -#include #include -#include #include #include @@ -52,7 +50,7 @@ enum { static void rv_exception_default_handler(struct riscv_t *rv) { - rv->csr_mepc += rv->insn_len; + rv->csr_mepc += rv->compressed ? INSN_16 : INSN_32; rv->PC = rv->csr_mepc; /* mret */ } @@ -256,8 +254,11 @@ static bool insn_is_misaligned(uint32_t pc) ); } -static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir) +static bool emulate(struct riscv_t *rv, struct rv_insn_t *ir) { + /* check instruction is compressed or not */ + rv->compressed = (ir->insn_len == INSN_16); + switch (ir->opcode) { /* RV32I Base Instruction Set */ case rv_insn_lui: /* LUI: Load Upper Immediate */ @@ -286,7 +287,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir) rv->PC += ir->imm; /* link with return address */ if (ir->rd) - rv->X[ir->rd] = pc + rv->insn_len; + rv->X[ir->rd] = pc + ir->insn_len; /* check instruction misaligned */ if (insn_is_misaligned(rv->PC)) { rv_except_insn_misaligned(rv, pc); @@ -308,7 +309,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir) rv->PC = (rv->X[ir->rs1] + ir->imm) & ~1U; /* link */ if (ir->rd) - rv->X[ir->rd] = pc + rv->insn_len; + rv->X[ir->rd] = pc + ir->insn_len; /* check instruction misaligned */ if (insn_is_misaligned(rv->PC)) { rv_except_insn_misaligned(rv, pc); @@ -963,7 +964,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir) rv->X[ir->rd] += (int16_t) ir->imm; break; case rv_insn_cjal: - rv->X[1] = rv->PC + rv->insn_len; + rv->X[1] = rv->PC + ir->insn_len; rv->PC += ir->imm; if (rv->PC & 0x1) { rv_except_insn_misaligned(rv, rv->PC); @@ -1055,11 +1056,11 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir) * the value in register rs1' is zero. It expands to beq rs1', x0, * offset[8:1]. */ - rv->PC += (!rv->X[ir->rs1]) ? (uint32_t) ir->imm : rv->insn_len; + rv->PC += (!rv->X[ir->rs1]) ? (uint32_t) ir->imm : ir->insn_len; /* can branch */ return true; case rv_insn_cbnez: /* C.BEQZ */ - rv->PC += (rv->X[ir->rs1]) ? (uint32_t) ir->imm : rv->insn_len; + rv->PC += (rv->X[ir->rs1]) ? (uint32_t) ir->imm : ir->insn_len; /* can branch */ return true; case rv_insn_cslli: /* C.SLLI */ @@ -1093,7 +1094,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir) case rv_insn_cjalr: { /* C.JALR */ /* Unconditional jump and store PC+2 to ra */ const int32_t jump_to = rv->X[ir->rs1]; - rv->X[rv_reg_ra] = rv->PC + rv->insn_len; + rv->X[rv_reg_ra] = rv->PC + ir->insn_len; rv->PC = jump_to; if (rv->PC & 0x1) { rv_except_insn_misaligned(rv, rv->PC); @@ -1125,146 +1126,201 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir) } /* step over instruction */ - rv->PC += rv->insn_len; + rv->PC += ir->insn_len; return true; } -void rv_step(struct riscv_t *rv, int32_t cycles) +static bool insn_is_branch(uint8_t opcode) { - assert(rv); - uint32_t insn; - struct rv_insn_t ir; - const uint64_t cycles_target = rv->csr_cycle + cycles; - - while (rv->csr_cycle < cycles_target && !rv->halt) { - /* enforce zero register */ - rv->X[rv_reg_zero] = 0; - - /* fetch the next instruction */ - insn = rv->io.mem_ifetch(rv, rv->PC); - memset(&ir, 0, sizeof(struct rv_insn_t)); - - /* decode the instruction */ - if (!rv_decode(&ir, insn, &rv->insn_len)) { - rv_except_illegal_insn(rv, insn); - break; - } - - /* execute the instruciton */ - if (!rv_emulate(rv, &ir)) - break; - - /* increment the cycles csr */ - rv->csr_cycle++; + switch (opcode) { + case rv_insn_jal: + case rv_insn_jalr: + case rv_insn_beq: + case rv_insn_bne: + case rv_insn_blt: + case rv_insn_bge: + case rv_insn_bltu: + case rv_insn_bgeu: + case rv_insn_ecall: + case rv_insn_ebreak: + case rv_insn_mret: +#if RV32_HAS(EXT_C) + case rv_insn_cj: + case rv_insn_cjr: + case rv_insn_cjal: + case rv_insn_cjalr: + case rv_insn_cbeqz: + case rv_insn_cbnez: + case rv_insn_cebreak: +#endif +#if RV32_HAS(Zifencei) + case rv_insn_fencei: +#endif + return true; } + return false; } -riscv_user_t rv_userdata(struct riscv_t *rv) +/* hash function is used when mapping address into the block map */ +static uint32_t hash(size_t k) { - assert(rv); - return rv->userdata; + k ^= k << 21; + k ^= k >> 17; +#if (SIZE_MAX > 0xFFFFFFFF) + k ^= k >> 35; + k ^= k >> 51; +#endif + return k; } -bool rv_set_pc(struct riscv_t *rv, riscv_word_t pc) +/* allocate a basic block */ +static struct block *block_alloc(const uint8_t bits) { - assert(rv); -#if RV32_HAS(EXT_C) - if (pc & 1) -#else - if (pc & 3) -#endif - return false; - - rv->PC = pc; - return true; + struct block *block = malloc(sizeof(struct block)); + block->insn_capacity = 1 << bits; + block->insn_number = 0; + block->predict = NULL; + block->ir = malloc(block->insn_capacity * sizeof(struct rv_insn_t)); + return block; } -riscv_word_t rv_get_pc(struct riscv_t *rv) +/* insert a block into block map */ +static void block_insert(struct block_map *map, const struct block *block) { - assert(rv); - return rv->PC; + assert(map && block); + const uint32_t mask = map->block_capacity - 1; + uint32_t index = hash(block->pc_start); + + /* insert into the block map */ + for (;; index++) { + if (!map->map[index & mask]) { + map->map[index & mask] = (struct block *) block; + break; + } + } + map->size++; } -void rv_set_reg(struct riscv_t *rv, uint32_t reg, riscv_word_t in) +/* try to locate an already translated block in the block map */ +static struct block *block_find(const struct block_map *map, const uint32_t pc) { - assert(rv); - if (reg < RV_NUM_REGS && reg != rv_reg_zero) - rv->X[reg] = in; + assert(map); + uint32_t index = hash(pc); + const uint32_t mask = map->block_capacity - 1; + + /* find block in block map */ + for (;; index++) { + struct block *block = map->map[index & mask]; + if (!block) + return NULL; + + if (block->pc_start == pc) + return block; + } + return NULL; } -riscv_word_t rv_get_reg(struct riscv_t *rv, uint32_t reg) +/* execute a basic block */ +static bool block_emulate(struct riscv_t *rv, const struct block *block) { - assert(rv); - if (reg < RV_NUM_REGS) - return rv->X[reg]; + /* execute the block */ + for (uint32_t i = 0; i < block->insn_number; i++) { + /* enforce zero register */ + rv->X[rv_reg_zero] = 0; + + /* execute the instruction */ + if (!emulate(rv, block->ir + i)) + return false; - return ~0U; + /* increment the cycles csr */ + rv->csr_cycle++; + } + return true; } -struct riscv_t *rv_create(const struct riscv_io_t *io, riscv_user_t userdata) +static void block_translate(struct riscv_t *rv, struct block *block) { - assert(io); - struct riscv_t *rv = (struct riscv_t *) malloc(sizeof(struct riscv_t)); - memset(rv, 0, sizeof(struct riscv_t)); + block->pc_start = rv->PC; + block->pc_end = rv->PC; - /* copy over the IO interface */ - memcpy(&rv->io, io, sizeof(struct riscv_io_t)); + /* translate the basic block */ + while (block->insn_number < block->insn_capacity) { + struct rv_insn_t *ir = block->ir + block->insn_number; + memset(ir, 0, sizeof(struct rv_insn_t)); - /* copy over the userdata */ - rv->userdata = userdata; + /* fetch the next instruction */ + const uint32_t insn = rv->io.mem_ifetch(rv, block->pc_end); - /* reset */ - rv_reset(rv, 0U); + /* decode the instruction */ + if (!rv_decode(ir, insn)) { + rv_except_illegal_insn(rv, insn); + break; + } - return rv; -} + /* compute the end of pc */ + block->pc_end += ir->insn_len; + block->insn_number++; -void rv_halt(struct riscv_t *rv) -{ - rv->halt = true; + /* stop on branch */ + if (insn_is_branch(ir->opcode)) + break; + } } -bool rv_has_halted(struct riscv_t *rv) +static struct block *block_find_or_translate(struct riscv_t *rv, + struct block *prev) { - return rv->halt; -} + struct block_map *map = &rv->block_map; + /* lookup the next block in the block map */ + struct block *next = block_find(map, rv->PC); + + if (!next) { + if (map->size * 1.25 > map->block_capacity) { + block_map_clear(map); + prev = NULL; + } -void rv_delete(struct riscv_t *rv) -{ - assert(rv); - free(rv); + /* allocate a new block */ + next = block_alloc(10); + + /* translate the basic block */ + block_translate(rv, next); + + /* insert the block into block map */ + block_insert(&rv->block_map, next); + + /* update the block prediction */ + if (prev) + prev->predict = next; + } + + return next; } -void rv_reset(struct riscv_t *rv, riscv_word_t pc) +void rv_step(struct riscv_t *rv, int32_t cycles) { assert(rv); - memset(rv->X, 0, sizeof(uint32_t) * RV_NUM_REGS); - - /* set the reset address */ - rv->PC = pc; - rv->insn_len = INSN_UNKNOWN; - /* set the default stack pointer */ - rv->X[rv_reg_sp] = DEFAULT_STACK_ADDR; + /* find or translate a block for starting PC */ + struct block *prev = NULL, *next = NULL; + const uint64_t cycles_target = rv->csr_cycle + cycles; - /* reset the csrs */ - rv->csr_mtvec = 0; - rv->csr_cycle = 0; - rv->csr_mstatus = 0; + while (rv->csr_cycle < cycles_target && !rv->halt) { + /* check the block prediction first */ + if (prev && prev->predict && prev->predict->pc_start == rv->PC) { + next = prev->predict; + } else { + next = block_find_or_translate(rv, prev); + } -#if RV32_HAS(EXT_F) - /* reset float registers */ - memset(rv->F, 0, sizeof(float) * RV_NUM_REGS); - rv->csr_fcsr = 0; -#endif + assert(next); - rv->halt = false; -} + /* execute the block */ + if (!block_emulate(rv, next)) + break; -/* FIXME: provide real implementation */ -void rv_stats(struct riscv_t *rv) -{ - printf("CSR cycle count: %" PRIu64 "\n", rv->csr_cycle); + prev = next; + } } void ebreak_handler(struct riscv_t *rv) diff --git a/src/riscv.c b/src/riscv.c new file mode 100644 index 00000000..eb63d1cd --- /dev/null +++ b/src/riscv.c @@ -0,0 +1,131 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ + +#include +#include +#include +#include +#include + +#include "riscv_private.h" + +/* initialize the block map */ +static void block_map_init(struct block_map *map, const uint8_t bits) +{ + map->block_capacity = 1 << bits; + map->size = 0; + map->map = calloc(map->block_capacity, sizeof(struct block *)); +} + +riscv_user_t rv_userdata(struct riscv_t *rv) +{ + assert(rv); + return rv->userdata; +} + +bool rv_set_pc(struct riscv_t *rv, riscv_word_t pc) +{ + assert(rv); +#if RV32_HAS(EXT_C) + if (pc & 1) +#else + if (pc & 3) +#endif + return false; + + rv->PC = pc; + return true; +} + +riscv_word_t rv_get_pc(struct riscv_t *rv) +{ + assert(rv); + return rv->PC; +} + +void rv_set_reg(struct riscv_t *rv, uint32_t reg, riscv_word_t in) +{ + assert(rv); + if (reg < RV_NUM_REGS && reg != rv_reg_zero) + rv->X[reg] = in; +} + +riscv_word_t rv_get_reg(struct riscv_t *rv, uint32_t reg) +{ + assert(rv); + if (reg < RV_NUM_REGS) + return rv->X[reg]; + + return ~0U; +} + +struct riscv_t *rv_create(const struct riscv_io_t *io, riscv_user_t userdata) +{ + assert(io); + + struct riscv_t *rv = calloc(1, sizeof(struct riscv_t)); + + /* copy over the IO interface */ + memcpy(&rv->io, io, sizeof(struct riscv_io_t)); + + /* copy over the userdata */ + rv->userdata = userdata; + + /* initialize the block map */ + block_map_init(&rv->block_map, 10); + + /* reset */ + rv_reset(rv, 0U); + + return rv; +} + +void rv_halt(struct riscv_t *rv) +{ + rv->halt = true; +} + +bool rv_has_halted(struct riscv_t *rv) +{ + return rv->halt; +} + +void rv_delete(struct riscv_t *rv) +{ + assert(rv); + block_map_clear(&rv->block_map); + free(rv); +} + +void rv_reset(struct riscv_t *rv, riscv_word_t pc) +{ + assert(rv); + memset(rv->X, 0, sizeof(uint32_t) * RV_NUM_REGS); + + /* set the reset address */ + rv->PC = pc; + + /* set the default stack pointer */ + rv->X[rv_reg_sp] = DEFAULT_STACK_ADDR; + + /* reset the csrs */ + rv->csr_mtvec = 0; + rv->csr_cycle = 0; + rv->csr_mstatus = 0; + +#if RV32_HAS(EXT_F) + /* reset float registers */ + memset(rv->F, 0, sizeof(float) * RV_NUM_REGS); + rv->csr_fcsr = 0; +#endif + + rv->halt = false; +} + +/* FIXME: provide real implementation */ +void rv_stats(struct riscv_t *rv) +{ + printf("CSR cycle count: %" PRIu64 "\n", rv->csr_cycle); +} \ No newline at end of file diff --git a/src/riscv.h b/src/riscv.h index e8e3e930..bbeca5ae 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -111,7 +111,7 @@ struct riscv_io_t { struct riscv_t *rv_create(const struct riscv_io_t *io, riscv_user_t user_data); /* delete a RISC-V emulator */ -void rv_delete(struct riscv_t *); +void rv_delete(struct riscv_t *rv); /* reset the RISC-V processor */ void rv_reset(struct riscv_t *, riscv_word_t pc); diff --git a/src/riscv_private.h b/src/riscv_private.h index b937c1ea..b2b8a411 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -10,6 +10,7 @@ #include "breakpoint.h" #include "mini-gdbstub/include/gdbstub.h" #endif +#include "decode.h" #include "riscv.h" #define RV_NUM_REGS 32 @@ -96,6 +97,20 @@ struct riscv_t { uint32_t csr_mip; uint32_t csr_mbadaddr; - /* current instruction length */ - uint8_t insn_len; + /* current instruction is compressed or not */ + bool compressed; + /* basic block map */ + struct block_map block_map; }; + +/* sign extend a 16 bit value */ +static inline uint32_t sign_extend_h(const uint32_t x) +{ + return (int32_t) ((int16_t) x); +} + +/* sign extend an 8 bit value */ +static inline uint32_t sign_extend_b(const uint32_t x) +{ + return (int32_t) ((int8_t) x); +}