Skip to content

Commit fcce814

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 fcce814

File tree

4 files changed

+166
-33
lines changed

4 files changed

+166
-33
lines changed

src/decode.c

+4-4
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

+67-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <stdbool.h>
99
#include <stdint.h>
10+
#include <stdlib.h>
1011

1112
enum {
1213
/* RV32I Base Instruction Set */
@@ -236,8 +237,21 @@ struct rv_insn_t {
236237
#if RV32_HAS(EXT_C)
237238
uint8_t shamt;
238239
#endif
240+
241+
/* instruction length */
242+
uint8_t insn_len;
239243
};
240244

245+
/* a translated basic block */
246+
typedef struct {
247+
/* memory blocks */
248+
struct rv_insn_t *ir;
249+
/* index of memory blocks */
250+
uint32_t front, rear;
251+
/* maximum of instructions encompased */
252+
uint32_t capacity;
253+
} block_t;
254+
241255
/* sign extend a 16 bit value */
242256
static inline uint32_t sign_extend_h(const uint32_t x)
243257
{
@@ -250,7 +264,57 @@ static inline uint32_t sign_extend_b(const uint32_t x)
250264
return (int32_t) ((int8_t) x);
251265
}
252266

267+
/* allocate a new basic block */
268+
static inline bool block_init(block_t *block, uint32_t bit)
269+
{
270+
assert(block);
271+
272+
block->capacity = 1 << bit;
273+
block->ir = malloc(sizeof(struct rv_insn_t) * block->capacity);
274+
return (bool) (uintptr_t) block->ir;
275+
}
276+
277+
/* free the basic block */
278+
static inline void block_free(block_t *block)
279+
{
280+
assert(block);
281+
free(block->ir);
282+
}
283+
284+
/* check the queue is full or not */
285+
static inline bool block_is_full(block_t *block)
286+
{
287+
assert(block);
288+
return (block->front - block->rear) == block->capacity;
289+
}
290+
291+
/* check the queue is empty or not */
292+
static inline bool block_is_empty(block_t *block)
293+
{
294+
assert(block);
295+
return block->front == block->rear;
296+
}
297+
298+
/* get the current memory block in queue */
299+
static inline struct rv_insn_t *queue_current(block_t *block)
300+
{
301+
assert(block);
302+
return block->ir + block->rear;
303+
}
304+
305+
/* enqueue a new memory block */
306+
static inline struct rv_insn_t *queue_enqueue(block_t *block)
307+
{
308+
assert(block);
309+
return block->ir + block->front++;
310+
}
311+
312+
/* dequeue a memory block */
313+
static inline void queue_dequeue(block_t *block)
314+
{
315+
assert(block);
316+
block->rear++;
317+
}
318+
253319
/* 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);
320+
bool rv_decode(struct rv_insn_t *rv_insn, const uint32_t insn);

src/emulate.c

+92-24
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 = queue_current(&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, 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 (!block_is_empty(block)) {
11401137
/* enforce zero register */
11411138
rv->X[rv_reg_zero] = 0;
11421139

1140+
/* execute the instruction */
1141+
if (!emulate(rv, queue_current(&rv->block)))
1142+
return false;
1143+
1144+
queue_dequeue(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, 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 (!block_is_full(block)) {
1190+
struct rv_insn_t *ir = queue_enqueue(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

@@ -1213,6 +1277,10 @@ struct riscv_t *rv_create(const struct riscv_io_t *io, riscv_user_t userdata)
12131277
/* copy over the userdata */
12141278
rv->userdata = userdata;
12151279

1280+
/* place a new queue */
1281+
if (!block_init(&rv->block, 10))
1282+
return NULL;
1283+
12161284
/* reset */
12171285
rv_reset(rv, 0U);
12181286

@@ -1232,6 +1300,7 @@ bool rv_has_halted(struct riscv_t *rv)
12321300
void rv_delete(struct riscv_t *rv)
12331301
{
12341302
assert(rv);
1303+
block_free(&rv->block);
12351304
free(rv);
12361305
}
12371306

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

12431312
/* set the reset address */
12441313
rv->PC = pc;
1245-
rv->insn_len = INSN_UNKNOWN;
12461314

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

src/riscv_private.h

+3-2
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+
block_t block;
101102
};

0 commit comments

Comments
 (0)