Smcntrpmf adds one inhibit bit per privilege mode to cycle and
instret, so a counter can be made to tick only in the mode you are
actually measuring.
Filtering is also what keeps a user-readable cycle counter safe to
expose: with MINH set, privileged execution becomes invisible rather
than merely uninteresting, and the fast unprivileged read stays
allowable.
By default cycle and instret tick straight through page faults and
interrupts — so a user program self-profiling with two counter reads
sees kernel noise, and worse, measures privileged execution (a timing
side channel). Smcntrpmf smcntrpmf Privilege-mode filtering for cycle/instret via mcyclecfg (0x321) / minstretcfg (0x322): MINH/SINH/UINH/VSINH/VUINH inhibit counting per mode (Sscofpmf register format, OF bit dead). instret: retire-mode gated; xRET counts by originating mode - so a faulting user instruction counts exactly once. Removes kernel noise and a timing side channel from user self-profiling.
defined in ch. II·7 — open in glossary
adds per-privilege
inhibit bits.
mcyclecfg (0x321) / minstretcfg (0x322)
(Address trivia the spec admits: 0x320 would’ve been natural, but
mcountinhibit got there first. RV32 gets mcyclecfgh/minstretcfgh;
Smcdeleg/Ssccfg can expose these to S-mode.)
The counting rules
| Rule | |
|---|---|
| cycle | Counts CPU cycles only in non-inhibited modes; multi-cycle trap/return transitions attribute the switchover to ANY one transition cycle, implementation-defined and not necessarily consistent |
| instret — ordinary instructions | Increment iff they retire in a non-inhibited mode |
| instret — exception-raising instructions | Never retire, never count (the u07 rule, unchanged) |
| instret — xRET | Counts iff its ORIGINATING mode is non-inhibited |
| Software emulation | An emulator running in an inhibited mode must emulate the increment for the emulated instruction |
One faulting load, counted once
Take the configuration a profiler actually wants — count U-mode only,
so MINH = SINH = 1 and UINH = 0 — and trace a single user load that
takes a page fault:
- The load raises the fault, so it never retires:
instretdoes not move (the u07 rule, unchanged). - The trap enters S-mode, which is inhibited, so nothing the handler retires counts.
- The handler’s
SREToriginates in S-mode — inhibited — so the return does not count either. - The load re-executes in U-mode and retires:
instret+= 1.
One user instruction, one increment. The xRET rule is what makes that arithmetic exact: had the return counted in its destination mode instead, every serviced fault would have inflated the user’s instruction count by one.
- Inhibition is per mode, per counter:
mcyclecfgandminstretcfgeach carry MINH/SINH/UINH (plus VSINH/VUINH with H), and all-zero is the legacy count-everywhere behavior. - Instructions count where they retire — except xRET, which counts where it originated. That one exception is what keeps mode-filtered totals exact across traps.
- Cycle attribution at trap boundaries is implementation-defined,
so a filtered
cyclecount is statistically sound but never cycle-exact — nothing in the ISA fixes trap latency.
Hardware Designer Notes
Cost: two config registers and two AND gates on existing increment wires; the only subtlety is plumbing the xRET originating mode to the commit-stage counter logic — which your trap unit already knows. Worthwhile the moment your users run perf.
Minimal Linux-boot hart MUST
- Gate the cycle increment with !xINH[current mode] and the instret increment with !xINH[retire mode] — except xRET, which uses its pre-return (originating) mode
- Read-only-zero the INH bits of unimplemented modes and the OF position
MAY simplify / trap-and-emulate
- Skip the extension (both cfg CSRs absent) — legacy counting is still conformant
- Attribute transition cycles however your trap microsequence falls — no consistency required
Check yourself — counter filtering
1.minstretcfg inhibits every mode except U. A user load page-faults, the kernel fixes it, the load re-executes. How many instret increments?
2.During a trap's multi-cycle mode transition, which mode do the transition cycles count toward?
3.Why do mcyclecfg/minstretcfg mirror the Sscofpmf event-register format with bit 63 read-only zero?