Skip to content

Commit

Permalink
Preliminary implement CLINT to multi-hart system
Browse files Browse the repository at this point in the history
Replace emu.timer with the CLINT device. This CLINT device
preliminarily supports sending 4095 individual timer and
software interrupts to different harts.
  • Loading branch information
ranvd committed Jun 24, 2024
1 parent 7a91485 commit cfdb547
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 8 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ OBJS := \
plic.o \
uart.o \
main.o \
clint.o \
$(OBJS_EXTRA)

deps := $(OBJS:%.o=.%.o.d)
Expand Down
87 changes: 87 additions & 0 deletions clint.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <stdint.h>
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"

void clint_update_interrupts(vm_t *hart, clint_state_t *clint)
{
if (clint->mtime > clint->mtimecmp[hart->mhartid])
hart->sip |= RV_INT_STI_BIT;
else
hart->sip &= ~RV_INT_STI_BIT;

if (clint->msip[hart->mhartid]) {
hart->sip |= RV_INT_SSI_BIT;
} else
hart->sip &= ~RV_INT_SSI_BIT;
}

static bool clint_reg_read(clint_state_t *clint, uint32_t addr, uint32_t *value)
{
if (addr < 0x4000) {
*value = clint->msip[addr >> 2];
return true;
} else if (addr < 0xBFF8) {
addr -= 0x4000;
*value =
(uint32_t) (clint->mtimecmp[addr >> 3] >> (32 & -!!(addr & 0b100)));
return true;
} else if (addr < 0xBFFF) {
*value = clint->mtime >> (32 & -!!(addr & 0b100));
return true;
}
return false;
}

static bool clint_reg_write(clint_state_t *clint, uint32_t addr, uint32_t value)
{
if (addr < 0x4000) {
clint->msip[addr >> 2] = value;
return true;
} else if (addr < 0xBFF8) {
addr -= 0x4000;
int32_t upper = clint->mtimecmp[addr >> 3] >> 32;
int32_t lowwer = clint->mtimecmp[addr >> 3];
if (addr & 0b100)
upper = value;
else
lowwer = value;

clint->mtimecmp[addr >> 3] = (uint64_t) upper << 32 | lowwer;
return true;
} else if (addr < 0xBFFF) {
int32_t upper = clint->mtime >> 32;
int32_t lowwer = clint->mtime;
if (addr & 0b100)
upper = value;
else
lowwer = value;

clint->mtime = (uint64_t) upper << 32 | lowwer;
return true;
}
return false;
}

void clint_read(vm_t *hart,
clint_state_t *clint,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
if (!clint_reg_read(clint, addr, value))
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
*value = (*value) >> (RV_MEM_SW - width);
return;
}

