Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use instruction-based emulation for debug mode #530

Merged
merged 1 commit into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 76 additions & 37 deletions src/emulate.c
Original file line number Diff line number Diff line change
Expand Up @@ -862,12 +862,9 @@ static block_t *block_find_or_translate(riscv_t *rv)
block_translate(rv, next_blk);

optimize_constant(rv, next_blk);
#if RV32_HAS(GDBSTUB)
if (likely(!rv->debug_mode))
#endif
#if RV32_HAS(MOP_FUSION)
/* macro operation fusion */
match_pattern(rv, next_blk);
/* macro operation fusion */
match_pattern(rv, next_blk);
#endif

#if !RV32_HAS(JIT)
Expand Down Expand Up @@ -958,6 +955,41 @@ static bool rv_has_plic_trap(riscv_t *rv)
return ((rv->csr_sstatus & SSTATUS_SIE || !rv->priv_mode) &&
(rv->csr_sip & rv->csr_sie));
}

static void rv_check_interrupt(riscv_t *rv)
{
vm_attr_t *attr = PRIV(rv);
if (peripheral_update_ctr-- == 0) {
peripheral_update_ctr = 64;

u8250_check_ready(PRIV(rv)->uart);
if (PRIV(rv)->uart->in_ready)
emu_update_uart_interrupts(rv);
}

if (ctr > attr->timer)
rv->csr_sip |= RV_INT_STI;
else
rv->csr_sip &= ~RV_INT_STI;

if (rv_has_plic_trap(rv)) {
uint32_t intr_applicable = rv->csr_sip & rv->csr_sie;
uint8_t intr_idx = ilog2(intr_applicable);
switch (intr_idx) {
case (SUPERVISOR_SW_INTR & 0xf):
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_SW_INTR, 0);
break;
case (SUPERVISOR_TIMER_INTR & 0xf):
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_TIMER_INTR, 0);
break;
case (SUPERVISOR_EXTERNAL_INTR & 0xf):
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_EXTERNAL_INTR, 0);
break;
default:
break;
}
}
}
#endif

void rv_step(void *arg)
Expand All @@ -975,38 +1007,8 @@ void rv_step(void *arg)
while (rv->csr_cycle < cycles_target && !rv->halt) {
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
/* check for any interrupt after every block emulation */

if (peripheral_update_ctr-- == 0) {
peripheral_update_ctr = 64;

u8250_check_ready(PRIV(rv)->uart);
if (PRIV(rv)->uart->in_ready)
emu_update_uart_interrupts(rv);
}

if (ctr > attr->timer)
rv->csr_sip |= RV_INT_STI;
else
rv->csr_sip &= ~RV_INT_STI;

if (rv_has_plic_trap(rv)) {
uint32_t intr_applicable = rv->csr_sip & rv->csr_sie;
uint8_t intr_idx = ilog2(intr_applicable);
switch (intr_idx) {
case (SUPERVISOR_SW_INTR & 0xf):
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_SW_INTR, 0);
break;
case (SUPERVISOR_TIMER_INTR & 0xf):
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_TIMER_INTR, 0);
break;
case (SUPERVISOR_EXTERNAL_INTR & 0xf):
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_EXTERNAL_INTR, 0);
break;
default:
break;
}
}
#endif /* RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) */
rv_check_interrupt(rv);
#endif

if (prev && prev->pc_start != last_pc) {
/* update previous block */
Expand Down Expand Up @@ -1111,6 +1113,43 @@ void rv_step(void *arg)
#endif
}

void rv_step_debug(void *arg)
{
assert(arg);
riscv_t *rv = arg;

#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
rv_check_interrupt(rv);
Copy link
Collaborator

@ChinYikMing ChinYikMing Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about modify here like following?

#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
    rv_check_interrupt(rv);
#endif

In this way, the dummy rv_check_interrupt can be removed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The debug mode of system simulation is not supported yet. Should we remove this statement to avoid ambiguity?

Copy link
Collaborator Author

@RinHizakura RinHizakura Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "debug mode of system simulation is not supported" mean here? The debug/gdbstub mode should be supported naturally because there's no big difference between program or system emulation. I already checked that we could read/write the register, set the breakpoint and step instructions on system emulation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would rv32emu support debugging with booting Linux Kernel now? I failed to run file build/linux-image/Image in riscv32-unknown-elf-gdb to launch the remote GDB since it is not the RISC-V executable.

Copy link
Collaborator Author

@RinHizakura RinHizakura Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Please use the vmlinux file, Image is not the right one. By the way, you can skip the step to link GDB to an ELF file if you don't need any debug symbol(i.e. just run target remote :1234 and start debugging).

Copy link
Collaborator Author

@RinHizakura RinHizakura Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check like this

  1. Open a bash shell and run ./build/rv32emu -k build/linux-image/Image -i build/linux-image/rootfs.cpio -b build/minimal.dtb -g
  2. Open another shell and run riscv32-unknown-elf-gdb -ex "target remote :1234" -ex "cont"
  3. You can see the emulator start executing in the first shell

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, it works. Thank you to point it out. Can you also update the document for this? (maybe in another PR)

#endif

retranslate:
/* fetch the next instruction */
rv_insn_t ir;
memset(&ir, 0, sizeof(rv_insn_t));

uint32_t insn = rv->io.mem_ifetch(rv, rv->PC);
#if RV32_HAS(SYSTEM)
if (!insn && need_retranslate) {
need_retranslate = false;
goto retranslate;
}
#endif
assert(insn);

/* decode the instruction */
if (!rv_decode(&ir, insn)) {
rv->compressed = is_compressed(insn);
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, ILLEGAL_INSN, insn);
return;
}

