sysprog21 / rv32emu

Compact and Efficient RISC-V RV32I[MAFC] emulator
MIT License
402 stars 97 forks source link

Add dummy WFI implementation #470

Closed ChinYikMing closed 3 months ago

ChinYikMing commented 4 months ago

The Wait for Interrupt instruction (WFI) provides a hint to the implementation that the current hart can be stalled until an interrupt might need servicing. As well, Linux kernel might call wait_for_interrupt() to enter idle state. In this commit, we could simply make PC + 4 and not really enter idle state.

Related: #310

jserv commented 4 months ago

You should clarify WFI implementation. WFI is crucial for SMP awareness, and I did the following incomplete work for semu. (Linux only)

static uint64_t read_time(vm_t *vm)
{
    emu_state_t *data = (emu_state_t *) vm->priv;
    return data->time;
}

static void wfi(vm_t *vm)
{
    emu_state_t *data = (emu_state_t *) vm->priv;

    struct timespec start;
    clock_gettime(CLOCK_MONOTONIC, &start);

    struct pollfd pfd[] = {
        {data->timer_fd, 0, 0},
    };

    if (data->time < data->timer) {
        int64_t ticks = data->timer - data->time;
        ticks = (ticks * 1e9) / CLOCK_FREQ;
        struct itimerspec spec = {
            {0, 0},
            {ticks / (int64_t) 1e9, ticks % (int64_t) 1e9},
        };
        timerfd_settime(data->timer_fd, 0, &spec, NULL);
        pfd[0].events |= POLLIN;
    }

    while (!(vm->sip & vm->sie)) {
        int ret = poll(pfd, sizeof(pfd) / sizeof(*pfd), -1);
        assert(ret >= 0);
        if (pfd[0].revents & POLLIN) {
            vm->sip |= RV_INT_STI_BIT;
            pfd[0].events &= ~POLLIN;
        }
    }         

    struct timespec end;
    clock_gettime(CLOCK_MONOTONIC, &end);
    int64_t ticks =
        (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec);
    data->time += (ticks * CLOCK_FREQ) / 1e9;
}