Automatically finds the useful Curve Optimizer (undervolt) limit of an AMD Ryzen laptop by measuring real performance — not just waiting for crashes.
Built for and tested on the ASUS ROG Zephyrus G14 2025 (Ryzen AI 9 HX 370 "Strix Point"). The tool hard-gates on the Strix Point family; porting to other Ryzen families means extending the SMU command table.
CLI output is currently in Spanish. Code and identifiers are in English.
Recent Ryzen chips protect themselves from undervolt-induced crashes with clock stretching: instead of crashing, the silicon silently reduces effective frequency. The consequence: "it doesn't crash at -80mV" tells you nothing — your chip may well be slower at -80 than at -20.
So this tool defines the limit by diminishing returns on measured performance: it benchmarks every offset under sustained thermally-limited load and stops where the gains flatten out (or where computation errors / WHEA events appear).
| Offset | Median Pi 2.5b | Cumulative gain vs stock |
|---|---|---|
| 0 | 84.04 s | — |
| -5 | 81.00 s | +3.6% |
| -10 | 80.80 s | +3.9% |
| -15 | 79.08 s | +5.9% |
| -20 | 76.94 s | +8.4% |
| -25 | FAILED | benchmark died mid-run |
At -25 the chip fell into severe protective throttling (35 W / ~3.6 GHz for a minute) and the next run died without output — no crash, no BSOD. A crash-based tuner would have kept going deeper.
- Control layer — talks to the Ryzen SMU through the signed PawnIO driver (Secure Boot friendly; no WinRing0). SMU mailbox code adapted from G-Helper (GPL-3.0). Offsets are encoded as 20-bit two's complement (RyzenAdj
--set-coallconvention) and sent via MP1 command0x4C(Strix Point). Offsets are volatile by design: any reboot returns to stock — that's the safety net. - Measurement layer — y-cruncher Pi benchmark as a deterministic workload: computation time is the metric and y-cruncher validates its own math (a spot-check failure = unstable offset, no crash needed). Telemetry via LibreHardwareMonitorLib; WHEA corrected-error monitoring (event ID 19) via the Windows event log.
- Orchestration layer — state machine persisted to
state.jsonbefore each offset is applied; a Scheduled Task finalizes the verdict after a mid-test reboot (pattern borrowed from CoreCycler). Thermal guard (≥97 °C sustained 10 s, or ≥100 °C instant) aborts any run. Curve Optimizer is restored to 0 on every exit path.
The benchmark is thermally limited, so chassis temperature is a confounder:
- Adaptive soak: the first step runs up to 4 discarded warmups until two consecutive times differ <1% (a cold chassis inflates the baseline ~3%, masking the undervolt's gain).
- Median of 3 runs per offset (measured run-to-run CV: ~2.3% when hot).
- Plateau needs confirmation: the sweep stops only after 2 consecutive steps without a ≥2% improvement over the best offset so far.
- Per-core validation: the winning offset is stress-validated core-by-core (1 thread pinned per physical core, where boost is highest and undervolts fail first), with per-run spot checks and continuous WHEA monitoring.
autotuner probe Read-only SMU sanity check (PawnIO + mailbox)
autotuner set --offset <N> Apply Curve Optimizer all-core (N in [-40, 0], volatile)
autotuner reset Restore Curve Optimizer to 0
autotuner bench [--size <S>] One instrumented benchmark (telemetry + WHEA)
autotuner sweep [--floor -40] Full sweep 0 → floor with plateau detection
autotuner validate --offset <N> Long per-core validation of a candidate offset
autotuner resume Post-reboot recovery (invoked by the Scheduled Task)
Typical session:
# From an elevated terminal, on AC power:
autotuner probe # verify the SMU chain
autotuner sweep # ~35-45 min: finds the useful limit
autotuner validate --offset <winner> --minutes 60 # overnight-friendlyThen persist the validated value with G-Helper (Fans + Power → Advanced → CPU undervolt), which re-applies it on every boot. This tool deliberately does not persist anything.
- ASUS Zephyrus G14 2025 / Ryzen AI 9 HX 370 (other Strix Point machines will probably work; everything else is rejected)
- Windows 11, elevated terminal, AC power
- PawnIO driver installed
- .NET 8 SDK
- y-cruncher binaries in
tools/y-cruncher/— copy them from CoreCycler'stest_programs/y-cruncher/(not redistributed here) - Heads-up: Windows Smart App Control blocks locally-built unsigned binaries; developing/running this requires it off
- Preflight: elevation, AC power, PawnIO reachable, CPU allowlist.
- Thermal guard: abort at ≥97 °C sustained (10 s) or ≥100 °C (Tjmax) instant.
- Hard offset floor: -40. The tool never goes below it.
- Curve Optimizer restored to 0 on exit — normal, Ctrl+C, exception, or reboot (volatility + recovery task).
- Every run logs to
logs/— after a crash, the log is the evidence.
Use at your own risk. Undervolting is out-of-spec operation. The reboot-resets-everything design keeps the blast radius small, but you are the one pressing enter.
- G-Helper (seerge) — PawnIO wrapper and Ryzen SMU mailbox code, adapted under GPL-3.0
- CoreCycler (sp00n) — per-core cycling and crash-recovery patterns, y-cruncher packaging
- y-cruncher (Alexander J. Yee) — the benchmark/validator workload
- RyzenAdj — SMU command documentation
- LibreHardwareMonitor — telemetry
GPL-3.0 — inherited from the adapted G-Helper code, and gladly so.