Reviewed-by: Pierrick Bouvier <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
 include/hw/hexagon/hexagon_globalreg.h |   5 +
 target/hexagon/helper.h                |   2 +
 hw/hexagon/hexagon_globalreg.c         | 129 +++++++++++++++++++++++++
 target/hexagon/cpu.c                   |   9 +-
 target/hexagon/genptr.c                |   8 ++
 target/hexagon/hexswi.c                |  10 +-
 target/hexagon/op_helper.c             |  45 +++++++++
 hw/hexagon/trace-events                |   1 +
 8 files changed, 205 insertions(+), 4 deletions(-)

diff --git a/include/hw/hexagon/hexagon_globalreg.h 
b/include/hw/hexagon/hexagon_globalreg.h
index 07437dfabb1..2d5c3c166b1 100644
--- a/include/hw/hexagon/hexagon_globalreg.h
+++ b/include/hw/hexagon/hexagon_globalreg.h
@@ -33,6 +33,10 @@ struct HexagonGlobalRegState {
     /* Global performance cycle counter base */
     uint64_t g_pcycle_base;
 
+    /* Global performance cycle counter clock state */
+    int64_t pcycle_start_ns;
+    bool pcycle_running;
+
     /* Properties for global register reset values */
     uint32_t boot_evb;           /* Boot Exception Vector Base (HEX_SREG_EVB) 
*/
     uint64_t config_table_addr;  /* Configuration table base */
@@ -56,6 +60,7 @@ void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, 
uint32_t reg,
                                     uint32_t value);
 
 /* Global performance cycle counter access */
+uint64_t hexagon_globalreg_read_pcycle(HexagonGlobalRegState *s);
 uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s);
 void hexagon_globalreg_set_pcycle_base(HexagonGlobalRegState *s,
                                        uint64_t value);
diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h
index 997d87e38e6..47958c48ea9 100644
--- a/target/hexagon/helper.h
+++ b/target/hexagon/helper.h
@@ -130,6 +130,8 @@ DEF_HELPER_2(getimask, i32, env, i32)
 DEF_HELPER_3(setimask, void, env, i32, i32)
 DEF_HELPER_2(sreg_read, i32, env, i32)
 DEF_HELPER_2(sreg_read_pair, i64, env, i32)
+DEF_HELPER_2(upcycle_read, i32, env, i32)
+DEF_HELPER_1(upcycle_read_pair, i64, env)
 DEF_HELPER_2(greg_read, i32, env, i32)
 DEF_HELPER_2(greg_read_pair, i64, env, i32)
 DEF_HELPER_3(sreg_write_masked, void, env, i32, i32)
diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c
index 616cddf3048..22ec4020665 100644
--- a/hw/hexagon/hexagon_globalreg.c
+++ b/hw/hexagon/hexagon_globalreg.c
@@ -18,6 +18,7 @@
 #include "target/hexagon/cpu.h"
 #include "target/hexagon/hex_regs.h"
 #include "qemu/log.h"
+#include "qemu/timer.h"
 #include "trace.h"
 #include "qapi/error.h"
 
@@ -157,6 +158,85 @@ static inline bool is_timer_reg(uint32_t reg)
     return reg == HEX_SREG_TIMERLO || reg == HEX_SREG_TIMERHI;
 }
 
