target/riscv/cpu.h | 2 ++
target/riscv/tcg/cpu_helper.c | 6 ++++++
target/riscv/tcg/csr.c | 25 +++++++++++++++++++++++++
target/riscv/tcg/pmu.c | 27 +++++++++++++++++++++++++++
4 files changed, 60 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index fe56a6afd1..36425c3605 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -473,6 +473,8 @@ struct CPUArchState {
/* Sspesa CSRs */
target_ulong shpmspc;
target_ulong shpmsdata;
+ /* Capture shpmspc/shpmsdata only on the first overflow */
+ bool sspesa_capture_pending;
/* Sstc CSRs */
uint64_t stimecmp;
diff --git a/target/riscv/tcg/cpu_helper.c b/target/riscv/tcg/cpu_helper.c
index 07d9222652..15af3262ed 100644
--- a/target/riscv/tcg/cpu_helper.c
+++ b/target/riscv/tcg/cpu_helper.c
@@ -2255,6 +2255,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
sxlen = 16 << riscv_cpu_sxl(env);
env->scause = cause | ((target_ulong)async << (sxlen - 1));
env->sepc = env->pc;
+ /* On LCOFI delivery, latch the sample PC to the interrupted PC. */
+ if (riscv_cpu_cfg(env)->ext_sspesa && env->sspesa_capture_pending) {
+ if (async && cause == IRQ_PMU_OVF) {
+ env->shpmspc = env->pc;
+ }
+ }
env->stval = tval;
env->htval = htval;
env->htinst = tinst;
diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
index ea49d28aae..e48c954359 100644
--- a/target/riscv/tcg/csr.c
+++ b/target/riscv/tcg/csr.c
@@ -1228,6 +1228,15 @@ static RISCVException write_minstretcfgh(CPURISCVState
*env, int csrno,
return RISCV_EXCP_NONE;
}
+static bool sspesa_is_captured_ctr(CPURISCVState *env, uint32_t evt_index)
+{
+ if (!riscv_cpu_cfg(env)->ext_sspesa || !env->sspesa_capture_pending) {
+ return false;
+ }
+
+ return (env->shpmsdata & SHPMSDATA_CNTRID) == evt_index;
+}
+
static RISCVException read_mhpmevent(CPURISCVState *env, int csrno,
target_ulong *val)
{
@@ -1259,6 +1268,14 @@ static RISCVException write_mhpmevent(CPURISCVState
*env, int csrno,
mhpmevt_val = val & inh_avail_mask;
}
+ /* Enable the sample capture when OF is cleared for the captured counter. */
+ if (sspesa_is_captured_ctr(env, evt_index)) {
+ if ((env->mhpmevent_val[evt_index] & MHPMEVENT_BIT_OF) &&
+ !(mhpmevt_val & MHPMEVENT_BIT_OF)) {
+ env->sspesa_capture_pending = false;
+ }
+ }
+
env->mhpmevent_val[evt_index] = mhpmevt_val;
riscv_pmu_update_event_map(env, mhpmevt_val, evt_index);
@@ -1289,6 +1306,14 @@ static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno,
inh_avail_mask |= (riscv_has_ext(env, RVH) &&
riscv_has_ext(env, RVS)) ? MHPMEVENTH_BIT_VSINH : 0;
+ /* Enable the sample capture when OF is cleared for the captured counter. */
+ if (sspesa_is_captured_ctr(env, evt_index)) {
+ if ((env->mhpmevent_val[evt_index] & MHPMEVENT_BIT_OF) &&
+ !((val & inh_avail_mask) & MHPMEVENTH_BIT_OF)) {
+ env->sspesa_capture_pending = false;
+ }
+ }
+
env->mhpmevent_val[evt_index] = deposit64(env->mhpmevent_val[evt_index],
32, 32, val & inh_avail_mask);
diff --git a/target/riscv/tcg/pmu.c b/target/riscv/tcg/pmu.c
index 38ad2737e1..337e255da4 100644
--- a/target/riscv/tcg/pmu.c
+++ b/target/riscv/tcg/pmu.c
@@ -147,6 +147,31 @@ void riscv_pmu_update_fixed_ctrs(CPURISCVState *env,
riscv_pmu_icount_update_priv(env, newpriv, new_virt);
}
+static void riscv_pmu_sspesa_capture(CPURISCVState *env, uint32_t ctr_idx)
+{
+ RISCVCPU *cpu = env_archcpu(env);
+
+ if (!cpu->cfg.ext_sspesa) {
+ return;
+ }
+
+ /*
+ * Only the first overflow updates the sample CSRs. While a capture is
+ * pending, keep the lowest counter ID as required by the spec.
+ */
+ if (env->sspesa_capture_pending) {
+ if ((ctr_idx & SHPMSDATA_CNTRID) <
+ (env->shpmsdata & SHPMSDATA_CNTRID)) {
+ env->shpmsdata = ctr_idx & SHPMSDATA_CNTRID;
+ }
+ return;
+ }
+
+ /* shpmspc is latched later, on LCOFI delivery. */
+ env->shpmsdata = ctr_idx & SHPMSDATA_CNTRID;
+ env->sspesa_capture_pending = true;
+}
+
int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx)
{
uint32_t ctr_idx;
@@ -191,6 +216,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum
riscv_pmu_event_idx event_idx)
/* Generate interrupt only if OF bit is clear */
if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) {
env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF;
+ riscv_pmu_sspesa_capture(env, ctr_idx);
riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1));
}
} else {
@@ -383,6 +409,7 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu,
if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) {
if (pmu_hpmevent_set_of_if_clear(env, ctr_idx)) {
+ riscv_pmu_sspesa_capture(env, ctr_idx);
riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1));
}
}