DF-4892 / rpm_wrap_demo.c
/* DF-4892 — arithmetic demo of the smu7 fan-RPM 32-bit wrap * (DF-4761 vega10_thermal twin). * * sys/dev/drm/amd/powerplay/hwmgr/smu7_thermal.c:100 * *speed = 60 * crystal_clock_freq * 10000 / tach_period; * and :273 * tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); * * crystal_clock_freq = amdgpu_asic_get_xclk() = vi_get_xclk() = * adev->clock.spll.reference_freq (25/27 MHz class, kHz units) on * Tonga/Fiji/Polaris dGPUs (vi.c:326-349). 60*25000*10000 = 1.5e10 wraps * u32 arithmetic before the division. This userspace program evaluates the * exact source expressions in both u32 and u64 to document the wrap; it is * an arithmetic demo only (no SMU7 hardware required to see the divergence). */ #include <stdio.h> #include <stdint.h> int main(void) { uint32_t xclk[] = { 25000, 27000, 25000 / 4 }; uint32_t tach_period[] = { 1, 100, 1000, 100000, 2000000 }; unsigned i, j; puts("DF-4892 smu7 fan RPM math: 60 * xclk * 10000 (u32 vs u64)"); for (i = 0; i < sizeof(xclk)/sizeof(xclk[0]); i++) { uint32_t prod32 = 60 * xclk[i] * 10000; /* kernel expr */ uint64_t prod64 = (uint64_t)60 * xclk[i] * 10000; printf("xclk=%-6u u32: %-12u u64: %-14llu wrap: %s\n", xclk[i], prod32, (unsigned long long)prod64, prod32 != prod64 ? "YES" : "no"); } puts("\nget_fan_speed_rpm (:100): reported RPM for real tach periods"); for (j = 0; j < sizeof(tach_period)/sizeof(tach_period[0]); j++) { uint32_t r32 = 60 * 25000 * 10000 / tach_period[j]; uint64_t r64 = (uint64_t)60 * 25000 * 10000 / tach_period[j]; printf("tach_period=%-8u kernel(u32)=%-12u correct(u64)=%-12llu error=%lldx\n", tach_period[j], r32, (unsigned long long)r64, r64 ? (long long)(r64 - r32) : 0); } puts("\nset_fan_speed_rpm (:273): programmed TARGET_PERIOD for requests"); { uint32_t req[] = { 800, 1500, 2200, 3000 }; for (j = 0; j < sizeof(req)/sizeof(req[0]); j++) { uint32_t p32 = 60 * 25000 * 10000 / (8 * req[j]); uint64_t p64 = (uint64_t)60 * 25000 * 10000 / (8 * req[j]); printf("rpm=%-6u kernel(u32)=%-12u correct(u64)=%-12llu -> fan target off by %.2fx\n", req[j], p32, (unsigned long long)p64, p32 ? (double)p64 / (double)p32 : 0.0); } } return 0; } |