+static inline bool is_pcycle_reg(uint32_t reg)
+{
+    return reg == HEX_SREG_PCYCLELO || reg == HEX_SREG_PCYCLEHI;
+}
+
+static inline bool modectl_any_thread_running(uint32_t modectl)
+{
+    uint32_t enabled = modectl & 0xffff;
+    uint32_t waiting = modectl >> 16;
+    return (enabled & ~waiting) != 0;
+}
+
+static uint64_t pcycle_value_now(HexagonGlobalRegState *s)
+{
+    uint64_t cycles = s->g_pcycle_base;
+
+    if (s->pcycle_running) {
+        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        int64_t elapsed = now - s->pcycle_start_ns;
+
+        if (elapsed > 0) {
+            cycles += elapsed;
+        }
+    }
+    return cycles;
+}
+
+static void pcycle_fold(HexagonGlobalRegState *s)
+{
+    int64_t now;
+    int64_t elapsed;
+
+    if (!s->pcycle_running) {
+        return;
+    }
+
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    elapsed = now - s->pcycle_start_ns;
+    if (elapsed > 0) {
+        s->g_pcycle_base += elapsed;
+    }
+    s->pcycle_start_ns = now;
+}
+
+static bool pcycle_enabled(uint32_t syscfg)
+{
+    return extract32(syscfg, reg_field_info[SYSCFG_PCYCLEEN].offset,
+                     reg_field_info[SYSCFG_PCYCLEEN].width);
+}
+
+uint64_t hexagon_globalreg_read_pcycle(HexagonGlobalRegState *s)
+{
+    g_assert(s);
+    return pcycle_enabled(s->regs[HEX_SREG_SYSCFG]) ?
+           pcycle_value_now(s) : 0;
+}
+static void pcycle_set_running(HexagonGlobalRegState *s, bool running)
+{
+    if (running == s->pcycle_running) {
+        return;
+    }
+    if (running) {
+        s->pcycle_start_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    } else {
+        s->g_pcycle_base = pcycle_value_now(s);
+    }
+    s->pcycle_running = running;
+    trace_hexagon_pcycle_run_state(s->pcycle_running, s->g_pcycle_base);
+}
+
+static void pcycle_write(HexagonGlobalRegState *s, uint32_t reg,
+                         uint32_t value)
+{
+    pcycle_fold(s);
+    s->g_pcycle_base = deposit64(s->g_pcycle_base,
+                                 reg == HEX_SREG_PCYCLELO ? 0 : 32, 32,
+                                 value);
+}
+
 static uint32_t get_reg_value(HexagonGlobalRegState *s, uint32_t reg)
 {
     if (is_vid_reg(reg)) {
@@ -167,16 +247,38 @@ static uint32_t get_reg_value(HexagonGlobalRegState *s, 
uint32_t reg)
                 qct_qtimer_get_timer_lo(s->qtimer) :
                 qct_qtimer_get_timer_hi(s->qtimer);
     }
+    if (is_pcycle_reg(reg)) {
+        uint64_t value = hexagon_globalreg_read_pcycle(s);
+        return reg == HEX_SREG_PCYCLELO ?
+                (uint32_t)value : (uint32_t)(value >> 32);
+    }
     return s->regs[reg];
 }
 
 static void set_reg_value(HexagonGlobalRegState *s, uint32_t reg,
                           uint32_t value)
 {
+    bool pcycle_enable = reg == HEX_SREG_SYSCFG &&
+        !pcycle_enabled(s->regs[reg]) && pcycle_enabled(value);
+
+    if (is_pcycle_reg(reg)) {
+        pcycle_write(s, reg, value);
+        return;
+    }
+
     s->regs[reg] = value;
+    if (pcycle_enable) {
+        s->g_pcycle_base = 0;
+        if (s->pcycle_running) {
+            s->pcycle_start_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        }
+    }
     if (is_vid_reg(reg)) {
         l2vic_update_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1, value);
     }
+    if (reg == HEX_SREG_MODECTL) {
+        pcycle_set_running(s, modectl_any_thread_running(value));
+    }
 }
 
 uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg,
@@ -256,6 +358,7 @@ static void 
do_hexagon_globalreg_reset(HexagonGlobalRegState *s)
     memset(s->regs, 0, sizeof(s->regs));
 
     s->g_pcycle_base = 0;
+    s->pcycle_running = false;
 
     s->regs[HEX_SREG_EVB] = s->boot_evb;
     s->regs[HEX_SREG_CFGBASE] = HEXAGON_CFG_ADDR_BASE(s->config_table_addr);
@@ -275,6 +378,8 @@ static void 
do_hexagon_globalreg_reset(HexagonGlobalRegState *s)
     }
     s->regs[HEX_SREG_ISDBEN] = isdben_val;
     s->regs[HEX_SREG_MODECTL] = 0x1;