ir.impl = dispatch_table[ir.opcode];
ir.pc = rv->PC;
ir.next = NULL;
ir.impl(rv, &ir, rv->csr_cycle, rv->PC);
return;
}

#if RV32_HAS(SYSTEM)
static void __trap_handler(riscv_t *rv)
{
Expand Down
8 changes: 2 additions & 6 deletions src/gdbstub.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,12 @@ static gdb_action_t rv_cont(void *args)
{
riscv_t *rv = (riscv_t *) args;
assert(rv);
vm_attr_t *attr = PRIV(rv);
attr->cycle_per_step = 1;

for (; !rv_has_halted(rv) && !rv_is_interrupt(rv);) {
if (breakpoint_map_find(rv->breakpoint_map, rv_get_pc(rv)))
break;

rv_step(rv);
rv_step_debug(rv);
}

/* Clear the interrupt if it's pending */
Expand All @@ -100,10 +98,8 @@ static gdb_action_t rv_stepi(void *args)
{
riscv_t *rv = (riscv_t *) args;
assert(rv);
vm_attr_t *attr = PRIV(rv);
attr->cycle_per_step = 1;

rv_step(rv);
rv_step_debug(rv);
return ACT_RESUME;
}

Expand Down
3 changes: 3 additions & 0 deletions src/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,9 @@ void rv_debug(riscv_t *rv);
/* step the RISC-V emulator */
void rv_step(void *arg);

/* step the RISC-V emulator for debug mode */
void rv_step_debug(void *arg);

/* set the program counter of a RISC-V emulator */
bool rv_set_pc(riscv_t *rv, riscv_word_t pc);

Expand Down
84 changes: 46 additions & 38 deletions src/rv32_template.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,53 +224,61 @@ RVOP(
* In addition, before relocate_enable_mmu, the block maybe retranslated, \
* thus the branch history lookup table should not be updated too. \
*/ \
IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped && !reloc_enable_mmu), )) \
IIF(RV32_HAS(GDBSTUB)(if (!rv->debug_mode), )) \
{ \
for (int i = 0; i < HISTORY_SIZE; i++) { \
if (ir->branch_table->PC[i] == PC) { \
MUST_TAIL return ir->branch_table->target[i]->impl( \
rv, ir->branch_table->target[i], cycle, PC); \
IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped && !reloc_enable_mmu), )) \
{ \
for (int i = 0; i < HISTORY_SIZE; i++) { \
if (ir->branch_table->PC[i] == PC) { \
MUST_TAIL return ir->branch_table->target[i]->impl( \
rv, ir->branch_table->target[i], cycle, PC); \
} \
} \
block_t *block = block_find(&rv->block_map, PC); \
if (block) { \
/* update branch history table */ \
ir->branch_table->PC[ir->branch_table->idx] = PC; \
ir->branch_table->target[ir->branch_table->idx] = \
block->ir_head; \
ir->branch_table->idx = \
(ir->branch_table->idx + 1) % HISTORY_SIZE; \
MUST_TAIL return block->ir_head->impl(rv, block->ir_head, \
cycle, PC); \
} \
} \
block_t *block = block_find(&rv->block_map, PC); \
}
#else
#define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \
IIF(RV32_HAS(GDBSTUB)(if (!rv->debug_mode), )) \
{ \
block_t *block = cache_get(rv->block_cache, PC, true); \
if (block) { \
for (int i = 0; i < HISTORY_SIZE; i++) { \
if (ir->branch_table->PC[i] == PC) { \
ir->branch_table->times[i]++; \
if (cache_hot(rv->block_cache, PC)) \
goto end_op; \
} \
} \
/* update branch history table */ \
ir->branch_table->PC[ir->branch_table->idx] = PC; \
ir->branch_table->target[ir->branch_table->idx] = block->ir_head; \
ir->branch_table->idx = \
(ir->branch_table->idx + 1) % HISTORY_SIZE; \
int min_idx = 0; \
for (int i = 0; i < HISTORY_SIZE; i++) { \
if (!ir->branch_table->times[i]) { \
min_idx = i; \
break; \
} else if (ir->branch_table->times[min_idx] > \
ir->branch_table->times[i]) { \
min_idx = i; \
} \
} \
ir->branch_table->times[min_idx] = ir->branch_table->PC[min_idx] = \
PC; \
if (cache_hot(rv->block_cache, PC)) \
goto end_op; \
MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, \
PC); \
} \
}
#else
#define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \
block_t *block = cache_get(rv->block_cache, PC, true); \
if (block) { \
for (int i = 0; i < HISTORY_SIZE; i++) { \
if (ir->branch_table->PC[i] == PC) { \
ir->branch_table->times[i]++; \
if (cache_hot(rv->block_cache, PC)) \
goto end_op; \
} \
} \
/* update branch history table */ \
int min_idx = 0; \
for (int i = 0; i < HISTORY_SIZE; i++) { \
if (!ir->branch_table->times[i]) { \
min_idx = i; \
break; \
} else if (ir->branch_table->times[min_idx] > \
ir->branch_table->times[i]) { \
min_idx = i; \
} \
} \
ir->branch_table->times[min_idx] = 1; \
ir->branch_table->PC[min_idx] = PC; \
if (cache_hot(rv->block_cache, PC)) \
goto end_op; \
MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, PC); \
}
#endif

/* The indirect jump instruction JALR uses the I-type encoding. The target
Expand Down
Loading