Re: [RFC PATCH 2/2] target/riscv: Capture Sspesa sample PC and counter ID on overflow

Daniel Henrique Barboza <[email protected]>
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-devel
Message-ID <[email protected]>

On 8/17/2026 1:03 PM, Mayuresh Chitale wrote:
> On a Zihpm counter overflow and with LCOFIP asserted, record the
> overflowing counter ID in shpmsdata and arm a capture. When the LCOFI is
> actually delivered, the shpmspc register is populated with the address of
> the interrupted instruction.
> Capturing shpmspc at LCOFI delivery (rather than at overflow time)
> ensures the sample PC reflects the correct interrupted instruction.
> 
> Signed-off-by: Mayuresh Chitale <[email protected]>
> ---

Reviewed-by: Daniel Henrique Barboza <[email protected]>

>   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));
>           }
>       }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.