+    pcycle_set_running(s,
+                       modectl_any_thread_running(s->regs[HEX_SREG_MODECTL]));
 
     /*
      * These register indices are placeholders in these arrays
@@ -316,10 +421,34 @@ static void hexagon_globalreg_realize(DeviceState *dev, 
Error **errp)
     }
 }
 
+static int hexagon_globalreg_pre_save(void *opaque)
+{
+    HexagonGlobalRegState *s = opaque;
+
+    /* Migrate a stable value, not a timestamp from the source clock. */
+    pcycle_fold(s);
+    return 0;
+}
+
+static int hexagon_globalreg_post_load(void *opaque,
+                                       int version_id G_GNUC_UNUSED)
+{
+    HexagonGlobalRegState *s = opaque;
+
+    s->pcycle_running = modectl_any_thread_running(
+        s->regs[HEX_SREG_MODECTL]);
+    if (s->pcycle_running) {
+        s->pcycle_start_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    }
+    return 0;
+}
+
 static const VMStateDescription vmstate_hexagon_globalreg = {
     .name = "hexagon_globalreg",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = hexagon_globalreg_pre_save,
+    .post_load = hexagon_globalreg_post_load,
     .fields = (const VMStateField[]){
         VMSTATE_UINT32_ARRAY(regs, HexagonGlobalRegState, NUM_SREGS),
         VMSTATE_UINT64(g_pcycle_base, HexagonGlobalRegState),
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 1bdc3d8aed6..796bb6df9db 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -861,15 +861,20 @@ uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t 
reg)
 {
     uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
     int ssr_ce = GET_SSR_FIELD(SSR_CE, ssr);
+    HexagonCPU *cpu = env_archcpu(env);
 
     if (reg <= HEX_GREG_G3) {
         return env->greg[reg];
     }
     switch (reg) {
     case HEX_GREG_GPCYCLELO:
-        return ssr_ce ? hexagon_get_sys_pcycle_count_low(env) : 0;
+        return ssr_ce ?
+            hexagon_globalreg_read(cpu->globalregs, HEX_SREG_PCYCLELO,
+                                   env->threadId) : 0;
     case HEX_GREG_GPCYCLEHI:
-        return ssr_ce ? hexagon_get_sys_pcycle_count_high(env) : 0;
+        return ssr_ce ?
+            hexagon_globalreg_read(cpu->globalregs, HEX_SREG_PCYCLEHI,
+                                   env->threadId) : 0;
     default:
         return env->greg[reg];
     }
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
index 90197c6e72e..e19b9c359b3 100644
--- a/target/hexagon/genptr.c
+++ b/target/hexagon/genptr.c
@@ -443,6 +443,12 @@ static inline void gen_read_ctrl_reg(DisasContext *ctx, 
const int reg_num,
     } else if (reg_num == HEX_REG_UTIMERHI) {
         gen_helper_sreg_read(dest, tcg_env,
                              tcg_constant_i32(HEX_SREG_TIMERHI));
+    } else if (reg_num == HEX_REG_UPCYCLELO) {
+        gen_helper_upcycle_read(dest, tcg_env,
+                                tcg_constant_i32(HEX_SREG_PCYCLELO));
+    } else if (reg_num == HEX_REG_UPCYCLEHI) {
+        gen_helper_upcycle_read(dest, tcg_env,
+                                tcg_constant_i32(HEX_SREG_PCYCLEHI));
 #else
     } else if (reg_num == HEX_REG_UTIMERLO) {
         TCGv_i64 utimer = tcg_temp_new_i64();
@@ -488,6 +494,8 @@ static inline void gen_read_ctrl_reg_pair(DisasContext 
*ctx, const int reg_num,
         gen_helper_sreg_read(lo, tcg_env, tcg_constant_i32(HEX_SREG_TIMERLO));
         gen_helper_sreg_read(hi, tcg_env, tcg_constant_i32(HEX_SREG_TIMERHI));
         tcg_gen_concat_i32_i64(dest, lo, hi);
+    } else if (reg_num == HEX_REG_UPCYCLELO) {
+        gen_helper_upcycle_read_pair(dest, tcg_env);
 #else
     } else if (reg_num == HEX_REG_UTIMERLO) {
         /* One helper call, so the pair is a coherent 64-bit snapshot. */
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
index ef1d558dfc4..e7362b56650 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -692,8 +692,14 @@ static void sim_handle_trap0(CPUHexagonState *env)
 
     case HEX_SYS_READ_PCYCLES:
     {
-        env->gpr[HEX_REG_R00] = hexagon_get_sys_pcycle_count_low(env);
-        env->gpr[HEX_REG_R01] = hexagon_get_sys_pcycle_count_high(env);
+        HexagonCPU *cpu = env_archcpu(env);
+        uint64_t pcycles;
+
+        BQL_LOCK_GUARD();
+        pcycles = cpu->globalregs ?
+            hexagon_globalreg_read_pcycle(cpu->globalregs) : 0;
+        env->gpr[HEX_REG_R00] = (uint32_t)pcycles;
+        env->gpr[HEX_REG_R01] = (uint32_t)(pcycles >> 32);
         break;
     }
 
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index accadd7927a..8c292854de3 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1943,27 +1943,72 @@ uint32_t HELPER(sreg_read)(CPUHexagonState *env, 
uint32_t reg)
 
 uint64_t HELPER(sreg_read_pair)(CPUHexagonState *env, uint32_t reg)
 {
+    HexagonCPU *cpu = env_archcpu(env);
+
     BQL_LOCK_GUARD();
 
+    if (reg == HEX_SREG_PCYCLELO && cpu->globalregs) {
+        return hexagon_globalreg_read_pcycle(cpu->globalregs);
+    }
     return deposit64((uint64_t) sreg_read(env, reg), 32, 32,
         sreg_read(env, reg + 1));
 }
 
+/*
+ * UPCYCLELO/UPCYCLEHI are the user-visible alias of PCYCLELO/PCYCLEHI,
+ * readable without privilege but only once SSR[CE] is set.
+ */
+uint32_t HELPER(upcycle_read)(CPUHexagonState *env, uint32_t sreg)
+{
+    uint32_t ssr;
+
+    BQL_LOCK_GUARD();
+    ssr = env->t_sreg[HEX_SREG_SSR];
+    if (!GET_SSR_FIELD(SSR_CE, ssr)) {
+        return 0;
+    }
+    return sreg_read(env, sreg);
+}
+
+uint64_t HELPER(upcycle_read_pair)(CPUHexagonState *env)
+{
+    HexagonCPU *cpu = env_archcpu(env);
+    uint32_t ssr;
+
+    BQL_LOCK_GUARD();
+    ssr = env->t_sreg[HEX_SREG_SSR];
+    if (!GET_SSR_FIELD(SSR_CE, ssr)) {
+        return 0;
+    }
+    return cpu->globalregs ?
+           hexagon_globalreg_read_pcycle(cpu->globalregs) : 0;
+}
+
 uint32_t HELPER(greg_read)(CPUHexagonState *env, uint32_t reg)
 
 {
+    BQL_LOCK_GUARD();
     return hexagon_greg_read(env, reg);
 }
 
 uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg)
 
 {
+    HexagonCPU *cpu = env_archcpu(env);
+
+    BQL_LOCK_GUARD();
     g_assert((reg & 1) == 0);
 
     if (reg == HEX_GREG_G0 || reg == HEX_GREG_G2) {
         return (uint64_t)(env->greg[reg]) |
                (((uint64_t)(env->greg[reg + 1])) << 32);
     }
+    if (reg == HEX_GREG_GPCYCLELO) {
+        uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
+
+        return GET_SSR_FIELD(SSR_CE, ssr) && cpu->globalregs ?
+               hexagon_globalreg_read_pcycle(cpu->globalregs) : 0;
+    }
     return (uint64_t)hexagon_greg_read(env, reg) |
            ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
 }
diff --git a/hw/hexagon/trace-events b/hw/hexagon/trace-events
index 5d623ed2516..8c906a30ebd 100644
--- a/hw/hexagon/trace-events
+++ b/hw/hexagon/trace-events
@@ -1,3 +1,4 @@
 # Hexagon global register access
 hexagon_globalreg_read(uint32_t htid, const char *reg_name, uint32_t value) 
"htid=%u reg=%s value=0x%x"
 hexagon_globalreg_write(uint32_t htid, const char *reg_name, uint32_t value) 
"htid=%u reg=%s value=0x%x"
+hexagon_pcycle_run_state(bool running, uint64_t base) "running=%u base=%" 
PRIu64
-- 
2.34.1

Reply via email to