@@ -1145,6 +1145,7 @@ struct CPUMIPSState {
struct MIPSITUState *itu;
MemoryRegion *itc_tag; /* ITC Configuration Tags */
target_ulong exception_base; /* ExceptionBase input to the core */
+ unsigned cp0_count_ns; /* CP0_Count clock period (in nanoseconds) */
};
/**
@@ -1160,6 +1161,14 @@ struct MIPSCPU {
CPUNegativeOffsetState neg;
CPUMIPSState env;
+ /*
+ * The Count register acts as a timer, incrementing at a constant rate,
+ * whether or not an instruction is executed, retired, or any forward
+ * progress is made through the pipeline. The rate at which the counter
+ * increments is implementation dependent, and is a function of the
+ * pipeline clock of the processor, not the issue width of the processor.
+ */
+ unsigned cp0_count_rate;
};
@@ -27,8 +27,6 @@
#include "sysemu/kvm.h"
#include "internal.h"
-#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
-
/* MIPS R4K timer */
static void cpu_mips_timer_update(CPUMIPSState *env)
{
@@ -37,8 +35,8 @@ static void cpu_mips_timer_update(CPUMIPSState *env)
now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
wait = env->CP0_Compare - env->CP0_Count -
- (uint32_t)(now_ns / TIMER_PERIOD);
- next_ns = now_ns + (uint64_t)wait * TIMER_PERIOD;
+ (uint32_t)(now_ns / env->cp0_count_ns);
+ next_ns = now_ns + (uint64_t)wait * env->cp0_count_ns;
timer_mod(env->timer, next_ns);
}
@@ -66,7 +64,7 @@ uint32_t cpu_mips_get_count(CPUMIPSState *env)
cpu_mips_timer_expire(env);
}
- return env->CP0_Count + (uint32_t)(now_ns / TIMER_PERIOD);
+ return env->CP0_Count + (uint32_t)(now_ns / env->cp0_count_ns);
}
}
@@ -82,7 +80,8 @@ void cpu_mips_store_count(CPUMIPSState *env, uint32_t count)
} else {
/* Store new count register */
env->CP0_Count = count -
- (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
+ (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
+ env->cp0_count_ns);
/* Update timer timer */
cpu_mips_timer_update(env);
}
@@ -109,7 +108,7 @@ void cpu_mips_stop_count(CPUMIPSState *env)
{
/* Store the current value */
env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
- TIMER_PERIOD);
+ env->cp0_count_ns);
}
static void mips_timer_cb(void *opaque)
@@ -26,6 +26,7 @@
#include "qemu/module.h"
#include "sysemu/kvm.h"
#include "exec/exec-all.h"
+#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
static void mips_cpu_set_pc(CPUState *cs, vaddr value)
@@ -134,6 +135,22 @@ static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info)
}
}
+static void mips_cp0_period_set(MIPSCPU *cpu)
+{
+ CPUMIPSState *env = &cpu->env;
+
+ /* Recompute CP0's period on clock change */
+ env->cp0_count_ns = cpu->cp0_count_rate * clock_get_ns(CPU(cpu)->clock);
+}
+
+static void mips_cpu_clk_update(CPUState *cs)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+
+ /* Recompute CP0's period on clock change */
+ mips_cp0_period_set(cpu);
+}
+
static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
@@ -148,6 +165,7 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
*/
clock_set_hz(cs->clock, 200000000);
}
+ mips_cpu_clk_update(cs);
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
@@ -190,6 +208,7 @@ static ObjectClass *mips_cpu_class_by_name(const char *cpu_model)
}
static Property mips_cpu_properties[] = {
+ DEFINE_PROP_UINT32("CP0_Count-rate", MIPSCPU, cp0_count_rate, 2),
DEFINE_PROP_END_OF_LIST()
};
@@ -224,6 +243,7 @@ static void mips_cpu_class_init(ObjectClass *c, void *data)
cc->tcg_initialize = mips_tcg_init;
cc->tlb_fill = mips_cpu_tlb_fill;
#endif
+ cc->clock_update = mips_cpu_clk_update;
cc->gdb_num_core_regs = 73;
cc->gdb_stop_before_watchpoint = true;
Since commit 6af0bf9c7c3 the CP0 counter is running at half the frequency of a 200 MHz CPU: a static 100 MHz value is used. By using the Clock API we can change the CPU frequency at runtime, so the CP0 counter might run out of sync with the CPU clock. Avoid that by using the recently introduced CPUClass::clock_update() callback. If the CPU clock is dynamically changed, our CP0 counter will be updated correspondingly. Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org> --- target/mips/cpu.h | 9 +++++++++ target/mips/cp0_timer.c | 13 ++++++------- target/mips/cpu.c | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-)