void clint_write(vm_t *hart,
clint_state_t *clint,
uint32_t addr,
uint8_t width,
uint32_t value)
{
if (!clint_reg_write(clint, addr, value >> (RV_MEM_SW - width)))
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
return;
}
22 changes: 21 additions & 1 deletion device.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,26 @@ void virtio_blk_write(vm_t *vm,
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
#endif /* SEMU_HAS(VIRTIOBLK) */

/* clint */
typedef struct {
uint32_t msip[4096];
uint64_t mtimecmp[4095];
uint64_t mtime;
} clint_state_t;

void clint_update_interrupts(vm_t *vm, clint_state_t *clint);
void clint_read(vm_t *vm,
clint_state_t *clint,
uint32_t addr,
uint8_t width,
uint32_t *value);

void clint_write(vm_t *vm,
clint_state_t *clint,
uint32_t addr,
uint8_t width,
uint32_t value);

/* memory mapping */

typedef struct {
Expand All @@ -185,5 +205,5 @@ typedef struct {
#if SEMU_HAS(VIRTIOBLK)
virtio_blk_state_t vblk;
#endif
uint64_t timer;
clint_state_t clint;
} emu_state_t;
35 changes: 28 additions & 7 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ static void emu_update_vblk_interrupts(vm_t *vm)
}
#endif

static void emu_update_timer_interrupt(vm_t *vm)
{
emu_state_t *data = PRIV(vm);
vm->time = data->clint.mtime;
clint_update_interrupts(vm, &data->clint);
}

static void emu_update_global_timer(vm_t *vm)
{
emu_state_t *data = PRIV(vm);
data->clint.mtime++;
return;
}


static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
{
emu_state_t *data = PRIV(vm);
Expand Down Expand Up @@ -105,6 +120,9 @@ static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
emu_update_vblk_interrupts(vm);
return;
#endif
case 0x43: /* clint */
clint_read(vm, &data->clint, addr & 0xFFFFF, width, value);
clint_update_interrupts(vm, &data->clint);
}
}
vm_set_exception(vm, RV_EXC_LOAD_FAULT, vm->exc_val);
Expand Down Expand Up @@ -143,6 +161,10 @@ static void mem_store(vm_t *vm, uint32_t addr, uint8_t width, uint32_t value)
emu_update_vblk_interrupts(vm);
return;
#endif
case 0x43: /* clint */
clint_write(vm, &data->clint, addr & 0xFFFFF, width, value);
clint_update_interrupts(vm, &data->clint);
return;
}
}
vm_set_exception(vm, RV_EXC_STORE_FAULT, vm->exc_val);
Expand All @@ -162,8 +184,9 @@ static inline sbi_ret_t handle_sbi_ecall_TIMER(vm_t *vm, int32_t fid)
emu_state_t *data = PRIV(vm);
switch (fid) {
case SBI_TIMER__SET_TIMER:
data->timer = (((uint64_t) vm->x_regs[RV_R_A1]) << 32) |
(uint64_t) (vm->x_regs[RV_R_A0]);
data->clint.mtimecmp[0] = (((uint64_t) vm->x_regs[RV_R_A1]) << 32) |
(uint64_t) (vm->x_regs[RV_R_A0]);
vm->sip &= ~RV_INT_STI_BIT;
return (sbi_ret_t){SBI_SUCCESS, 0};
default:
return (sbi_ret_t){SBI_ERR_NOT_SUPPORTED, 0};
Expand Down Expand Up @@ -369,6 +392,7 @@ static int semu_start(int argc, char **argv)
.mem_store = mem_store,
.mem_page_table = mem_page_table,
};

vm_init(&vm);

/* Set up RAM */
Expand Down Expand Up @@ -406,7 +430,6 @@ static int semu_start(int argc, char **argv)
atexit(unmap_files);

/* Set up RISC-V hart */
emu.timer = 0xFFFFFFFFFFFFFFFF;
vm.s_mode = true;
vm.x_regs[RV_R_A0] = 0; /* hart ID. i.e., cpuid */
vm.x_regs[RV_R_A1] = dtb_addr;
Expand All @@ -428,6 +451,7 @@ static int semu_start(int argc, char **argv)
/* Emulate */
uint32_t peripheral_update_ctr = 0;
while (!emu.stopped) {
emu_update_global_timer(&vm);
if (peripheral_update_ctr-- == 0) {
peripheral_update_ctr = 64;

Expand All @@ -447,10 +471,7 @@ static int semu_start(int argc, char **argv)
#endif
}

if (vm.insn_count > emu.timer)
vm.sip |= RV_INT_STI_BIT;
else
vm.sip &= ~RV_INT_STI_BIT;
emu_update_timer_interrupt(&vm);

vm_step(&vm);
if (likely(!vm.error))
Expand Down
7 changes: 7 additions & 0 deletions minimal.dts
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,12 @@
interrupts = <3>;
};
#endif
clint0: clint@4300000 {
compatible = "riscv,clint0";
interrupt-controller;
interrupts-extended =
<&cpu0_intc 3 &cpu0_intc 7>;
reg = <0x4300000 0x10000>;
};
};
};
1 change: 1 addition & 0 deletions riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct __vm_internal {
* resets.
*/
uint64_t insn_count;
uint64_t time;

/* Instruction execution state must be set to "NONE" for instruction
* execution to continue. If the state is not "NONE," the vm_step()
Expand Down

0 comments on commit cfdb547

Please sign in to comment.