Skip to content

Commit cfdb547

Browse files
committed
Preliminary implement CLINT to multi-hart system
Replace emu.timer with the CLINT device. This CLINT device preliminarily supports sending 4095 individual timer and software interrupts to different harts.
1 parent 7a91485 commit cfdb547

File tree

6 files changed

+145
-8
lines changed

6 files changed

+145
-8
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ OBJS := \
4545
plic.o \
4646
uart.o \
4747
main.o \
48+
clint.o \
4849
$(OBJS_EXTRA)
4950

5051
deps := $(OBJS:%.o=.%.o.d)

clint.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include <stdint.h>
2+
#include "device.h"
3+
#include "riscv.h"
4+
#include "riscv_private.h"
5+
6+
void clint_update_interrupts(vm_t *hart, clint_state_t *clint)
7+
{
8+
if (clint->mtime > clint->mtimecmp[hart->mhartid])
9+
hart->sip |= RV_INT_STI_BIT;
10+
else
11+
hart->sip &= ~RV_INT_STI_BIT;
12+
13+
if (clint->msip[hart->mhartid]) {
14+
hart->sip |= RV_INT_SSI_BIT;
15+
} else
16+
hart->sip &= ~RV_INT_SSI_BIT;
17+
}
18+
19+
static bool clint_reg_read(clint_state_t *clint, uint32_t addr, uint32_t *value)
20+
{
21+
if (addr < 0x4000) {
22+
*value = clint->msip[addr >> 2];
23+
return true;
24+
} else if (addr < 0xBFF8) {
25+
addr -= 0x4000;
26+
*value =
27+
(uint32_t) (clint->mtimecmp[addr >> 3] >> (32 & -!!(addr & 0b100)));
28+
return true;
29+
} else if (addr < 0xBFFF) {
30+
*value = clint->mtime >> (32 & -!!(addr & 0b100));
31+
return true;
32+
}
33+
return false;
34+
}
35+
36+
static bool clint_reg_write(clint_state_t *clint, uint32_t addr, uint32_t value)
37+
{
38+
if (addr < 0x4000) {
39+
clint->msip[addr >> 2] = value;
40+
return true;
41+
} else if (addr < 0xBFF8) {
42+
addr -= 0x4000;
43+
int32_t upper = clint->mtimecmp[addr >> 3] >> 32;
44+
int32_t lowwer = clint->mtimecmp[addr >> 3];
45+
if (addr & 0b100)
46+
upper = value;
47+
else
48+
lowwer = value;
49+
50+
clint->mtimecmp[addr >> 3] = (uint64_t) upper << 32 | lowwer;
51+
return true;
52+
} else if (addr < 0xBFFF) {
53+
int32_t upper = clint->mtime >> 32;
54+
int32_t lowwer = clint->mtime;
55+
if (addr & 0b100)
56+
upper = value;
57+
else
58+
lowwer = value;
59+
60+
clint->mtime = (uint64_t) upper << 32 | lowwer;
61+
return true;
62+
}
63+
return false;
64+
}
65+
66+
void clint_read(vm_t *hart,
67+
clint_state_t *clint,
68+
uint32_t addr,
69+
uint8_t width,
70+
uint32_t *value)
71+
{
72+
if (!clint_reg_read(clint, addr, value))
73+
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
74+
*value = (*value) >> (RV_MEM_SW - width);
75+
return;
76+
}
77+
78+
void clint_write(vm_t *hart,
79+
clint_state_t *clint,
80+
uint32_t addr,
81+
uint8_t width,
82+
uint32_t value)
83+
{
84+
if (!clint_reg_write(clint, addr, value >> (RV_MEM_SW - width)))
85+
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
86+
return;
87+
}

