[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 16/18] target-riscv: Timer Support
From: |
Sagar Karandikar |
Subject: |
[Qemu-devel] [PATCH 16/18] target-riscv: Timer Support |
Date: |
Mon, 26 Sep 2016 03:56:46 -0700 |
Signed-off-by: Sagar Karandikar <address@hidden>
---
hw/riscv/Makefile.objs | 1 +
hw/riscv/cpudevs.h | 17 +++
hw/riscv/riscv_rtc.c | 230 ++++++++++++++++++++++++++++++++++
include/hw/riscv/riscv_rtc.h | 25 ++++
include/hw/riscv/riscv_rtc_internal.h | 3 +
target-riscv/op_helper.c | 4 +-
6 files changed, 278 insertions(+), 2 deletions(-)
create mode 100644 hw/riscv/Makefile.objs
create mode 100644 hw/riscv/cpudevs.h
create mode 100644 hw/riscv/riscv_rtc.c
create mode 100644 include/hw/riscv/riscv_rtc.h
create mode 100644 include/hw/riscv/riscv_rtc_internal.h
diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs
new file mode 100644
index 0000000..79b4553
--- /dev/null
+++ b/hw/riscv/Makefile.objs
@@ -0,0 +1 @@
+obj-y += riscv_rtc.o
diff --git a/hw/riscv/cpudevs.h b/hw/riscv/cpudevs.h
new file mode 100644
index 0000000..f2f4195
--- /dev/null
+++ b/hw/riscv/cpudevs.h
@@ -0,0 +1,17 @@
+#ifndef HW_RISCV_CPUDEVS_H
+#define HW_RISCV_CPUDEVS_H
+
+#include "target-riscv/cpu.h"
+
+/* Definitions for RISCV CPU internal devices. */
+
+/* riscv_board.c */
+uint64_t identity_translate(void *opaque, uint64_t addr);
+
+/* riscv_int.c */
+void cpu_riscv_irq_init_cpu(CPURISCVState *env);
+
+/* cputimer.c */
+void cpu_riscv_clock_init(CPURISCVState *);
+
+#endif
diff --git a/hw/riscv/riscv_rtc.c b/hw/riscv/riscv_rtc.c
new file mode 100644
index 0000000..178c7ff
--- /dev/null
+++ b/hw/riscv/riscv_rtc.c
@@ -0,0 +1,230 @@
+/*
+ * QEMU RISC-V timer, instret counter support
+ *
+ * Author: Sagar Karandikar, address@hidden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/riscv/cpudevs.h"
+#include "hw/riscv/riscv_rtc_internal.h"
+#include "hw/riscv/riscv_rtc.h"
+#include "qemu/timer.h"
+
+/*#define TIMER_DEBUGGING_RISCV */
+
+/* this is the "right value" for defaults in pk/linux
+ see pk/sbi_entry.S and arch/riscv/kernel/time.c call to
+ clockevents_config_and_register */
+#define TIMER_FREQ (10 * 1000 * 1000)
+/* CPU_FREQ is for instret approximation - say we're running at 1 BIPS */
+#define CPU_FREQ (1000 * 1000 * 1000)
+
+inline uint64_t rtc_read(CPURISCVState *env)
+{
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ,
+ NANOSECONDS_PER_SECOND);
+}
+
+inline uint64_t instret_read(CPURISCVState *env)
+{
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), CPU_FREQ,
+ NANOSECONDS_PER_SECOND);
+}
+
+/*
+ * Called when timecmp is written to update the QEMU timer or immediately
+ * trigger timer interrupt if mtimecmp <= current timer value.
+ */
+static inline void cpu_riscv_timer_update(CPURISCVState *env)
+{
+ uint64_t next;
+ uint64_t diff;
+
+ uint64_t rtc_r = rtc_read(env);
+
+ #ifdef TIMER_DEBUGGING_RISCV
+ printf("timer update: mtimecmp %016lx, timew %016lx\n",
+ env->timecmp, rtc_r);
+ #endif
+
+ if (env->timecmp <= rtc_r) {
+ /* if we're setting an MTIMECMP value in the "past",
+ immediately raise the timer interrupt */
+ env->csr[CSR_MIP] |= MIP_MTIP;
+ qemu_irq_raise(env->irq[3]);
+ return;
+ }
+
+ /* otherwise, set up the future timer interrupt */
+ diff = env->timecmp - rtc_r;
+ /* back to ns (note args switched in muldiv64) */
+ next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ);
+ timer_mod(env->timer, next);
+}
+
+/*
+ * Called by the callback used when the timer set using timer_mod expires.
+ * Should raise the timer interrupt line
+ */
+static inline void cpu_riscv_timer_expire(CPURISCVState *env)
+{
+ /* do not call update here */
+ env->csr[CSR_MIP] |= MIP_MTIP;
+ qemu_irq_raise(env->irq[3]);
+}
+
+/* used in op_helper.c */
+inline uint64_t cpu_riscv_read_instret(CPURISCVState *env)
+{
+ uint64_t retval = instret_read(env);
+ return retval;
+}
+
+inline void write_timecmp(CPURISCVState *env, uint64_t value)
+{
+ #ifdef TIMER_DEBUGGING_RISCV
+ uint64_t rtc_r = rtc_read_with_delta(env);
+ printf("wrote mtimecmp %016lx, timew %016lx\n", value, rtc_r);
+ #endif
+
+ env->timecmp = value;
+ env->csr[CSR_MIP] &= ~MIP_MTIP;
+ cpu_riscv_timer_update(env);
+}
+
+/*
+ * Callback used when the timer set using timer_mod expires.
+ */
+static void riscv_timer_cb(void *opaque)
+{
+ CPURISCVState *env;
+ env = opaque;
+ cpu_riscv_timer_expire(env);
+}
+
+/*
+ * Initialize clock mechanism.
+ */
+void cpu_riscv_clock_init(CPURISCVState *env)
+{
+ env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_timer_cb, env);
+ env->timecmp = 0;
+}
+
+
+#include "target-riscv/cpu.h"
+
+static void timer_pre_save(void *opaque)
+{
+ return;
+}
+
+static int timer_post_load(void *opaque, int version_id)
+{
+ return 0;
+}
+
+const VMStateDescription vmstate_timer_rv = {
+ .name = "rvtimer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = timer_pre_save,
+ .post_load = timer_post_load,
+ .fields = (VMStateField []) { /* TODO what */
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+/* CPU wants to read rtc or timecmp register */
+static uint64_t timer_mm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ TIMERState *timerstate = opaque;
+ if (addr == 0) {
+ /* rtc */
+ timerstate->temp_rtc_val = rtc_read(timerstate->env);
+ return timerstate->temp_rtc_val & 0xFFFFFFFF;
+ } else if (addr == 4) {
+ /* rtc */
+ return (timerstate->temp_rtc_val >> 32) & 0xFFFFFFFF;
+ } else if (addr == 8) {
+ /* timecmp */
+ printf("TIMECMP READ NOT IMPL\n");
+ exit(1);
+ } else if (addr == 0xc) {
+ /* timecmp */
+ printf("TIMECMP READ NOT IMPL\n");
+ exit(1);
+ } else {
+ printf("Invalid timer register address %016lx\n", (uint64_t)addr);
+ exit(1);
+ }
+}
+
+/* CPU wrote to rtc or timecmp register */
+static void timer_mm_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ TIMERState *timerstate = opaque;
+ if (addr == 0) {
+ /*rtc */
+ printf("RTC WRITE NOT IMPL\n");
+ exit(1);
+ } else if (addr == 4) {
+ /*rtc */
+ printf("RTC WRITE NOT IMPL\n");
+ exit(1);
+ } else if (addr == 8) {
+ /* timecmp */
+ timerstate->timecmp_lower = value & 0xFFFFFFFF;
+ } else if (addr == 0xc) {
+ /* timecmp */
+ write_timecmp(timerstate->env, value << 32 |
+ timerstate->timecmp_lower);
+ } else {
+ printf("Invalid timer register address %016lx\n", (uint64_t)addr);
+ exit(1);
+ }
+}
+
+static const MemoryRegionOps timer_mm_ops[3] = {
+ [DEVICE_LITTLE_ENDIAN] = {
+ .read = timer_mm_read,
+ .write = timer_mm_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ },
+};
+
+TIMERState *timer_mm_init(MemoryRegion *address_space, hwaddr base,
+ CPURISCVState *env)
+{
+ TIMERState *timerstate;
+ timerstate = g_malloc0(sizeof(TIMERState));
+ timerstate->env = env;
+ timerstate->temp_rtc_val = 0;
+ vmstate_register(NULL, base, &vmstate_timer_rv, timerstate);
+ memory_region_init_io(&timerstate->io, NULL,
+ &timer_mm_ops[DEVICE_LITTLE_ENDIAN],
+ timerstate, "timer", 16 /* 2 64-bit registers */);
+ memory_region_add_subregion(address_space, base, &timerstate->io);
+ return timerstate;
+}
diff --git a/include/hw/riscv/riscv_rtc.h b/include/hw/riscv/riscv_rtc.h
new file mode 100644
index 0000000..d5c422c
--- /dev/null
+++ b/include/hw/riscv/riscv_rtc.h
@@ -0,0 +1,25 @@
+#ifndef HW_RISCV_TIMER_H
+#define HW_RISCV_TIMER_H 1
+
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "exec/memory.h"
+#include "target-riscv/cpu.h"
+
+typedef struct TIMERState TIMERState;
+
+struct TIMERState {
+ CPURISCVState *env;
+ MemoryRegion io;
+ uint32_t timecmp_lower;
+ uint64_t temp_rtc_val;
+};
+
+extern const VMStateDescription vmstate_timer_rv;
+extern const MemoryRegionOps timer_io_ops;
+
+/* legacy pre qom */
+TIMERState *timer_mm_init(MemoryRegion *address_space, hwaddr base,
+ CPURISCVState *env);
+
+#endif
diff --git a/include/hw/riscv/riscv_rtc_internal.h
b/include/hw/riscv/riscv_rtc_internal.h
new file mode 100644
index 0000000..d13e104
--- /dev/null
+++ b/include/hw/riscv/riscv_rtc_internal.h
@@ -0,0 +1,3 @@
+uint64_t rtc_read(CPURISCVState *env);
+uint64_t instret_read(CPURISCVState *env);
+void write_timecmp(CPURISCVState *env, uint64_t value);
diff --git a/target-riscv/op_helper.c b/target-riscv/op_helper.c
index 1ecdef2..54d7538 100644
--- a/target-riscv/op_helper.c
+++ b/target-riscv/op_helper.c
@@ -335,10 +335,10 @@ inline target_ulong csr_read_helper(CPURISCVState *env,
target_ulong csrno)
/* notice the lack of CSR_MTIME - this is handled by throwing an exception
and letting the handler read from the RTC */
case CSR_MCYCLE:
- /* Read instret here */
+ return cpu_riscv_read_instret(env);
break;
case CSR_MINSTRET:
- /* Read instret here */
+ return cpu_riscv_read_instret(env);
break;
case CSR_MCYCLEH:
printf("CSR 0x%x unsupported on RV64\n", csrno2);
--
2.9.3
- Re: [Qemu-devel] [PATCH 01/18] target-riscv: Add RISC-V target stubs and Maintainer, (continued)
- [Qemu-devel] [PATCH 15/18] target-riscv: Interrupt Handling, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 07/18] target-riscv: Add Loads/Stores, FP Loads/Stores, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 08/18] target-riscv: Add Atomic Instructions, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 03/18] target-riscv: Add initialization for translation, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 14/18] target-riscv: softmmu/address translation support, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 16/18] target-riscv: Timer Support,
Sagar Karandikar <=
- [Qemu-devel] [PATCH 12/18] target-riscv: Add system instructions, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 13/18] target-riscv: Add CSR read/write helpers, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 09/18] target-riscv: Add FMADD, FMSUB, FNMADD, FNMSUB Instructions,, Sagar Karandikar, 2016/09/26
- Re: [Qemu-devel] [PATCH 09/18] target-riscv: Add FMADD, FMSUB, FNMADD, FNMSUB Instructions,, Richard Henderson, 2016/09/26
- Re: [Qemu-devel] [PATCH 09/18] target-riscv: Add FMADD, FMSUB, FNMADD, FNMSUB Instructions,, Richard Henderson, 2016/09/27