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