Skip to content

Commit 2fde42d

Browse files
committed
Replace mtime counter with realtime timer
Replace 'mtime' counter by retrieving time of the specified clock ID. Additionally, a Real-Time counter (RTC) mechanism is implemented to prevent 'mtime' overflow. By changing 'mtime' to use the retrieved timer, real timestamps are visible in the booting log due to 'CONFIG_PRINTK_TIME' being enable by default. However, SEMU experiences a slowdown as the number of simulated harts increases. This is due to the increased number of timer interrupts and 'clock_gettime' system calls in this commit. Another issue addressed is RCU CPU stall warning[1], which occurs when the system waits more than a grace period (typically 21 seconds). [1]: https://docs.kernel.org/RCU/stallwarn.html
1 parent 24f5c80 commit 2fde42d

File tree

10 files changed

+89
-25
lines changed

10 files changed

+89
-25
lines changed

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ CC ?= gcc
44
CFLAGS := -O2 -g -Wall -Wextra
55
CFLAGS += -include common.h
66

7+
# clock frequency
8+
CLOCK_FREQ ?= 65000000
9+
DT_CFLAGS := -D CLOCK_FREQ=$(CLOCK_FREQ)
10+
CFLAGS += $(DT_CFLAGS)
11+
712
OBJS_EXTRA :=
813
# command line option
914
OPTS :=
@@ -42,6 +47,7 @@ all: $(BIN) minimal.dtb
4247
OBJS := \
4348
riscv.o \
4449
ram.o \
50+
utils.o \
4551
plic.o \
4652
uart.o \
4753
main.o \
@@ -72,11 +78,12 @@ S := $E $E
7278
SMP ?= 1
7379
.PHONY: riscv-harts.dtsi
7480
riscv-harts.dtsi:
75-
$(Q)python3 scripts/gen-hart-dts.py $@ $(SMP)
81+
$(Q)python3 scripts/gen-hart-dts.py $@ $(SMP) $(CLOCK_FREQ)
7682

7783
minimal.dtb: minimal.dts riscv-harts.dtsi
7884
$(VECHO) " DTC\t$@\n"
7985
$(Q)$(CC) -nostdinc -E -P -x assembler-with-cpp -undef \
86+
$(DT_CFLAGS) \
8087
$(subst ^,$S,$(filter -D^SEMU_FEATURE_%, $(subst -D$(S)SEMU_FEATURE,-D^SEMU_FEATURE,$(CFLAGS)))) $< \
8188
| $(DTC) - > $@
8289

clint.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
void clint_update_interrupts(hart_t *hart, clint_state_t *clint)
77
{
8-
if (clint->mtime > clint->mtimecmp[hart->mhartid])
8+
uint64_t time_delta =
9+
clint->mtimecmp[hart->mhartid] - semu_timer_get(&hart->time);
10+
if ((int64_t) time_delta <= 0)
911
hart->sip |= RV_INT_STI_BIT;
1012
else
1113
hart->sip &= ~RV_INT_STI_BIT;
@@ -31,7 +33,8 @@ static bool clint_reg_read(clint_state_t *clint, uint32_t addr, uint32_t *value)
3133
}
3234

3335
if (addr < 0xBFFF) {
34-
*value = clint->mtime >> (32 & -!!(addr & 0b100));
36+
*value = (uint32_t) (semu_timer_get(&clint->mtime) >>
37+
(32 & -!!(addr & 0b100)));
3538
return true;
3639
}
3740
return false;
@@ -58,14 +61,14 @@ static bool clint_reg_write(clint_state_t *clint, uint32_t addr, uint32_t value)
5861
}
5962

6063
if (addr < 0xBFFF) {
61-
int32_t upper = clint->mtime >> 32;
62-
int32_t lowwer = clint->mtime;
64+
int32_t upper = clint->mtime.begin >> 32;
65+
int32_t lower = clint->mtime.begin;
6366
if (addr & 0b100)
6467
upper = value;
6568
else
66-
lowwer = value;
69+
lower = value;
6770

68-
clint->mtime = (uint64_t) upper << 32 | lowwer;
71+
semu_timer_rebase(&clint->mtime, (uint64_t) upper << 32 | lower);
6972
return true;
7073
}
7174
return false;

device.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
175175
typedef struct {
176176
uint32_t msip[4096];
177177
uint64_t mtimecmp[4095];
178-
uint64_t mtime;
178+
semu_timer_t mtime;
179179
} clint_state_t;
180180

181181
void clint_update_interrupts(hart_t *vm, clint_state_t *clint);

main.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,6 @@ static void emu_update_timer_interrupt(hart_t *hart)
8181
clint_update_interrupts(hart, &data->clint);
8282
}
8383

