From bc4a203fcfa6df5501bffb9231a85689534e7828 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Apr 2019 14:51:35 -0700 Subject: [PATCH 1/2] Power: Correct hz on update and improve resched. It only reschedules when the PLL changes, which changes in steps. This also reads back much more accurate Mhz for each of PLL, CPU, and bus. --- Core/CoreTiming.cpp | 33 ++++--- Core/CoreTiming.h | 6 +- Core/HLE/sceDisplay.cpp | 4 +- Core/HLE/scePower.cpp | 195 ++++++++++++++++++++++++++-------------- 4 files changed, 147 insertions(+), 91 deletions(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 0e80ddc3a7..9d9123139f 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -92,43 +92,40 @@ static std::mutex externalEventLock; std::vector mhzChangeCallbacks; void FireMhzChange() { - for (auto it = mhzChangeCallbacks.begin(), end = mhzChangeCallbacks.end(); it != end; ++it) { - MHzChangeCallback cb = *it; + for (MHzChangeCallback cb : mhzChangeCallbacks) { cb(); } } -void SetClockFrequencyMHz(int cpuMhz) -{ +void SetClockFrequencyHz(int cpuHz) { // When the mhz changes, we keep track of what "time" it was before hand. // This way, time always moves forward, even if mhz is changed. lastGlobalTimeUs = GetGlobalTimeUs(); lastGlobalTimeTicks = GetTicks(); - CPU_HZ = cpuMhz * 1000000; + CPU_HZ = cpuHz; // TODO: Rescale times of scheduled events? FireMhzChange(); } -int GetClockFrequencyMHz() -{ - return CPU_HZ / 1000000; +int GetClockFrequencyHz() { + return CPU_HZ; } -u64 GetGlobalTimeUsScaled() -{ - s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks; - int freq = GetClockFrequencyMHz(); - s64 usSinceLast = ticksSinceLast / freq; - return lastGlobalTimeUs + usSinceLast; +u64 GetGlobalTimeUsScaled() { + return GetGlobalTimeUs(); } -u64 GetGlobalTimeUs() -{ +u64 GetGlobalTimeUs() { s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks; - int freq = GetClockFrequencyMHz(); - s64 usSinceLast = ticksSinceLast / freq; + int freq = GetClockFrequencyHz(); + s64 usSinceLast = ticksSinceLast * 1000000 / freq; + if (ticksSinceLast > UINT_MAX) { + // Adjust the calculated value to avoid overflow errors. + lastGlobalTimeUs += usSinceLast; + lastGlobalTimeTicks = GetTicks(); + } return lastGlobalTimeUs + usSinceLast; } diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index ee7a26a714..c72ff2ba44 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -67,7 +67,7 @@ inline s64 usToCycles(u64 us) { } inline s64 cyclesToUs(s64 cycles) { - return cycles / (CPU_HZ / 1000000); + return (cycles * CPU_HZ) / 1000000; } namespace CoreTiming @@ -121,8 +121,8 @@ namespace CoreTiming void DoState(PointerWrap &p); - void SetClockFrequencyMHz(int cpuMhz); - int GetClockFrequencyMHz(); + void SetClockFrequencyHz(int cpuHz); + int GetClockFrequencyHz(); extern int slicelength; }; // end of namespace diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 57f510f278..d1a2de1110 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -1069,10 +1069,10 @@ static u32 sceDisplayGetVcount() { } static u32 __DisplayGetCurrentHcount() { - const static int ticksPerVblank333 = 333 * 1000000 / 60 / hCountPerVblank; const int ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks; + const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank; // Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right. - return 1 + (ticksIntoFrame / (CoreTiming::GetClockFrequencyMHz() * ticksPerVblank333 / 333)); + return 1 + (ticksIntoFrame / ticksPerVblank); } static u32 __DisplayGetAccumulatedHcount() { diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 8204a51c58..35ff5c4f0e 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -57,10 +57,57 @@ static int powerCbSlots[numberOfCBPowerSlots]; static std::vector volatileWaitingThreads; // Should this belong here, or in CoreTiming? -static int RealpllFreq = 222; -static int RealbusFreq = 111; -static int pllFreq = 222; -static int busFreq = 111; +static int RealpllFreq = 222000000; +static int RealbusFreq = 111000000; +static int pllFreq = 222000000; +static int busFreq = 111000000; + +// The CPU mhz can only be a multiple of the PLL divided by 511. +int PowerCpuMhzToHz(int desired, int pllHz) { + double maxfreq = desired * 1000000.0; + + double step = (double)pllHz / 511.0; + // These values seem to be locked. + if (pllHz >= 333000000 && desired == 333) { + return 333000000; + } else if (pllHz >= 222000000 && desired == 222) { + return 222000000; + } + + double freq = 0; + while (freq + step < maxfreq) { + freq += step; + } + + // We match the PSP's HLE funcs better when we have the same float error, it seems. + return (float)(freq / 1000000.0f) * 1000000; +} + +int PowerPllMhzToHz(int mhz) { + // These seem to be the only steps it has. + if (mhz <= 190) + return 190285721; + if (mhz <= 222) + return 222000000; + if (mhz <= 266) + return 266399994; + if (mhz <= 333) + return 333000000; + return mhz * 1000000; +} + +int PowerBusMhzToHz(int mhz) { + // These seem to be the only steps it has. + if (mhz <= 95) + return 95142860; + if (mhz <= 111) + return 111000000; + if (mhz <= 133) + return 133199997; + if (mhz <= 166) + return 166500000; + return mhz * 1000000; +} void __PowerInit() { memset(powerCbSlots, 0, sizeof(powerCbSlots)); @@ -68,15 +115,15 @@ void __PowerInit() { volatileWaitingThreads.clear(); if (g_Config.iLockedCPUSpeed > 0) { - CoreTiming::SetClockFrequencyMHz(g_Config.iLockedCPUSpeed); - pllFreq = g_Config.iLockedCPUSpeed; - busFreq = g_Config.iLockedCPUSpeed / 2; + pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed); + busFreq = PowerBusMhzToHz(pllFreq / 2000000); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq)); } else { - pllFreq = 222; - busFreq = 111; + pllFreq = PowerPllMhzToHz(222); + busFreq = PowerBusMhzToHz(111); } - RealpllFreq = 222; - RealbusFreq = 111; + RealpllFreq = PowerPllMhzToHz(222); + RealbusFreq = PowerBusMhzToHz(111); } void __PowerDoState(PointerWrap &p) { @@ -87,17 +134,20 @@ void __PowerDoState(PointerWrap &p) { if (s >= 2) { p.Do(RealpllFreq); p.Do(RealbusFreq); - } - else { - RealpllFreq = 222; - RealbusFreq = 111; + + if (RealpllFreq < 1000000) + RealpllFreq = PowerPllMhzToHz(RealpllFreq); + if (RealbusFreq < 1000000) + RealbusFreq = PowerBusMhzToHz(RealbusFreq); + } else { + RealpllFreq = PowerPllMhzToHz(222); + RealbusFreq = PowerBusMhzToHz(111); } if (g_Config.iLockedCPUSpeed > 0) { - CoreTiming::SetClockFrequencyMHz(g_Config.iLockedCPUSpeed); - pllFreq = g_Config.iLockedCPUSpeed; - busFreq = g_Config.iLockedCPUSpeed / 2; - } - else { + pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed); + busFreq = PowerBusMhzToHz(pllFreq / 2000000); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq)); + } else { pllFreq = RealpllFreq; busFreq = RealbusFreq; } @@ -372,104 +422,113 @@ static int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) { static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { + // 190 might (probably) be a typo for 19, but it's what the actual PSP validates against. + if (pllfreq < 19 || pllfreq < cpufreq || pllfreq > 333) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid pll frequency"); + } if (cpufreq == 0 || cpufreq > 333) { - WARN_LOG(HLE,"scePowerSetClockFrequency(%i,%i,%i): invalid frequency", pllfreq, cpufreq, busfreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid cpu frequency"); + } + if (busfreq == 0 || busfreq > 166) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid bus frequency"); } // TODO: More restrictions. if (g_Config.iLockedCPUSpeed > 0) { INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i): locked by user config at %i, %i, %i", pllfreq, cpufreq, busfreq, g_Config.iLockedCPUSpeed, g_Config.iLockedCPUSpeed, busFreq); - } - else { + } else { INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i)", pllfreq, cpufreq, busfreq); } - if (RealpllFreq != pllfreq || RealbusFreq != busfreq) { - RealpllFreq = pllfreq; - RealbusFreq = busfreq; + // Only reschedules when the stepped PLL frequency changes. + // It seems like the busfreq parameter has no effect (but can cause errors.) + if (RealpllFreq != PowerPllMhzToHz(pllfreq)) { + RealpllFreq = PowerPllMhzToHz(pllfreq); + RealbusFreq = PowerBusMhzToHz(RealpllFreq / 2000000); if (g_Config.iLockedCPUSpeed <= 0) { - pllFreq = pllfreq; - busFreq = busfreq; - CoreTiming::SetClockFrequencyMHz(cpufreq); + pllFreq = RealpllFreq; + busFreq = RealbusFreq; + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); } return hleDelayResult(0, "scepower set clockFrequency", 150000); } if (g_Config.iLockedCPUSpeed <= 0) - CoreTiming::SetClockFrequencyMHz(cpufreq); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); return 0; } static u32 scePowerSetCpuClockFrequency(u32 cpufreq) { - if(g_Config.iLockedCPUSpeed > 0) { - DEBUG_LOG(HLE,"scePowerSetCpuClockFrequency(%i): locked by user config at %i", cpufreq, g_Config.iLockedCPUSpeed); + if (cpufreq == 0 || cpufreq > 333) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency"); } - else { - if (cpufreq == 0 || cpufreq > 333) { - WARN_LOG(HLE,"scePowerSetCpuClockFrequency(%i): invalid frequency", cpufreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; - } - CoreTiming::SetClockFrequencyMHz(cpufreq); - INFO_LOG(HLE,"scePowerSetCpuClockFrequency(%i)", cpufreq); + if (g_Config.iLockedCPUSpeed > 0) { + return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed); } - return 0; + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); + return hleLogSuccessI(SCEMISC, 0); } static u32 scePowerSetBusClockFrequency(u32 busfreq) { - if(g_Config.iLockedCPUSpeed > 0) { - DEBUG_LOG(HLE,"scePowerSetBusClockFrequency(%i): locked by user config at %i", busfreq, busFreq); + if (busfreq == 0 || busfreq > 111) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency"); } - else { - if (busfreq == 0 || busfreq > 111) { - WARN_LOG(HLE,"scePowerSetBusClockFrequency(%i): invalid frequency", busfreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; - } - // TODO: It seems related to other frequencies, though. - busFreq = busfreq; - INFO_LOG_REPORT_ONCE(SetBusClockFrequency,HLE,"scePowerSetBusClockFrequency(%i)", busfreq); + if (g_Config.iLockedCPUSpeed > 0) { + return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed / 2); } - return 0; + + // The value passed is validated, but then doesn't seem to matter for the result. + // However, this sets a different hz than scePowerSetClockFrequency would have. + + if (pllFreq <= 190) + busFreq = 94956673; + else if (pllFreq <= 222) + busFreq = 111000000; + else if (pllFreq <= 266) + busFreq = 132939331; + else if (pllFreq <= 333) + busFreq = 165848343; + else + busFreq = pllFreq / 2; + + return hleLogSuccessI(SCEMISC, 0); } static u32 scePowerGetCpuClockFrequencyInt() { - int cpuFreq = CoreTiming::GetClockFrequencyMHz(); - DEBUG_LOG(HLE,"%i=scePowerGetCpuClockFrequencyInt()", cpuFreq); - return cpuFreq; + int cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000; + return hleLogSuccessI(SCEMISC, cpuFreq); } static u32 scePowerGetPllClockFrequencyInt() { - INFO_LOG(HLE,"%i=scePowerGetPllClockFrequencyInt()", pllFreq); - return pllFreq; + return hleLogSuccessInfoI(SCEMISC, pllFreq / 1000000); } static u32 scePowerGetBusClockFrequencyInt() { - INFO_LOG(HLE,"%i=scePowerGetBusClockFrequencyInt()", busFreq); - return busFreq; + return hleLogSuccessInfoI(SCEMISC, busFreq / 1000000); } static float scePowerGetCpuClockFrequencyFloat() { - int cpuFreq = CoreTiming::GetClockFrequencyMHz(); - DEBUG_LOG(HLE, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq); - return (float) cpuFreq; + float cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000.0f; + DEBUG_LOG(SCEMISC, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq); + return cpuFreq; } static float scePowerGetPllClockFrequencyFloat() { - INFO_LOG(HLE, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq); - return (float) pllFreq; + INFO_LOG(SCEMISC, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq / 1000000.0f); + return (float) pllFreq / 1000000.0f; } static float scePowerGetBusClockFrequencyFloat() { - INFO_LOG(HLE, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq); - return (float) busFreq; + INFO_LOG(SCEMISC, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq / 1000000.0f); + return (float) busFreq / 1000000.0f; } static int scePowerTick() { - DEBUG_LOG(HLE, "scePowerTick()"); + DEBUG_LOG(SCEMISC, "scePowerTick()"); // Don't think we need to do anything. return 0; } static u32 IsPSPNonFat() { - DEBUG_LOG(HLE, "%d=scePower_a85880d0_IsPSPNonFat()", g_Config.iPSPModel); + DEBUG_LOG(SCEMISC, "%d=scePower_a85880d0_IsPSPNonFat()", g_Config.iPSPModel); return g_Config.iPSPModel; } From ad299ca92dcb9f5dca572508a8d8c1d64cb01b7e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Apr 2019 15:06:32 -0700 Subject: [PATCH 2/2] Power: Match reschedule timing better. --- Core/HLE/scePower.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 35ff5c4f0e..b4fe3b0925 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -441,6 +441,8 @@ static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { // Only reschedules when the stepped PLL frequency changes. // It seems like the busfreq parameter has no effect (but can cause errors.) if (RealpllFreq != PowerPllMhzToHz(pllfreq)) { + int oldPll = RealpllFreq / 1000000; + RealpllFreq = PowerPllMhzToHz(pllfreq); RealbusFreq = PowerBusMhzToHz(RealpllFreq / 2000000); if (g_Config.iLockedCPUSpeed <= 0) { @@ -448,7 +450,16 @@ static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { busFreq = RealbusFreq; CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); } - return hleDelayResult(0, "scepower set clockFrequency", 150000); + + // The delay depends on the source and destination frequency, most are 150ms. + int newPll = RealpllFreq / 1000000; + int usec = 150000; + if ((newPll == 190 && oldPll == 222) || (newPll == 222 && oldPll == 190)) + usec = 15700; + else if ((newPll == 266 && oldPll == 333) || (newPll == 333 && oldPll == 266)) + usec = 16600; + + return hleDelayResult(0, "scepower set clockFrequency", usec); } if (g_Config.iLockedCPUSpeed <= 0) CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq));