Skip to content

Commit b7f09eb

Browse files
committed
Introduce basic block
This commit introduces the basic block in emulator, meaning that it makes emulator decode and execute numerous instructions at a time. Use queue-based block management to manage the instruction decode and execution. In decode stage, allocate a new memory block, put the decoded instruction into the block, and record the order of instructions by the member front in struct block_t until the queue is full or the latest instruction is a branch instruction. The default size of memory block in queue is 1024. In execution stage, when the queue is not empty, emulator executes the memory block that index is rear in struct block_t, frees the memory block and increments the clock cycle finally. In particular, when an exception/interrupt occurs, emulator will do the following steps: 1. Execute the exception/interrupt handler that resets a new program counter from the register mtvec and function emulate return false. 2. Enter to the decode stage again, and create new memory blocks based on the new program counter. That is, emulator will dischard old memory blocks and create the new one from new program counter.
1 parent 2aa7154 commit b7f09eb

File tree

4 files changed

+162
-33
lines changed

4 files changed

+162
-33
lines changed

src/decode.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,9 +1663,9 @@ static inline bool op_unimp(struct rv_insn_t *rv_insn UNUSED,
16631663
typedef bool (*decode_t)(struct rv_insn_t *rv_insn, uint32_t insn);
16641664

16651665
/* decode RISC-V instruction */
1666-
bool rv_decode(struct rv_insn_t *ir, uint32_t insn, uint8_t *insn_len)
1666+
bool rv_decode(struct rv_insn_t *ir, uint32_t insn)
16671667
{
1668-
assert(ir && insn_len);
1668+
assert(ir);
16691669

16701670
#define OP_UNIMP op_unimp
16711671
#define OP(insn) op_##insn
@@ -1704,7 +1704,7 @@ bool rv_decode(struct rv_insn_t *ir, uint32_t insn, uint8_t *insn_len)
17041704
if ((insn & FC_OPCODE) != 3) {
17051705
insn &= 0x0000FFFF;
17061706
const uint16_t c_index = (insn & FC_FUNC3) >> 11 | (insn & FC_OPCODE);
1707-
*insn_len = INSN_16;
1707+
ir->insn_len = INSN_16;
17081708

17091709
/* decode instruction (compressed instructions) */
17101710
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)
17151715

17161716
/* standard uncompressed instruction */
17171717
const uint32_t index = (insn & INSN_6_2) >> 2;
1718-
*insn_len = INSN_32;
1718+
ir->insn_len = INSN_32;
17191719

17201720
/* decode instruction */
17211721
const decode_t op = rv_jump_table[index];

src/decode.h

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,19 @@ struct rv_insn_t {
236236
#if RV32_HAS(EXT_C)
237237
uint8_t shamt;
238238
#endif
239+
240+
/* instruction length */
241+
uint8_t insn_len;
242+
};
243+
244+
/* a translated basic block */
245+
struct block_t {
246+
/* memory blocks */
247+
struct rv_insn_t *ir;
248+
/* index of memory blocks */
249+
uint32_t front, rear;
250+
/* maximum of instructions encompased */
251+
uint32_t capacity;
239252
};
240253

241254
/* sign extend a 16 bit value */
@@ -250,7 +263,35 @@ static inline uint32_t sign_extend_b(const uint32_t x)
250263
return (int32_t) ((int8_t) x);
251264
}
252265

266+
static inline bool queue_is_full(struct block_t *block)
267+
{
268+
assert(block);
269+
return (block->front - block->rear) == block->capacity;
270+
}
271+
272+
static inline bool queue_is_empty(struct block_t *block)
273+
{
274+
assert(block);
275+
return block->front == block->rear;
276+
}
277+
278+
static inline struct rv_insn_t *get_rear_mem_block(struct block_t *block)
279+
{
280+
assert(block);
281+
return block->ir + block->rear;
282+
}
283+
284+
static inline struct rv_insn_t *alloc_mem_block(struct block_t *block)
285+
{
286+
assert(block);
287+
return block->ir + block->front++;
288+
}
289+
290+
static inline void free_mem_block(struct block_t *block)
291+
{
292+
assert(block);
293+
block->rear++;
294+
}
295+
253296
/* decode the RISC-V instruction */
254-
bool rv_decode(struct rv_insn_t *rv_insn,
255-
const uint32_t insn,
256-
uint8_t *insn_len);
297+
bool rv_decode(struct rv_insn_t *rv_insn, const uint32_t insn);

src/emulate.c

Lines changed: 111 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ enum {
5252

5353
static void rv_exception_default_handler(struct riscv_t *rv)
5454
{
55-
rv->csr_mepc += rv->insn_len;
55+
struct rv_insn_t *ir = get_rear_mem_block(&rv->block);
56+
rv->csr_mepc += ir->insn_len;
5657
rv->PC = rv->csr_mepc; /* mret */
5758
}
5859

@@ -256,7 +257,7 @@ static bool insn_is_misaligned(uint32_t pc)
256257
);
257258
}
258259

259-
static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir)
260+
static bool emulate(struct riscv_t *rv, struct rv_insn_t *ir)
260261
{
261262
switch (ir->opcode) {
262263
/* RV32I Base Instruction Set */
@@ -286,7 +287,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir)
286287
rv->PC += ir->imm;
287288
/* link with return address */
288289
if (ir->rd)
289-
rv->X[ir->rd] = pc + rv->insn_len;
290+
rv->X[ir->rd] = pc + ir->insn_len;
290291
/* check instruction misaligned */
291292
if (insn_is_misaligned(rv->PC)) {
292293
rv_except_insn_misaligned(rv, pc);
@@ -308,7 +309,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir)
308309
rv->PC = (rv->X[ir->rs1] + ir->imm) & ~1U;
309310
/* link */
310311
if (ir->rd)
311-
rv->X[ir->rd] = pc + rv->insn_len;
312+
rv->X[ir->rd] = pc + ir->insn_len;
312313
/* check instruction misaligned */
313314
if (insn_is_misaligned(rv->PC)) {
314315
rv_except_insn_misaligned(rv, pc);
@@ -963,7 +964,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir)
963964
rv->X[ir->rd] += (int16_t) ir->imm;
964965
break;
965966
case rv_insn_cjal:
966-
rv->X[1] = rv->PC + rv->insn_len;
967+
rv->X[1] = rv->PC + ir->insn_len;
967968
rv->PC += ir->imm;
968969
if (rv->PC & 0x1) {
969970
rv_except_insn_misaligned(rv, rv->PC);
@@ -1055,11 +1056,11 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir)
10551056
* the value in register rs1' is zero. It expands to beq rs1', x0,
10561057
* offset[8:1].
10571058
*/
1058-
rv->PC += (!rv->X[ir->rs1]) ? (uint32_t) ir->imm : rv->insn_len;
1059+
rv->PC += (!rv->X[ir->rs1]) ? (uint32_t) ir->imm : ir->insn_len;
10591060
/* can branch */
10601061
return true;
10611062
case rv_insn_cbnez: /* C.BEQZ */
1062-
rv->PC += (rv->X[ir->rs1]) ? (uint32_t) ir->imm : rv->insn_len;
1063+
rv->PC += (rv->X[ir->rs1]) ? (uint32_t) ir->imm : ir->insn_len;
10631064
/* can branch */
10641065
return true;
10651066
case rv_insn_cslli: /* C.SLLI */
@@ -1093,7 +1094,7 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir)
10931094
case rv_insn_cjalr: { /* C.JALR */
10941095
/* Unconditional jump and store PC+2 to ra */
10951096
const int32_t jump_to = rv->X[ir->rs1];
1096-
rv->X[rv_reg_ra] = rv->PC + rv->insn_len;
1097+
rv->X[rv_reg_ra] = rv->PC + ir->insn_len;
10971098
rv->PC = jump_to;
10981099
if (rv->PC & 0x1) {
10991100
rv_except_insn_misaligned(rv, rv->PC);
@@ -1125,37 +1126,100 @@ static bool rv_emulate(struct riscv_t *rv, struct rv_insn_t *ir)
11251126
}
11261127

11271128
/* step over instruction */
1128-
rv->PC += rv->insn_len;
1129+
rv->PC += ir->insn_len;
11291130
return true;
11301131
}
11311132

