From 3cd64f3a26c05b0110eade970c7b124fce41b113 Mon Sep 17 00:00:00 2001 From: chiangkd Date: Mon, 15 Jul 2024 03:14:17 +0800 Subject: [PATCH] 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 --- Makefile | 9 ++++++- clint.c | 15 +++++++----- device.h | 2 +- main.c | 10 +------- minimal.dts | 2 +- riscv.c | 4 ++-- riscv.h | 5 ++-- scripts/gen-hart-dts.py | 7 +++--- utils.c | 52 +++++++++++++++++++++++++++++++++++++++++ utils.h | 13 +++++++++++ 10 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 utils.c create mode 100644 utils.h diff --git a/Makefile b/Makefile index caaab7d..ceb397f 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,11 @@ CC ?= gcc CFLAGS := -O2 -g -Wall -Wextra CFLAGS += -include common.h +# clock frequency +CLOCK_FREQ ?= 65000000 +DT_CFLAGS := -D CLOCK_FREQ=$(CLOCK_FREQ) +CFLAGS += $(DT_CFLAGS) + OBJS_EXTRA := # command line option OPTS := @@ -42,6 +47,7 @@ all: $(BIN) minimal.dtb OBJS := \ riscv.o \ ram.o \ + utils.o \ plic.o \ uart.o \ main.o \ @@ -72,11 +78,12 @@ S := $E $E SMP ?= 1 .PHONY: riscv-harts.dtsi riscv-harts.dtsi: - $(Q)python3 scripts/gen-hart-dts.py $@ $(SMP) + $(Q)python3 scripts/gen-hart-dts.py $@ $(SMP) $(CLOCK_FREQ) minimal.dtb: minimal.dts riscv-harts.dtsi $(VECHO) " DTC\t$@\n" $(Q)$(CC) -nostdinc -E -P -x assembler-with-cpp -undef \ + $(DT_CFLAGS) \ $(subst ^,$S,$(filter -D^SEMU_FEATURE_%, $(subst -D$(S)SEMU_FEATURE,-D^SEMU_FEATURE,$(CFLAGS)))) $< \ | $(DTC) - > $@ diff --git a/clint.c b/clint.c index f5560f6..fd3c8e9 100644 --- a/clint.c +++ b/clint.c @@ -5,7 +5,9 @@ void clint_update_interrupts(hart_t *hart, clint_state_t *clint) { - if (clint->mtime > clint->mtimecmp[hart->mhartid]) + uint64_t time_delta = + clint->mtimecmp[hart->mhartid] - semu_timer_get(&hart->time); + if ((int64_t) time_delta <= 0) hart->sip |= RV_INT_STI_BIT; else 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) } if (addr < 0xBFFF) { - *value = clint->mtime >> (32 & -!!(addr & 0b100)); + *value = (uint32_t) (semu_timer_get(&clint->mtime) >> + (32 & -!!(addr & 0b100))); return true; } return false; @@ -58,14 +61,14 @@ static bool clint_reg_write(clint_state_t *clint, uint32_t addr, uint32_t value) } if (addr < 0xBFFF) { - int32_t upper = clint->mtime >> 32; - int32_t lowwer = clint->mtime; + int32_t upper = clint->mtime.begin >> 32; + int32_t lower = clint->mtime.begin; if (addr & 0b100) upper = value; else - lowwer = value; + lower = value; - clint->mtime = (uint64_t) upper << 32 | lowwer; + semu_timer_rebase(&clint->mtime, (uint64_t) upper << 32 | lower); return true; } return false; diff --git a/device.h b/device.h index d95bb6a..1bb7900 100644 --- a/device.h +++ b/device.h @@ -175,7 +175,7 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file); typedef struct { uint32_t msip[4096]; uint64_t mtimecmp[4095]; - uint64_t mtime; + semu_timer_t mtime; } clint_state_t; void clint_update_interrupts(hart_t *vm, clint_state_t *clint); diff --git a/main.c b/main.c index 2e011c6..4a92113 100644 --- a/main.c +++ b/main.c @@ -81,13 +81,6 @@ static void emu_update_timer_interrupt(hart_t *hart) clint_update_interrupts(hart, &data->clint); } -static void emu_update_global_timer(vm_t *vm) -{ - emu_state_t *data = PRIV(vm->hart[0]); - data->clint.mtime++; - return; -} - static void mem_load(hart_t *hart, uint32_t addr, uint8_t width, @@ -523,6 +516,7 @@ static int semu_start(int argc, char **argv) /* Initialize the emulator */ emu_state_t emu; memset(&emu, 0, sizeof(emu)); + semu_timer_init(&emu.clint.mtime, CLOCK_FREQ); /* Set up RAM */ emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE, @@ -591,8 +585,6 @@ static int semu_start(int argc, char **argv) /* Emulate */ uint32_t peripheral_update_ctr = 0; while (!emu.stopped) { - emu_update_global_timer(&vm); - for (uint32_t i = 0; i < vm.n_hart; i++) { if (peripheral_update_ctr-- == 0) { peripheral_update_ctr = 64; diff --git a/minimal.dts b/minimal.dts index 924d3d9..d83bcfc 100644 --- a/minimal.dts +++ b/minimal.dts @@ -24,7 +24,7 @@ cpus { #address-cells = <1>; #size-cells = <0>; - timebase-frequency = <65000000>; + timebase-frequency = ; }; sram: memory@0 { diff --git a/riscv.c b/riscv.c index bc5f50a..069aeeb 100644 --- a/riscv.c +++ b/riscv.c @@ -434,10 +434,10 @@ static void csr_read(hart_t *vm, uint16_t addr, uint32_t *value) { switch (addr) { case RV_CSR_TIME: - *value = vm->time; + *value = semu_timer_get(&vm->time); return; case RV_CSR_TIMEH: - *value = vm->time >> 32; + *value = semu_timer_get(&vm->time) >> 32; return; case RV_CSR_INSTRET: *value = vm->instret; diff --git a/riscv.h b/riscv.h index 4263778..62c9cf8 100644 --- a/riscv.h +++ b/riscv.h @@ -3,6 +3,8 @@ #include #include +#include "utils.h" + /* ERR_EXCEPTION indicates that the instruction has raised one of the * exceptions defined in the specification. If this flag is set, the * additional fields "exc_cause" and "exc_val" must also be set to values @@ -70,8 +72,7 @@ struct __hart_internal { * resets. */ uint64_t instret; - uint64_t time; - + semu_timer_t time; /* Instruction execution state must be set to "NONE" for instruction * execution to continue. If the state is not "NONE," the vm_step() * function will exit. diff --git a/scripts/gen-hart-dts.py b/scripts/gen-hart-dts.py index c57fb00..c6baa0b 100644 --- a/scripts/gen-hart-dts.py +++ b/scripts/gen-hart-dts.py @@ -34,12 +34,12 @@ def clint_irq_format(nums): s += f"<&cpu{i}_intc 3 &cpu{i}_intc 7>, " return s[:-2] -def dtsi_template (cpu_list: str, plic_list, clint_list): +def dtsi_template (cpu_list: str, plic_list, clint_list, clock_freq): return f"""/{{ cpus {{ #address-cells = <1>; #size-cells = <0>; - timebase-frequency = <65000000>; + timebase-frequency = <{clock_freq}>; {cpu_list} }}; @@ -67,6 +67,7 @@ def dtsi_template (cpu_list: str, plic_list, clint_list): dtsi = sys.argv[1] harts = int(sys.argv[2]) +clock_freq = int(sys.argv[3]) with open(dtsi, "w") as dts: - dts.write(dtsi_template(cpu_format(harts), plic_irq_format(harts), clint_irq_format(harts))) + dts.write(dtsi_template(cpu_format(harts), plic_irq_format(harts), clint_irq_format(harts), clock_freq)) diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..b88170b --- /dev/null +++ b/utils.c @@ -0,0 +1,52 @@ +#include + +#include "utils.h" + +#if defined(__APPLE__) +#define HAVE_MACH_TIMER +#include +#elif !defined(_WIN32) && !defined(_WIN64) +#define HAVE_POSIX_TIMER + +/* + * Use a faster but less precise clock source because we need quick + * timestamps rather than fine-grained precision. + */ +#ifdef CLOCK_MONOTONIC_COARSE +#define CLOCKID CLOCK_MONOTONIC_COARSE +#else +#define CLOCKID CLOCK_REALTIME_COARSE +#endif +#endif + +void semu_timer_init(semu_timer_t *timer, uint64_t freq) +{ + timer->freq = freq; + semu_timer_rebase(timer, 0); +} + +static uint64_t semu_timer_clocksource(uint64_t freq) +{ +#if defined(HAVE_POSIX_TIMER) + struct timespec t; + clock_gettime(CLOCKID, &t); + return (t.tv_sec * freq) + (t.tv_nsec * freq / 1e9); +#elif defined(HAVE_MACH_TIMER) + static mach_timebase_info_data_t t; + if (mach_clk.denom == 0) + (void) mach_timebase_info(&t); + return mach_absolute_time() * freq / t.denom * t.numer; +#else + return time(0) * freq; +#endif +} + +uint64_t semu_timer_get(semu_timer_t *timer) +{ + return semu_timer_clocksource(timer->freq) - timer->begin; +} + +void semu_timer_rebase(semu_timer_t *timer, uint64_t time) +{ + timer->begin = semu_timer_clocksource(timer->freq) - time; +} \ No newline at end of file diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..6e03ea0 --- /dev/null +++ b/utils.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +/* TIMER */ +typedef struct { + uint64_t begin; + uint64_t freq; +} semu_timer_t; + +void semu_timer_init(semu_timer_t *timer, uint64_t freq); +uint64_t semu_timer_get(semu_timer_t *timer); +void semu_timer_rebase(semu_timer_t *timer, uint64_t time); \ No newline at end of file