Skip to content

Commit

Permalink
Replace mtime counter with realtime timer
Browse files Browse the repository at this point in the history
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
  • Loading branch information
chiangkd committed Jul 28, 2024
1 parent 24f5c80 commit 3cd64f3
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 25 deletions.
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 :=
Expand Down Expand Up @@ -42,6 +47,7 @@ all: $(BIN) minimal.dtb
OBJS := \
riscv.o \
ram.o \
utils.o \
plic.o \
uart.o \
main.o \
Expand Down Expand Up @@ -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) - > $@

Expand Down
15 changes: 9 additions & 6 deletions clint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion device.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 1 addition & 9 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion minimal.dts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
cpus {
#address-cells = <1>;
#size-cells = <0>;
timebase-frequency = <65000000>;
timebase-frequency = <CLOCK_FREQ>;
};

sram: memory@0 {
Expand Down
4 changes: 2 additions & 2 deletions riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <stdbool.h>
#include <stdint.h>

#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
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 4 additions & 3 deletions scripts/gen-hart-dts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}
}};
Expand Down Expand Up @@ -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))
52 changes: 52 additions & 0 deletions utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <time.h>

#include "utils.h"

#if defined(__APPLE__)
#define HAVE_MACH_TIMER
#include <mach/mach_time.h>
#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;
}
13 changes: 13 additions & 0 deletions utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <stdint.h>

/* 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);

0 comments on commit 3cd64f3

Please sign in to comment.