1132-
void rv_step(struct riscv_t *rv, int32_t cycles)
1133+
static bool emulate_block(struct riscv_t *rv, struct block_t *block)
11331134
{
1134-
assert(rv);
1135-
uint32_t insn;
1136-
struct rv_insn_t ir;
1137-
const uint64_t cycles_target = rv->csr_cycle + cycles;
1138-
1139-
while (rv->csr_cycle < cycles_target && !rv->halt) {
1135+
/* execute the block */
1136+
while (!queue_is_empty(block)) {
11401137
/* enforce zero register */
11411138
rv->X[rv_reg_zero] = 0;
11421139

1140+
/* execute the instruction */
1141+
if (!emulate(rv, get_rear_mem_block(&rv->block)))
1142+
return false;
1143+
1144+
free_mem_block(block);
1145+
1146+
/* increment the cycles csr */
1147+
rv->csr_cycle++;
1148+
}
1149+
return true;
1150+
}
1151+
1152+
static bool insn_is_branch(uint8_t opcode)
1153+
{
1154+
switch (opcode) {
1155+
case rv_insn_jal:
1156+
case rv_insn_jalr:
1157+
case rv_insn_beq:
1158+
case rv_insn_bne:
1159+
case rv_insn_blt:
1160+
case rv_insn_bge:
1161+
case rv_insn_bltu:
1162+
case rv_insn_bgeu:
1163+
case rv_insn_ecall:
1164+
case rv_insn_ebreak:
1165+
case rv_insn_mret:
1166+
#if RV32_HAS(EXT_C)
1167+
case rv_insn_cj:
1168+
case rv_insn_cjr:
1169+
case rv_insn_cjal:
1170+
case rv_insn_cjalr:
1171+
case rv_insn_cbeqz:
1172+
case rv_insn_cbnez:
1173+
case rv_insn_cebreak:
1174+
#endif
1175+
#if RV32_HAS(Zifencei)
1176+
case rv_insn_fencei:
1177+
#endif
1178+
return true;
1179+
}
1180+
return false;
1181+
}
1182+
1183+
static void translate_block(struct riscv_t *rv, struct block_t *block)
1184+
{
1185+
/* translate the basic block */
1186+
uint32_t pc = rv->PC;
1187+
block->front = 0;
1188+
block->rear = 0;
1189+
while (!queue_is_full(block)) {
1190+
struct rv_insn_t *ir = alloc_mem_block(block);
1191+
memset(ir, 0, sizeof(struct rv_insn_t));
1192+
11431193
/* fetch the next instruction */
1144-
insn = rv->io.mem_ifetch(rv, rv->PC);
1145-
memset(&ir, 0, sizeof(struct rv_insn_t));
1194+
const uint32_t insn = rv->io.mem_ifetch(rv, pc);
11461195

11471196
/* decode the instruction */
1148-
if (!rv_decode(&ir, insn, &rv->insn_len)) {
1197+
if (!rv_decode(ir, insn)) {
11491198
rv_except_illegal_insn(rv, insn);
11501199
break;
11511200
}
11521201

1153-
/* execute the instruciton */
1154-
if (!rv_emulate(rv, &ir))
1202+
/* stop on branch */
1203+
if (insn_is_branch(ir->opcode))
11551204
break;
11561205

1157-
/* increment the cycles csr */
1158-
rv->csr_cycle++;
1206+
/* compute next pc */
1207+
pc += ir->insn_len;
1208+
}
1209+
}
1210+
1211+
void rv_step(struct riscv_t *rv, int32_t cycles)
1212+
{
1213+
assert(rv);
1214+
const uint64_t cycles_target = rv->csr_cycle + cycles;
1215+
1216+
while (rv->csr_cycle < cycles_target && !rv->halt) {
1217+
/* translate basic block */
1218+
translate_block(rv, &rv->block);
1219+
1220+
/* execute the block */
1221+
if (!emulate_block(rv, &rv->block))
1222+
break;
11591223
}
11601224
}
11611225

@@ -1201,6 +1265,19 @@ riscv_word_t rv_get_reg(struct riscv_t *rv, uint32_t reg)
12011265
return ~0U;
12021266
}
12031267

1268+
/* allocate a new code block */
1269+
static bool block_init(struct block_t *block, uint32_t bit)
1270+
{
1271+
assert(block);
1272+
1273+
block->capacity = 1 << bit;
1274+
block->ir = malloc(sizeof(struct rv_insn_t) * block->capacity);
1275+
if (!block->ir)
1276+
return false;
1277+
1278+
return true;
1279+
}
1280+
12041281
struct riscv_t *rv_create(const struct riscv_io_t *io, riscv_user_t userdata)
12051282
{
12061283
assert(io);
@@ -1213,6 +1290,10 @@ struct riscv_t *rv_create(const struct riscv_io_t *io, riscv_user_t userdata)
12131290
/* copy over the userdata */
12141291
rv->userdata = userdata;
12151292

1293+
/* place a new block */
1294+
if (!block_init(&rv->block, 10))
1295+
return NULL;
1296+
12161297
/* reset */
12171298
rv_reset(rv, 0U);
12181299

@@ -1229,9 +1310,16 @@ bool rv_has_halted(struct riscv_t *rv)
12291310
return rv->halt;
12301311
}
12311312

1313+
static void block_free(struct block_t *block)
1314+
{
1315+
assert(block);
1316+
free(block->ir);
1317+
}
1318+
12321319
void rv_delete(struct riscv_t *rv)
12331320
{
12341321
assert(rv);
1322+
block_free(&rv->block);
12351323
free(rv);
12361324
}
12371325

@@ -1242,7 +1330,6 @@ void rv_reset(struct riscv_t *rv, riscv_word_t pc)
12421330

12431331
/* set the reset address */
12441332
rv->PC = pc;
1245-
rv->insn_len = INSN_UNKNOWN;
12461333

12471334
/* set the default stack pointer */
12481335
rv->X[rv_reg_sp] = DEFAULT_STACK_ADDR;

src/riscv_private.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "breakpoint.h"
1111
#include "mini-gdbstub/include/gdbstub.h"
1212
#endif
13+
#include "decode.h"
1314
#include "riscv.h"
1415

1516
#define RV_NUM_REGS 32
@@ -96,6 +97,6 @@ struct riscv_t {
9697
uint32_t csr_mip;
9798
uint32_t csr_mbadaddr;
9899

99-
/* current instruction length */
100-
uint8_t insn_len;
100+
/* basic block */
101+
struct block_t block;
101102
};

0 commit comments

Comments
 (0)