device.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,26 @@ void virtio_blk_write(vm_t *vm,
171171
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
172172
#endif /* SEMU_HAS(VIRTIOBLK) */
173173

174+
/* clint */
175+
typedef struct {
176+
uint32_t msip[4096];
177+
uint64_t mtimecmp[4095];
178+
uint64_t mtime;
179+
} clint_state_t;
180+
181+
void clint_update_interrupts(vm_t *vm, clint_state_t *clint);
182+
void clint_read(vm_t *vm,
183+
clint_state_t *clint,
184+
uint32_t addr,
185+
uint8_t width,
186+
uint32_t *value);
187+
188+
void clint_write(vm_t *vm,
189+
clint_state_t *clint,
190+
uint32_t addr,
191+
uint8_t width,
192+
uint32_t value);
193+
174194
/* memory mapping */
175195

176196
typedef struct {
@@ -185,5 +205,5 @@ typedef struct {
185205
#if SEMU_HAS(VIRTIOBLK)
186206
virtio_blk_state_t vblk;
187207
#endif
188-
uint64_t timer;
208+
clint_state_t clint;
189209
} emu_state_t;

main.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ static void emu_update_vblk_interrupts(vm_t *vm)
7272
}
7373
#endif
7474

75+
static void emu_update_timer_interrupt(vm_t *vm)
76+
{
77+
emu_state_t *data = PRIV(vm);
78+
vm->time = data->clint.mtime;
79+
clint_update_interrupts(vm, &data->clint);
80+
}
81+
82+
static void emu_update_global_timer(vm_t *vm)
83+
{
84+
emu_state_t *data = PRIV(vm);
85+
data->clint.mtime++;
86+
return;
87+
}
88+
89+
7590
static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
7691
{
7792
emu_state_t *data = PRIV(vm);
@@ -105,6 +120,9 @@ static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
105120
emu_update_vblk_interrupts(vm);
106121
return;
107122
#endif
123+
case 0x43: /* clint */
124+
clint_read(vm, &data->clint, addr & 0xFFFFF, width, value);
125+
clint_update_interrupts(vm, &data->clint);
108126
}
109127
}
110128
vm_set_exception(vm, RV_EXC_LOAD_FAULT, vm->exc_val);
@@ -143,6 +161,10 @@ static void mem_store(vm_t *vm, uint32_t addr, uint8_t width, uint32_t value)
143161
emu_update_vblk_interrupts(vm);
144162
return;
145163
#endif
164+
case 0x43: /* clint */
165+
clint_write(vm, &data->clint, addr & 0xFFFFF, width, value);
166+
clint_update_interrupts(vm, &data->clint);
167+
return;
146168
}
147169
}
148170
vm_set_exception(vm, RV_EXC_STORE_FAULT, vm->exc_val);
@@ -162,8 +184,9 @@ static inline sbi_ret_t handle_sbi_ecall_TIMER(vm_t *vm, int32_t fid)
162184
emu_state_t *data = PRIV(vm);
163185
switch (fid) {
164186
case SBI_TIMER__SET_TIMER:
165-
data->timer = (((uint64_t) vm->x_regs[RV_R_A1]) << 32) |
166-
(uint64_t) (vm->x_regs[RV_R_A0]);
187+
data->clint.mtimecmp[0] = (((uint64_t) vm->x_regs[RV_R_A1]) << 32) |
188+
(uint64_t) (vm->x_regs[RV_R_A0]);
189+
vm->sip &= ~RV_INT_STI_BIT;
167190
return (sbi_ret_t){SBI_SUCCESS, 0};
168191
default:
169192
return (sbi_ret_t){SBI_ERR_NOT_SUPPORTED, 0};
@@ -369,6 +392,7 @@ static int semu_start(int argc, char **argv)
369392
.mem_store = mem_store,
370393
.mem_page_table = mem_page_table,
371394
};
395+
372396
vm_init(&vm);
373397

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

408432
/* Set up RISC-V hart */
409-
emu.timer = 0xFFFFFFFFFFFFFFFF;
410433
vm.s_mode = true;
411434
vm.x_regs[RV_R_A0] = 0; /* hart ID. i.e., cpuid */
412435
vm.x_regs[RV_R_A1] = dtb_addr;
@@ -428,6 +451,7 @@ static int semu_start(int argc, char **argv)
428451
/* Emulate */
429452
uint32_t peripheral_update_ctr = 0;
430453
while (!emu.stopped) {
454+
emu_update_global_timer(&vm);
431455
if (peripheral_update_ctr-- == 0) {
432456
peripheral_update_ctr = 64;
433457

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

450-
if (vm.insn_count > emu.timer)
451-
vm.sip |= RV_INT_STI_BIT;
452-
else
453-
vm.sip &= ~RV_INT_STI_BIT;
474+
emu_update_timer_interrupt(&vm);
454475

455476
vm_step(&vm);
456477
if (likely(!vm.error))

minimal.dts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,12 @@
8181
interrupts = <3>;
8282
};
8383
#endif
84+
clint0: clint@4300000 {
85+
compatible = "riscv,clint0";
86+
interrupt-controller;
87+
interrupts-extended =
88+
<&cpu0_intc 3 &cpu0_intc 7>;
89+
reg = <0x4300000 0x10000>;
90+
};
8491
};
8592
};

riscv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ struct __vm_internal {
6969
* resets.
7070
*/
7171
uint64_t insn_count;
72+
uint64_t time;
7273

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

0 commit comments

Comments
 (0)