Full instruction tracing drowns in its own bandwidth; CTR control transfer records Smctr/Ssctr: a circular on-chip buffer (depth 16-256) recording qualified control transfers (source PC, target PC, type, mispredict, compressed cycle count) for low-overhead profiling. Accessed via siselect 0x200+N (logical entry 0 = youngest); configured by m/s/vsctrctl (mode enables, type filters, RASEMU call-stack mode, freeze-on-LCOFI/breakpoint); SCTRCLR wipes it. defined in ch. II·11 — open in glossary instead keeps the last 16–256 control transfers in on-chip storage — enough for Linux perf’s LBR-style sampling and AutoFDO, at near-zero overhead. It’s a circular FIFO of (source PC, target PC, metadata) records behind the indirect-CSR window; Smctr/Ssctr require S-mode and Sscsrind.
The control register
mctrctl (0x34E) — Figure 10
sctrctl (0x14E) is the same register minus M/MTE (read-only 0 there);
vsctrctl substitutes when V=1 — a guest manages its own filters
without traps, while its enables can’t affect V=0 recording.
Depth, status, entries
| Contents & rules | |
|---|---|
| sctrdepth (0x15F) | DEPTH[2:0] WARL: 16/32/64/128/256 entries (support is implementation-specific). VS access ALWAYS raises virtual-instruction |
| sctrstatus (0x14F) | WRPTR[7:0] (next physical write slot; wraps at depth; excess bits RO-0) + FROZEN[31] |
| Entries via siselect 0x200-0x2FF | Logical entry N (0 = youngest): sireg = ctrsource (PC[63:1] + V valid bit), sireg2 = ctrtarget (PC[63:1] + MISP mispredict bit; not-taken branch records the fall-through PC), sireg3 = ctrdata (TYPE[3:0], CCV[15], CC[31:16] compressed cycle count). sireg4-6 read zero; entries ≥ depth read zero. All MXLEN-wide (immune to XLEN switching truncation) |
| SCTRCLR (SYSTEM, func12 0x104) | Zeroes ALL entries across every depth, plus the cycle counter and CCV. Illegal-instruction in U, virtual-instruction in VU — the context-isolation wipe |
Access control
mstateen0.CTR (bit 54) gates every CTR CSR, the entry window, and
SCTRCLR below M-mode (illegal-instruction); hstateen0.CTR does the
same for VS (virtual-instruction). Crucially, recording continues
regardless — the stateen bits guard the register interface, not the
recorder, enabling the lazy-virtualization pattern: trap the guest’s
first CTR touch, then start context-switching CTR state for that guest
only.
Hardware Designer Notes
The storage bill: depth × (2 × MXLEN + 32) bits of SRAM plus an 8-bit WRPTR — a 16-entry RV64 buffer is ~264 bytes. The write port sits at retirement where source/target/type are already known. The behavioral rules that make records trustworthy are the next page.
Minimal Linux-boot hart MUST
- Implement M/S/U and BPFRZ fields in the control registers (LCOFIFRZ with Sscofpmf); everything else may be RO-0
- Keep entry registers MXLEN-wide with zero-extension of narrow writes
- Make SCTRCLR wipe all physical entries, not just those below the current depth
MAY simplify / trap-and-emulate
- Support a single depth value (e.g. 16) — DEPTH is WARL
- Omit MISP, cycle counting, all filters, and RASEMU — each is independently optional
- Skip CTR entirely on a Linux-boot v1: it needs Sscsrind anyway and only perf tooling misses it
Check yourself — CTR registers
1.How does software read the youngest recorded transfer?
2.Why does VS-mode access to sctrdepth ALWAYS raise virtual-instruction, even with all stateen bits set?
3.With mstateen0.CTR=0, S-mode runs with recording enabled. What still happens?
4.What does SCTRCLR guarantee?