84-
static void emu_update_global_timer(vm_t *vm)
85-
{
86-
emu_state_t *data = PRIV(vm->hart[0]);
87-
data->clint.mtime++;
88-
return;
89-
}
90-
9184
static void mem_load(hart_t *hart,
9285
uint32_t addr,
9386
uint8_t width,
@@ -523,6 +516,7 @@ static int semu_start(int argc, char **argv)
523516
/* Initialize the emulator */
524517
emu_state_t emu;
525518
memset(&emu, 0, sizeof(emu));
519+
semu_timer_init(&emu.clint.mtime, CLOCK_FREQ);
526520

527521
/* Set up RAM */
528522
emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE,
@@ -591,8 +585,6 @@ static int semu_start(int argc, char **argv)
591585
/* Emulate */
592586
uint32_t peripheral_update_ctr = 0;
593587
while (!emu.stopped) {
594-
emu_update_global_timer(&vm);
595-
596588
for (uint32_t i = 0; i < vm.n_hart; i++) {
597589
if (peripheral_update_ctr-- == 0) {
598590
peripheral_update_ctr = 64;

minimal.dts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
cpus {
2525
#address-cells = <1>;
2626
#size-cells = <0>;
27-
timebase-frequency = <65000000>;
27+
timebase-frequency = <CLOCK_FREQ>;
2828
};
2929

3030
sram: memory@0 {

riscv.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,10 @@ static void csr_read(hart_t *vm, uint16_t addr, uint32_t *value)
434434
{
435435
switch (addr) {
436436
case RV_CSR_TIME:
437-
*value = vm->time;
437+
*value = semu_timer_get(&vm->time);
438438
return;
439439
case RV_CSR_TIMEH:
440-
*value = vm->time >> 32;
440+
*value = semu_timer_get(&vm->time) >> 32;
441441
return;
442442
case RV_CSR_INSTRET:
443443
*value = vm->instret;

riscv.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <stdbool.h>
44
#include <stdint.h>
55

6+
#include "utils.h"
7+
68
/* ERR_EXCEPTION indicates that the instruction has raised one of the
79
* exceptions defined in the specification. If this flag is set, the
810
* additional fields "exc_cause" and "exc_val" must also be set to values
@@ -70,8 +72,7 @@ struct __hart_internal {
7072
* resets.
7173
*/
7274
uint64_t instret;
73-
uint64_t time;
74-
75+
semu_timer_t time;
7576
/* Instruction execution state must be set to "NONE" for instruction
7677
* execution to continue. If the state is not "NONE," the vm_step()
7778
* function will exit.

scripts/gen-hart-dts.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ def clint_irq_format(nums):
3434
s += f"<&cpu{i}_intc 3 &cpu{i}_intc 7>, "
3535
return s[:-2]
3636

37-
def dtsi_template (cpu_list: str, plic_list, clint_list):
37+
def dtsi_template (cpu_list: str, plic_list, clint_list, clock_freq):
3838
return f"""/{{
3939
cpus {{
4040
#address-cells = <1>;
4141
#size-cells = <0>;
42-
timebase-frequency = <65000000>;
42+
timebase-frequency = <{clock_freq}>;
4343
{cpu_list}
4444
}};
4545
@@ -67,6 +67,7 @@ def dtsi_template (cpu_list: str, plic_list, clint_list):
6767

6868
dtsi = sys.argv[1]
6969
harts = int(sys.argv[2])
70+
clock_freq = int(sys.argv[3])
7071

7172
with open(dtsi, "w") as dts:
72-
dts.write(dtsi_template(cpu_format(harts), plic_irq_format(harts), clint_irq_format(harts)))
73+
dts.write(dtsi_template(cpu_format(harts), plic_irq_format(harts), clint_irq_format(harts), clock_freq))

utils.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include <time.h>
2+
3+
#include "utils.h"
4+
5+
#if defined(__APPLE__)
6+
#define HAVE_MACH_TIMER
7+
#include <mach/mach_time.h>
8+
#elif !defined(_WIN32) && !defined(_WIN64)
9+
#define HAVE_POSIX_TIMER
10+
#ifdef CLOCK_MONOTONIC
11+
#define CLOCKID CLOCK_MONOTONIC
12+
#else
13+
#define CLOCKID CLOCK_REALTIME
14+
#endif
15+
#endif
16+
17+
void semu_timer_init(semu_timer_t *timer, uint64_t freq)
18+
{
19+
timer->freq = freq;
20+
semu_timer_rebase(timer, 0);
21+
}
22+
23+
static uint64_t semu_timer_clocksource(uint64_t freq)
24+
{
25+
#if defined(HAVE_POSIX_TIMER)
26+
struct timespec t;
27+
clock_gettime(CLOCKID, &t);
28+
return (t.tv_sec * freq) + (t.tv_nsec * freq / 1e9);
29+
#elif defined(HAVE_MACH_TIMER)
30+
static mach_timebase_info_data_t t;
31+
if (mach_clk.denom == 0)
32+
(void) mach_timebase_info(&t);
33+
return mach_absolute_time() * freq / t.denom * t.numer;
34+
#else
35+
return time(0) * freq;
36+
#endif
37+
}
38+
39+
uint64_t semu_timer_get(semu_timer_t *timer)
40+
{
41+
return semu_timer_clocksource(timer->freq) - timer->begin;
42+
}
43+
44+
void semu_timer_rebase(semu_timer_t *timer, uint64_t time)
45+
{
46+
timer->begin = semu_timer_clocksource(timer->freq) - time;
47+
}

utils.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#include <stdint.h>
4+
5+
/* TIMER */
6+
typedef struct {
7+
uint64_t begin;
8+
uint64_t freq;
9+
} semu_timer_t;
10+
11+
void semu_timer_init(semu_timer_t *timer, uint64_t freq);
12+
uint64_t semu_timer_get(semu_timer_t *timer);
13+
void semu_timer_rebase(semu_timer_t *timer, uint64_t time);

0 commit comments

Comments
 (0)