From bd6d6151431b326064c300361d70d20cf7bfe9a6 Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Sun, 6 Sep 2026 15:37:36 +0200 Subject: [PATCH] improve timings --- .../loader/source/oc/customize.hpp | 4 +- .../loader/source/oc/mtc_timing_value.hpp | 41 +- .../pcv/common/calculate_timings_common.cpp | 186 +++++++++ .../calculate_timings_common.hpp} | 18 +- .../pcv/erista/calculate_timings_erista.cpp | 285 +++++++------- .../pcv/erista/calculate_timings_erista.hpp | 1 + .../source/oc/pcv/erista/pcv_erista_mtc.cpp | 38 +- .../pcv/mariko/calculate_timings_mariko.cpp | 356 +++++++++--------- .../pcv/mariko/calculate_timings_mariko.hpp | 1 - .../source/oc/pcv/mariko/pcv_mariko_mtc.cpp | 5 +- .../source/oc/pcv/mariko/timing_tables.cpp | 47 --- .../loader/source/oc/pcv/pcv_common.hpp | 5 - .../hoc-clk/overlay/src/ui/gui/misc_gui.cpp | 15 +- 13 files changed, 566 insertions(+), 436 deletions(-) create mode 100644 Source/Atmosphere/stratosphere/loader/source/oc/pcv/common/calculate_timings_common.cpp rename Source/Atmosphere/stratosphere/loader/source/oc/pcv/{mariko/timing_tables.hpp => common/calculate_timings_common.hpp} (69%) delete mode 100644 Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.cpp diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index c34cfbcb..e5e9051e 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -47,14 +47,14 @@ enum ReadLatency: u32 { RL_2133 = 40, RL_1866 = 36, RL_1600 = 32, - RL_1331 = 28, + RL_1333 = 28, }; enum WriteLatency: u32 { WL_2133 = 18, WL_1866 = 16, WL_1600 = 14, - WL_1331 = 12, + WL_1333 = 12, }; using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp index 6dcf9ef0..67c1370f 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2023 hanai3Bi * - * Copyright (c) 2025 Lightos_ + * Copyright (c) Lightos_ * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -20,7 +20,7 @@ #include "oc_common.hpp" -namespace ams::ldr::hoc { +namespace ams::ldr::hoc::pcv { #define MAX(A, B) std::max(A, B) #define MIN(A, B) std::min(A, B) #define CEIL(A) std::ceil(A) @@ -31,6 +31,7 @@ namespace ams::ldr::hoc { #define PACK_U32_NIBBLE_HIGH_BYTE_LOW(high, low) ((static_cast(high & 0xF) << 28) | (static_cast(low) & 0xFF)) #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) + #define GET_CYCLE_FLOOR(PARAM) u32(FLOOR(double(PARAM) / tCK_avg)) /* Burst latency, not to be confused with base latency (tWRL). */ const u32 BL = 16; @@ -41,6 +42,9 @@ namespace ams::ldr::hoc { /* DQS output access time from CK_t/CK_c. */ const double tDQSCK_max = 3.5; + /* Turn-around time RDQS to WDQS for WDQS control case. */ + const double tDQSTA = 0.25; + /* Write preamble. (tCK) */ const u32 tWPRE = 2; @@ -59,12 +63,22 @@ namespace ams::ldr::hoc { /* DQ-to-DQS offset(max) (ns) */ const double tDQS2DQ_max = 0.8; + /* Internal READ to PRECHARGE command delay */ + const double tRTP = 7.5; + /* Write recovery time. */ const u32 tWR = 18; - const double roundTripDelay = 0.5; /* ns. */ + /* Write-to-Read delay. */ + const u32 tWTRStock = 10; - namespace pcv::erista { + /* Round trace routing delay(?) Guessed based on tK1 (ns) */ + const double roundTripDelay = 0.5; + + inline u32 RL; + inline u32 WL; + + namespace erista { const std::array tRCD_values = { 18, 17, 16, 15, 14, 13, 12, 11 }; const std::array tRP_values = { 18, 17, 16, 15, 14, 13, 12, 11 }; const std::array tRAS_values = { 42, 36, 34, 32, 30, 28, 26, 24, 22, 20 }; @@ -88,9 +102,6 @@ namespace ams::ldr::hoc { const u32 tFAW = static_cast(tRRD * 4.0); const double tRPab = tRPpb + 3; - inline u32 RL; - inline u32 WL; - inline u32 tR2P; inline u32 tR2W; inline u32 rext; @@ -124,13 +135,15 @@ namespace ams::ldr::hoc { inline u8 mrw2; namespace { - double chipDelay = 1.165; /* Guessed. */ + /* Delay of entering soc? Guessed based on tK1. (ns) */ + double chipDelay = 1.655; } + /* Soc->dram->soc delay(?) */ const double flyByTime = chipDelay + roundTripDelay; } - namespace pcv::mariko { + namespace mariko { const std::array tRCD_values = { 18, 17, 16, 15, 14, 13, 12, 11 }; const std::array tRP_values = { 18, 17, 16, 15, 14, 13, 12, 11 }; const std::array tRAS_values = { 42, 36, 34, 32, 30, 28, 26, 24, 22, 20 }; @@ -153,9 +166,6 @@ namespace ams::ldr::hoc { inline u32 refresh_raw; - inline u32 RL; - inline u32 WL; - inline u32 tR2P; inline u32 tR2W; inline u32 tRTM; @@ -188,15 +198,16 @@ namespace ams::ldr::hoc { inline u32 tCLKSTOP; - inline u32 pdex2mrr; + inline double pdex2mrr; inline u8 mrw2; - /* TODO: Add comments (lazy) */ namespace { - const double chipDelay = 1.109; /* ns. */ + /* Delay of entering soc? Guessed based on tK1. (ns) */ + const double chipDelay = 1.109; } + /* Soc->dram->soc delay(?) */ const double flyByTime = chipDelay + roundTripDelay; } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/common/calculate_timings_common.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/common/calculate_timings_common.cpp new file mode 100644 index 00000000..1a366a80 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/common/calculate_timings_common.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) Lightos_ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "../../oc_common.hpp" +#include "../../mtc_timing_value.hpp" + +namespace ams::ldr::hoc::pcv { + + namespace { + void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) { + latency += index * latencyStep; + } + + void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) { + if (freq > 1600'000 && freq <= 1866'000) { /* 1866tRWL */ + latency += latencyStep * 2; + } else { /* 2133tRWL */ + latency += latencyStep * 3; + } + } + + static s32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) { + s32 maxIndex = -1; + for (u32 i = 0; i < latencySize; ++i) { + if (latencyArray[i]) { + maxIndex = i; + } + } + + return maxIndex; + } + + void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) { + for (u32 i = 0; i <= indexMax; ++i) { + if (latencyArray[i] != 0 && freq <= latencyArray[i]) { + SwitchLatency(latency, i, latencyStep); + return; + } + } + + SwitchLatency(latency, indexMax, latencyStep); + } + + u32 RoundUpToEven(u32 value) { + return value + (value % 2); + } + + + double GetTckAvg(u32 khz) { + return 1000'000.0 / static_cast(khz); + } + } + + void HandleLatency(u32 freq) { + static s32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency)); + static s32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency)); + constexpr u32 ReadLatencyStep = 4; + constexpr u32 WriteLatencyStep = 2; + bool autoLatencyRead = false, autoLatencyWrite = false; + + if (rlIndexMax == -1) { + AutoLatency(RL, freq, ReadLatencyStep); + autoLatencyRead = true; + } + + if (wlIndexMax == -1) { + AutoLatency(WL, freq, WriteLatencyStep); + autoLatencyWrite = true; + } + + if (autoLatencyRead && autoLatencyWrite) { + return; + } + + if (!autoLatencyRead) { + HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep); + } + + if (!autoLatencyWrite) { + HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep); + } + } + + u32 GetRlFrequency() { + switch (RL) { + case RL_1333: + return 1333'000; + case RL_1600: + return 1600'000; + case RL_1866: + return 1866'000; + case RL_2133: + return 2133'000; + default: + return 2133'000; + } + } + + u32 GetWlFrequency() { + switch (WL) { + case WL_1333: + return 1333'000; + case WL_1600: + return 1600'000; + case WL_1866: + return 1866'000; + case WL_2133: + return 2133'000; + default: + return 2133'000; + } + } + + u32 GetWdqsOn() { + /* Set A. */ + switch (WL) { + case WL_1333: + return 4; + case WL_1600: + case WL_1866: + return 6; + case WL_2133: + return 8; + default: + return 8; + } + } + + u32 CalculateNWr() { + const u32 latencyFrequency = GetWlFrequency(); + const double tCK = GetTckAvg(latencyFrequency); + const u32 nWr = CEIL(tWR / tCK); + + return RoundUpToEven(nWr); + } + + u32 CalculateNTRTP() { + const u32 latencyFrequency = GetRlFrequency(); + const double tCK = GetTckAvg(latencyFrequency); + + return CEIL(tRTP / tCK); + } + + u8 CalculateMrw2() { + static const u8 rlMapDBI[8] = { + 6, 12, 16, 22, 28, 32, 36, 40 + }; + + static const u8 wlMapSetA[8] = { + 4, 6, 8, 10, 12, 14, 16, 18 + }; + + u32 rlIndex = 0; + u32 wlIndex = 0; + + for (u32 i = 0; i < std::size(rlMapDBI); ++i) { + if (rlMapDBI[i] == RL) { + rlIndex = i; + break; + } + } + + for (u32 i = 0; i < std::size(wlMapSetA); ++i) { + if (wlMapSetA[i] == WL) { + wlIndex = i; + break; + } + } + + /* DBI is always enabled. */ + return static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6))); + } +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/common/calculate_timings_common.hpp similarity index 69% rename from Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.hpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/common/calculate_timings_common.hpp index a1aa7f0a..e6d37e18 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/common/calculate_timings_common.hpp @@ -15,12 +15,20 @@ */ #pragma once -#include -namespace ams::ldr::hoc::pcv::mariko { +namespace ams::ldr::hoc::pcv { - extern const RextPatch g_rext_table[]; - extern const u32 g_rext_table_size; - const RextPatch *FindRext(u32 freq); + struct RextPatch { + u32 freq; + u32 rext; + }; + + void HandleLatency(u32 freq); + u32 GetRlFrequency(); + u32 GetWlFrequency(); + u32 GetWdqsOn(); + u32 CalculateNWr(); + u32 CalculateNTRTP(); + u8 CalculateMrw2(); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.cpp index 1a40676f..e4521a08 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.cpp @@ -15,189 +15,174 @@ */ #include "../../mtc_timing_value.hpp" +#include "../common/calculate_timings_common.hpp" namespace ams::ldr::hoc::pcv::erista { - /* Dumped from bpmp fw mtc table (l4t). */ - const RextPatch g_rext_table[] = { - {1'633'000, 0x17}, {1'728'000, 0x17}, {1'795'200, 0x19}, - {1'862'400, 0x1A}, {1'894'400, 0x1A}, {1'932'800, 0x1A}, - {1'958'400, 0x1A}, {1'996'800, 0x1A}, {2'035'200, 0x1A}, - {2'064'000, 0x19}, {2'099'200, 0x19}, {2'131'200, 0x1A}, - {2'163'200, 0x1A}, {2'188'800, 0x1A}, {2'227'200, 0x1A}, - {2'265'600, 0x1B}, {2'291'200, 0x1B}, {2'329'600, 0x1A}, - {2'361'600, 0x1A}, - }; + namespace { + /* Dumped from bpmp fw mtc table (l4t). */ + const RextPatch gRextTable[] = { + {1'633'000, 0x17}, {1'728'000, 0x17}, {1'795'200, 0x19}, + {1'862'400, 0x1A}, {1'894'400, 0x1A}, {1'932'800, 0x1A}, + {1'958'400, 0x1A}, {1'996'800, 0x1A}, {2'035'200, 0x1A}, + {2'064'000, 0x19}, {2'099'200, 0x19}, {2'131'200, 0x1A}, + {2'163'200, 0x1A}, {2'188'800, 0x1A}, {2'227'200, 0x1A}, + {2'265'600, 0x1B}, {2'291'200, 0x1B}, {2'329'600, 0x1A}, + {2'361'600, 0x1A}, + }; - const RextPatch *LookupRext(u32 freq) { - for (u32 i = 0; i < std::size(g_rext_table); i++) { - if (g_rext_table[i].freq >= freq) { - return &g_rext_table[i]; + const RextPatch *LookupRext(u32 freq) { + for (u32 i = 0; i < std::size(gRextTable); i++) { + if (gRextTable[i].freq >= freq) { + return &gRextTable[i]; + } } - } - return nullptr; - } - - void GetRext(u32 freq) { - if (auto r = LookupRext(freq)) { - rext = r->rext; - return; + return nullptr; } - /* Fallback */ - rext = 0x1A; - } - - void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) { - latency += index * latencyStep; - } - - static s32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) { - s32 maxIndex = -1; - for (u32 i = 0; i < latencySize; ++i) { - if (latencyArray[i]) { - maxIndex = i; - } - } - - return maxIndex; - } - - void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) { - if (freq > 1600'000 && freq <= 1866'000) { /* 1866tRWL */ - latency += latencyStep * 2; - } else { /* 2133tRWL */ - latency += latencyStep * 3; - } - } - - void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) { - for (u32 i = 0; i <= indexMax; ++i) { - if (latencyArray[i] != 0 && freq <= latencyArray[i]) { - SwitchLatency(latency, i, latencyStep); + void GetRext(u32 freq) { + if (auto r = LookupRext(freq)) { + rext = r->rext; return; } + + /* Fallback */ + rext = 0x1A; } - SwitchLatency(latency, indexMax, latencyStep); - } + void CalculateReadTimings(u32 freq, double tCK_avg) { + tR2P = CalculateNTRTP(); - void HandleLatency(u32 freq) { - static s32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency)); - static s32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency)); - constexpr u32 ReadLatencyStep = 4; - constexpr u32 WriteLatencyStep = 2; - bool autoLatencyRead = false, autoLatencyWrite = false; + double fullRoundDelay = flyByTime + tDQSCK_max; - if (rlIndexMax == -1) { - AutoLatency(RL, freq, ReadLatencyStep); - autoLatencyRead = true; + /* Read to write turnaround. */ + /* Nvidia seem to use a 0.25 constant here for tK1, I am 99% certain this is tDQSTA. */ + /* This still left a mismatch; after some brute forcing I got GET_CYCLE_FLOOR(1.5) - 8 (using a static latency) as the cleanest result. */ + /* The latency definitely is not WL, but when testing for every latency, it lines up perfectly with wqsOn and wl seems to cancel out? */ + tR2W = RL + (BL / 2) + CEIL((fullRoundDelay / tCK_avg) + tDQSTA) - GET_CYCLE_FLOOR(1.5) - GetWdqsOn() + tWPRE - (C.t6_tRTW * 3) + finetRTW; + + /* Read data valid: Read command -> data ready to be latched into the registers. */ + rdv = RL + GET_CYCLE_CEIL(fullRoundDelay) + 18; + + /* Read command -> data poppable at the pad macros, 16 cycles ahead of rdv. */ + qpop = rdv - 16; + + constexpr double TrimmerStepPs = 3.0; + constexpr double MaxTrimmerSteps = 95.0; + + double tCkAvgPs = tCK_avg * 1000.0; + double tckDerated = tCkAvgPs * 0.85; + u32 derateStepCount = MIN(FLOOR(tckDerated / TrimmerStepPs), MaxTrimmerSteps); + double deratedNsDelay = (derateStepCount * TrimmerStepPs) / 1000.0; + + /* readEyeEndDerated, nv call this quseExtra. */ + u32 quseExtra = GET_CYCLE_CEIL(fullRoundDelay - deratedNsDelay); + + /* The amount of deration from the perspective of a full round trip. */ + u32 readEyeEndDerateMargin = GET_CYCLE_CEIL(fullRoundDelay) - quseExtra; + + u32 tQuse = FLOOR((flyByTime + 0.94) / tCK_avg); + u32 readEyeStart = tQuse + RL + 1; + + u32 einputMinimal = readEyeStart + readEyeEndDerateMargin - 2; + + /* Data valid window, quse aligns with the start of the read eye (probably). */ + /* DQS needs to be logically and'ed with quse in order to be valid. */ + /* einputMinimal satisfies this. */ + quse = einputMinimal; + + /* Difference between the two eye edges. */ + quse_width = quseExtra - tQuse + 4; + + /* input receiver takes time to enable. */ + u32 einputLatency = GET_CYCLE_CEIL(10.0); + + /* Enable input receiver. */ + einput = einputMinimal - einputLatency; + + /* Duration of input receiver being enabled? */ + einput_duration = einputLatency + quse_width + 4; + + /* qrst_duration is CEIL(a / tCK_avg + b) */ + /* a and b were derived through a brute forcer. */ + u32 qrst_duration = CEIL(1.4 / tCK_avg + 3.5); + u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); + + /* quse reset? */ + qrst = PACK_U32(qrst_duration, qrstLow); + + /* Qsafe, safe to reset quse? */ + qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); + + /* Input buffer delay? */ + ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); + + GetRext(freq); } - if (wlIndexMax == -1) { - AutoLatency(WL, freq, WriteLatencyStep); - autoLatencyWrite = true; + void CalculateWriteTimings(double tCK_avg) { + /* Write to read turnaround. */ + /* According to jedec specifications: nWR >= tWTR + nRTP */ + /* Therefore tWTR >= nWR - tRTP. */ + const u32 nTWTR = GET_CYCLE_CEIL(tWTRStock); + const u32 nWr = CalculateNWr(); + u32 autoPrecharcetWTRMin = nWr - tR2P; + + /* tWTR reduction is applied in a weird way to not break existing setups. */ + tW2R = WL + 1 + MAX(autoPrecharcetWTRMin, nTWTR) - GET_CYCLE_CEIL(tWTR) + finetWTR; + tW2P = (CEIL(WL * 1.7303) * 2) - 5; + tWTPDEN = CEIL(((1.8 / tCK_avg) + MAX(RL + (2.7 / tCK_avg), static_cast(tW2P))) + (BL / 2)); + + wdv = WL; + wsv = WL - 2; + wev = WL - 4; + + /* Output buffer delay? */ + u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); + u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); + obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); } - if (autoLatencyRead && autoLatencyWrite) { - return; - } + void CalculatePowerDownTimings(double tCK_avg) { + pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); + tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; - if (!autoLatencyRead) { - HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep); - } - - if (!autoLatencyWrite) { - HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep); + const double tMMRI = tRCD + (tCK_avg * 3); + pdex2mrr = tMMRI + 10; } } - void CalculateMrw2() { - static const u8 rlMapDBI[8] = { - 6, 12, 16, 22, 28, 32, 36, 40 + u8 GetNWrIndex() { + static const u8 nWrMap[8] = { + 6, 10, 16, 20, 24, 30, 34, 40 }; - static const u8 wlMapSetA[8] = { - 4, 6, 8, 10, 12, 14, 16, 18 - }; + const u32 nWr = CalculateNWr(); - u32 rlIndex = 0; - u32 wlIndex = 0; - - for (u32 i = 0; i < std::size(rlMapDBI); ++i) { - if (rlMapDBI[i] == 32) { - rlIndex = i; - break; + for (u8 i = 0; i < std::size(nWrMap); ++i) { + if (nWrMap[i] >= nWr) { + return i; } } - for (u32 i = 0; i < std::size(wlMapSetA); ++i) { - if (wlMapSetA[i] == WL) { - wlIndex = i; - break; - } - } - - /* DBI is always enabled. */ - mrw2 = static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6))); + return static_cast(std::size(nWrMap) - 1); } void CalculateTimings(double tCK_avg, u32 freq) { - RL = RL_1331; - WL = WL_1331; + RL = RL_1333; + WL = WL_1333; + /* I don't understand why, I don't want to know why, but for some fucking reason erista doesn't handle RL 40. */ + /* This should not be required but for these fuck damn reasons any type of dram oc will crash instantly with RL 40. */ + /* But it works fine in l4t? Even when copying the tables it still doesn't work??? WTF */ + readLatency[std::size(readLatency) - 1] = 0; HandleLatency(freq); - GetRext(freq); - CalculateMrw2(); + RL = std::min(static_cast(RL_1866), RL); - tR2P = CEIL((RL * 0.426) - 2.0); - tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW; - - u32 fullRoundDelay = CEIL((flyByTime + tDQSCK_max) / tCK_avg); - rdv = RL + fullRoundDelay + 19; - qpop = rdv - 16; - - constexpr double TrimmerStepPs = 3.0; - constexpr double MaxTrimmerSteps = 96.0; - - double tCkAvgPs = tCK_avg * 1000.0; - double tckDerated = tCkAvgPs * 0.85; - u32 derateStepCount = MIN(FLOOR(tckDerated / TrimmerStepPs), MaxTrimmerSteps); - double deratedNsDelay = (derateStepCount * TrimmerStepPs) / 1000.0; - u32 fullTripDerating = fullRoundDelay - CEIL((flyByTime + tDQSCK_max - deratedNsDelay) / tCK_avg); - u32 readEyeStart = FLOOR((flyByTime + 0.94) / tCK_avg) + RL; - - /* Data valid window, quse aligns with the start of the read eye (probably). */ - /* DQS needs to be logically and'ed with quse in order to be valid. */ - quse = readEyeStart + fullTripDerating - 2; - - quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782); - einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width; - einput = quse - CEIL(9.928 / tCK_avg); - u32 qrst_duration = FLOOR(8.399 - tCK_avg); - u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); - qrst = PACK_U32(qrst_duration, qrstLow); - ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); - qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); - - tW2P = (CEIL(WL * 1.7303) * 2) - 5; - tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast(tW2P))) + (BL / 2)); - const s32 wlToRl = static_cast(WL) - static_cast(RL); - tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * wlToRl), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR; - - wdv = WL; - wsv = WL - 2; - wev = 0xA + (WL - 14); - - u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); - u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); - obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); - - pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); - tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; - - const double tMMRI = tRCD + (tCK_avg * 3); - pdex2mrr = tMMRI + 10; + CalculateReadTimings(freq, tCK_avg); + CalculateWriteTimings(tCK_avg); + CalculatePowerDownTimings(tCK_avg); + mrw2 = CalculateMrw2(); } } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.hpp index 7a61386d..668f44fc 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.hpp @@ -18,6 +18,7 @@ namespace ams::ldr::hoc::pcv::erista { + u8 GetNWrIndex(); void CalculateTimings(double tCK_avg, u32 freq); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.cpp index f2c7808b..12dd90a3 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.cpp @@ -46,6 +46,7 @@ namespace ams::ldr::hoc::pcv::erista { #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ TABLE->burst_regs.PARAM = VALUE; \ TABLE->shadow_regs_ca_train.PARAM = VALUE; \ + TABLE->shadow_regs_quse_train.PARAM = VALUE; \ TABLE->shadow_regs_rdwr_train.PARAM = VALUE; /* Ram power down */ @@ -109,28 +110,28 @@ namespace ams::ldr::hoc::pcv::erista { /* Accept imperfection or prepare for suffering. */ // #if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) - // WRITE_PARAM_ALL_REG(table, emc_einput, einput); - // WRITE_PARAM_ALL_REG(table, emc_einput_duration, einput_duration); - // WRITE_PARAM_ALL_REG(table, emc_obdly, obdly); + WRITE_PARAM_ALL_REG(table, emc_einput, einput); + WRITE_PARAM_ALL_REG(table, emc_einput_duration, einput_duration); + WRITE_PARAM_ALL_REG(table, emc_obdly, obdly); WRITE_PARAM_ALL_REG(table, emc_ibdly, ibdly); WRITE_PARAM_ALL_REG(table, emc_wdv_mask, wdv); - // WRITE_PARAM_ALL_REG(table, emc_quse_width, quse_width); - // WRITE_PARAM_ALL_REG(table, emc_quse, quse); + WRITE_PARAM_ALL_REG(table, emc_quse_width, quse_width); + WRITE_PARAM_ALL_REG(table, emc_quse, quse); WRITE_PARAM_ALL_REG(table, emc_wdv, wdv); WRITE_PARAM_ALL_REG(table, emc_wsv, wsv); WRITE_PARAM_ALL_REG(table, emc_wev, wev); - // WRITE_PARAM_ALL_REG(table, emc_qrst, qrst); - // WRITE_PARAM_ALL_REG(table, emc_tr_qrst, qrst); - // WRITE_PARAM_ALL_REG(table, emc_qsafe, qsafe); - // WRITE_PARAM_ALL_REG(table, emc_tr_qsafe, qsafe); - // WRITE_PARAM_ALL_REG(table, emc_tr_qpop, qpop); - // WRITE_PARAM_ALL_REG(table, emc_qpop, qpop); - // WRITE_PARAM_ALL_REG(table, emc_rdv, rdv); - // WRITE_PARAM_ALL_REG(table, emc_tr_rdv_mask, rdv + 2); - // WRITE_PARAM_ALL_REG(table, emc_rdv_early, rdv - 2); - // WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, rdv); - // WRITE_PARAM_ALL_REG(table, emc_rdv_mask, rdv + 2); - // WRITE_PARAM_ALL_REG(table, emc_tr_rdv, rdv); + WRITE_PARAM_ALL_REG(table, emc_qrst, qrst); + WRITE_PARAM_ALL_REG(table, emc_tr_qrst, qrst); + WRITE_PARAM_ALL_REG(table, emc_qsafe, qsafe); + WRITE_PARAM_ALL_REG(table, emc_tr_qsafe, qsafe); + WRITE_PARAM_ALL_REG(table, emc_tr_qpop, qpop); + WRITE_PARAM_ALL_REG(table, emc_qpop, qpop); + WRITE_PARAM_ALL_REG(table, emc_rdv, rdv); + WRITE_PARAM_ALL_REG(table, emc_tr_rdv_mask, rdv + 2); + WRITE_PARAM_ALL_REG(table, emc_rdv_early, rdv - 2); + WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, rdv); + WRITE_PARAM_ALL_REG(table, emc_rdv_mask, rdv + 2); + WRITE_PARAM_ALL_REG(table, emc_tr_rdv, rdv); table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast(mrw2); table->dram_timings.rl = RL; // #endif @@ -223,8 +224,7 @@ namespace ams::ldr::hoc::pcv::erista { const u32 tRFCabStock = tRFC_values[0] * 2; table->dram_timings.t_rfc = tRFCabStock; - table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast(mrw2); - table->emc_mrw = (table->emc_mrw & ~0x70u) | 0x40; /* nWR */ + table->emc_mrw = (table->emc_mrw & ~0x70u) | (GetNWrIndex() << 4); table->emc_cfg_2 = 0x11083D; table->min_volt = std::clamp(900 + (C.emcDvbShift * 25), 900, 1050); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.cpp index ac58276b..a939a2d4 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.cpp @@ -14,227 +14,209 @@ * along with this program. If not, see . */ -#include #include "../../mtc_timing_value.hpp" -#include "timing_tables.hpp" +#include "../common/calculate_timings_common.hpp" namespace ams::ldr::hoc::pcv::mariko { - void GetRext(u32 freq) { - if (auto r = FindRext(freq)) { - rext = r->rext; - return; - } + namespace { + const RextPatch gRextTable[] = { + {1'633'000, 0x17}, {1'666'000, 0x18}, {1'700'000, 0x18}, + {1'733'000, 0x18}, {1'766'000, 0x18}, {1'800'000, 0x1A}, + {1'833'000, 0x1A}, {1'866'000, 0x1A}, {1'900'000, 0x19}, + {1'933'000, 0x19}, {1'966'000, 0x19}, {2'100'000, 0x1A}, + {2'133'000, 0x1A}, {2'166'000, 0x19}, {2'200'000, 0x19}, + {2'233'000, 0x19}, {2'266'000, 0x1A}, {2'300'000, 0x1B}, + {2'333'000, 0x1B}, {2'366'000, 0x1B}, {2'400'000, 0x1B}, + {2'433'000, 0x1B}, {2'466'000, 0x1B}, {2'500'000, 0x1A}, + {2'533'000, 0x1C}, {2'566'000, 0x1B}, {2'600'000, 0x1B}, + {2'633'000, 0x1B}, {2'666'000, 0x1B}, {2'700'000, 0x1C}, + {2'733'000, 0x1C}, {2'766'000, 0x1D}, {2'800'000, 0x1D}, + {2'833'000, 0x1D}, {2'866'000, 0x1D}, {2'900'000, 0x1D}, + {2'933'000, 0x1C}, {2'966'000, 0x1D}, {3'000'000, 0x1D}, + {3'033'000, 0x1D}, {3'066'000, 0x1D}, {3'100'000, 0x1D}, + {3'133'000, 0x1D}, {3'166'000, 0x1C}, {3'200'000, 0x1C}, + {3'233'000, 0x1E}, {3'266'000, 0x1F}, {3'300'000, 0x1E}, + {3'333'000, 0x1F}, {3'366'000, 0x1F}, {3'400'000, 0x1F}, + {3'433'000, 0x1F}, {3'466'000, 0x1F}, {3'500'000, 0x1E}, + {3'533'000, 0x1E}, {3'566'000, 0x1E}, {3'600'000, 0x1E}, + {3'633'000, 0x1E}, {3'666'000, 0x1E}, {3'700'000, 0x1F}, + }; - /* Fallback */ - rext = 0x1E; - } - - void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) { - latency += index * latencyStep; - } - - static s32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) { - s32 maxIndex = -1; - for (u32 i = 0; i < latencySize; ++i) { - if (latencyArray[i]) { - maxIndex = i; + const RextPatch *LookupRext(u32 freq) { + for (u32 i = 0; i < std::size(gRextTable); i++) { + if (gRextTable[i].freq >= freq) { + return &gRextTable[i]; + } } + return nullptr; } - return maxIndex; - } - - void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) { - if (freq > 1600'000 && freq <= 1866'000) { /* 1866tRWL */ - latency += latencyStep * 2; - } else { /* 2133tRWL */ - latency += latencyStep * 3; - } - } - - void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) { - for (u32 i = 0; i <= indexMax; ++i) { - if (latencyArray[i] != 0 && freq <= latencyArray[i]) { - SwitchLatency(latency, i, latencyStep); + void GetRext(u32 freq) { + if (auto r = LookupRext(freq)) { + rext = r->rext; return; } + + /* Fallback */ + rext = 0x1E; } - SwitchLatency(latency, indexMax, latencyStep); - } + void CalculateActivatePrechargeTimings(bool lowFreq) { + tRCD = tRCD_values[lowFreq ? C.low_t1_tRCD : C.t1_tRCD]; + tRPpb = tRP_values[lowFreq ? C.low_t2_tRP : C.t2_tRP]; + tRAS = tRAS_values[lowFreq ? C.low_t3_tRAS : C.t3_tRAS]; - void HandleLatency(u32 freq) { - static s32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency)); - static s32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency)); - constexpr u32 ReadLatencyStep = 4; - constexpr u32 WriteLatencyStep = 2; - bool autoLatencyRead = false, autoLatencyWrite = false; - - if (rlIndexMax == -1) { - AutoLatency(RL, freq, ReadLatencyStep); - autoLatencyRead = true; + tRC = tRAS + tRPpb; + tRPab = tRPpb + 3; } - if (wlIndexMax == -1) { - AutoLatency(WL, freq, WriteLatencyStep); - autoLatencyWrite = true; + void CalculateActivateWindowTimings(bool lowFreq) { + tRRD = tRRD_values[lowFreq ? C.low_t4_tRRD : C.t4_tRRD]; + tFAW = static_cast(tRRD * 4.0); } - if (autoLatencyRead && autoLatencyWrite) { - return; + void CalculateReadTimings(u32 freq, double tCK_avg, bool lowFreq) { + u32 tRTW = lowFreq ? C.low_t6_tRTW : C.t6_tRTW; + s32 finetRTW = C.fineTune_t6_tRTW; + + double fullRoundDelay = flyByTime + tDQSCK_max; + + /* Read to precharge delay. */ + tR2P = CalculateNTRTP(); + + /* Read to write turnaround. */ + /* Nvidia seem to use a 0.25 constant here for tK1, I am 99% certain this is tDQSTA. */ + /* This still left a mismatch; after some brute forcing I got GET_CYCLE_FLOOR(1.5) - 8 (using a static latency) as the cleanest result. */ + /* The latency definitely is not WL, but when testing for every latency, it lines up perfectly with wqsOn and wl seems to cancel out? */ + tR2W = RL + (BL / 2) + CEIL((fullRoundDelay / tCK_avg) + tDQSTA) - GET_CYCLE_FLOOR(1.5) - GetWdqsOn() - (tRTW * 3) + finetRTW; + tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + GET_CYCLE_FLOOR(7.489); + tRATM = CEIL((tRTM - 10.0) + (RL * 0.426)); + + /* Read data valid: Read command -> data ready to be latched into the registers. */ + rdv = RL + GET_CYCLE_CEIL(fullRoundDelay) + 16; + + /* Read command -> data poppable at the pad macros, 14 cycles ahead of rdv. */ + qpop = rdv - 14; + + constexpr double TrimmerStepPs = 3.0; + constexpr double MaxTrimmerSteps = 96.0; + + double tCkAvgPs = tCK_avg * 1000.0; + double tckDerated = tCkAvgPs * 0.85; + u32 derateStepCount = MIN(FLOOR(tckDerated / TrimmerStepPs), MaxTrimmerSteps); + double deratedNsDelay = (derateStepCount * TrimmerStepPs) / 1000.0; + /* readEyeEndDerated, nv call this quseExtra. */ + u32 quseExtra = GET_CYCLE_CEIL(fullRoundDelay - deratedNsDelay); + + /* The amount of deration from the perspective of a full round trip. */ + u32 readEyeEndDerateMargin = GET_CYCLE_CEIL(fullRoundDelay) - quseExtra; + + u32 tQuse = FLOOR((flyByTime + 0.94) / tCK_avg); + u32 readEyeStart = tQuse + RL + 1; + + u32 einputMinimal = readEyeStart + readEyeEndDerateMargin - 2; + + /* Data valid window, quse aligns with the start of the read eye (probably). */ + /* DQS needs to be logically and'ed with quse in order to be valid. */ + /* einputMinimal satisfies this. */ + quse = einputMinimal; + + /* input receiver takes time to enable. */ + u32 einputLatency = GET_CYCLE_CEIL(10.0); + + /* Enable input receiver. */ + einput = einputMinimal - einputLatency; + + /* Difference between the two eye edges. */ + quse_width = quseExtra - tQuse + 4; + + /* Duration of input receiver being enabled? */ + einput_duration = einputLatency + quse_width + 4; + + /* qrst_duration is CEIL(a / tCK_avg + b) */ + /* a and b were derived through a brute forcer. */ + u32 qrst_duration = CEIL(1.4 / tCK_avg + 3.5); + u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); + + /* quse reset? */ + qrst = PACK_U32(qrst_duration, qrstLow); + + /* Input buffer delay? */ + ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); + + /* Qsafe, safe to reset quse? */ + qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); + + GetRext(freq); } - if (!autoLatencyRead) { - HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep); + void CalculateWriteTimings(double tCK_avg, bool lowFreq) { + u32 tWTR = 10 - tWTR_values[lowFreq ? C.low_t7_tWTR : C.t7_tWTR]; + s32 finetWTR = C.fineTune_t7_tWTR; + + /* Write to read turnaround. */ + /* According to jedec specifications: nWR >= tWTR + nRTP */ + /* Therefore tWTR >= nWR - tRTP. */ + const u32 nTWTR = GET_CYCLE_CEIL(tWTRStock); + const u32 nWr = CalculateNWr(); + u32 autoPrecharcetWTRMin = nWr - tR2P; + + /* tWTR reduction is applied in a weird way to not break existing setups. */ + tW2R = WL + 1 + MAX(autoPrecharcetWTRMin, nTWTR) - GET_CYCLE_CEIL(tWTR) + finetWTR; + tW2P = (CEIL(WL * 1.7303) * 2) - 5; + tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753)); + tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0; + + wdv = WL; + wsv = WL - 2; + wev = WL - 4; + + /* Output buffer delay? */ + u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); + u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); + obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); } - if (!autoLatencyWrite) { - HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep); - } - } + void CalculateRefreshTimings(double tCK_avg, bool lowFreq) { + tRFCpb = tRFC_values[lowFreq ? C.low_t5_tRFC : C.t5_tRFC]; + tRFCab = tRFCpb * 2; + tXSR = static_cast(tRFCab + 7.5); - void CalculateMrw2() { - static const u8 rlMapDBI[8] = { - 6, 12, 16, 22, 28, 32, 36, 40 - }; - - static const u8 wlMapSetA[8] = { - 4, 6, 8, 10, 12, 14, 16, 18 - }; - - u32 rlIndex = 0; - u32 wlIndex = 0; - - for (u32 i = 0; i < std::size(rlMapDBI); ++i) { - if (rlMapDBI[i] == RL) { - rlIndex = i; - break; + u32 tREFI = lowFreq ? C.low_t8_tREFI : C.t8_tREFI; + refresh_raw = 0xFFFF; + if (tREFI != 6) { + refresh_raw = GET_CYCLE_CEIL(tREFpb_values[tREFI]) - 0x40; + refresh_raw = MIN(refresh_raw, static_cast(0xFFFF)); } } - for (u32 i = 0; i < std::size(wlMapSetA); ++i) { - if (wlMapSetA[i] == WL) { - wlIndex = i; - break; - } - } + void CalculatePowerDownTimings(double tCK_avg) { + tWTPDEN = CEIL(((1.8 / tCK_avg) + MAX(RL + (2.7 / tCK_avg), static_cast(tW2P))) + (BL / 2)); + pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); - /* DBI is always enabled. */ - mrw2 = static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6))); + tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; + + double tMMRI = tRCD + (tCK_avg * 3); + pdex2mrr = tMMRI + 10; + } } void CalculateTimings(double tCK_avg, u32 freq) { - RL = RL_1331; - WL = WL_1331; + RL = RL_1333; + WL = WL_1333; HandleLatency(freq); - GetRext(freq); - const bool lowFreq = freq < C.timingEmcTbreak; - tRCD = tRCD_values[lowFreq ? C.low_t1_tRCD : C.t1_tRCD]; - tRPpb = tRP_values[lowFreq ? C.low_t2_tRP : C.t2_tRP]; - tRAS = tRAS_values[lowFreq ? C.low_t3_tRAS : C.t3_tRAS]; - tRRD = tRRD_values[lowFreq ? C.low_t4_tRRD : C.t4_tRRD]; - tRFCpb = tRFC_values[lowFreq ? C.low_t5_tRFC : C.t5_tRFC]; - - u32 tRTW = lowFreq ? C.low_t6_tRTW : C.t6_tRTW; - u32 tWTR = 10 - tWTR_values[lowFreq ? C.low_t7_tWTR : C.t7_tWTR]; - - s32 finetRTW = C.fineTune_t6_tRTW; - s32 finetWTR = C.fineTune_t7_tWTR; - - u32 tREFI = lowFreq ? C.low_t8_tREFI : C.t8_tREFI; - refresh_raw = 0xFFFF; - if (tREFI != 6) { - refresh_raw = GET_CYCLE_CEIL(tREFpb_values[tREFI]) - 0x40; - refresh_raw = MIN(refresh_raw, static_cast(0xFFFF)); - } - - tRC = tRAS + tRPpb; - tRFCab = tRFCpb * 2; - tXSR = static_cast(tRFCab + 7.5); - tFAW = static_cast(tRRD * 4.0); - tRPab = tRPpb + 3; - - tR2P = CEIL((RL * 0.426) - 2.0); - tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (tRTW * 3) + finetRTW; - tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg); - tRATM = CEIL((tRTM - 10.0) + (RL * 0.426)); - - double fullRoundDelay = flyByTime + tDQSCK_max; - /* Read data valid: Read command -> data ready to be latched into the registers. */ - rdv = RL + GET_CYCLE_CEIL(fullRoundDelay) + 16; - - /* Read command -> data poppable at the pad macros, 14 cycles ahead of rdv. */ - qpop = rdv - 14; - - constexpr double TrimmerStepPs = 3.0; - constexpr double MaxTrimmerSteps = 96.0; - - double tCkAvgPs = tCK_avg * 1000.0; - double tckDerated = tCkAvgPs * 0.85; - u32 derateStepCount = MIN(FLOOR(tckDerated / TrimmerStepPs), MaxTrimmerSteps); - double deratedNsDelay = (derateStepCount * TrimmerStepPs) / 1000.0; - /* readEyeEndDerated, nv call this quseExtra. */ - u32 quseExtra = GET_CYCLE_CEIL(fullRoundDelay - deratedNsDelay); - - /* The amount of deration from the perspective of a full round trip. */ - u32 readEyeEndDerateMargin = GET_CYCLE_CEIL(fullRoundDelay) - quseExtra; - - u32 tQuse = FLOOR((flyByTime + 0.94) / tCK_avg); - u32 readEyeStart = tQuse + RL + 1; - - u32 einputMinimal = readEyeStart + readEyeEndDerateMargin - 2; - /* Data valid window, quse aligns with the start of the read eye (probably). */ - /* DQS needs to be logically and'ed with quse in order to be valid. */ - /* einputMinimal satisfies this. */ - quse = einputMinimal; - - /* input receiver takes time to enable. */ - u32 einputLatency = GET_CYCLE_CEIL(10.0); - - /* Enable input receiver. */ - einput = einputMinimal - einputLatency; - - /* Difference between the two eye edges. */ - quse_width = quseExtra - tQuse + 4; - - /* Duration of input receiver being enabled? */ - einput_duration = einputLatency + quse_width + 4; - - /* qrst_duration is CEIL(a / tCK_avg + b) */ - /* a and b were derived through a brute forcer. */ - u32 qrst_duration = CEIL(1.4 / tCK_avg + 3.5); - u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); - qrst = PACK_U32(qrst_duration, qrstLow); - ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); - qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); - tW2P = (CEIL(WL * 1.7303) * 2) - 5; - tWTPDEN = CEIL(((1.8 / tCK_avg) + MAX(RL + (2.7 / tCK_avg), static_cast(tW2P))) + (BL / 2)); - const s32 wlToRl = static_cast(WL) - static_cast(RL); - tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * wlToRl), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR; - tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753)); - tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0; - - wdv = WL; - wsv = WL - 2; - wev = 0xA + (WL - 14); - - u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); - u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); - obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); - - pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); - - tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; - - u32 tMMRI = tRCD + (tCK_avg * 3); - pdex2mrr = tMMRI + 10; - - CalculateMrw2(); + CalculateActivatePrechargeTimings(lowFreq); + CalculateActivateWindowTimings(lowFreq); + CalculateReadTimings(freq, tCK_avg, lowFreq); + CalculateWriteTimings(tCK_avg, lowFreq); + CalculateRefreshTimings(tCK_avg, lowFreq); + CalculatePowerDownTimings(tCK_avg); + mrw2 = CalculateMrw2(); } } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.hpp index 8659d3f2..c908a220 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.hpp @@ -21,4 +21,3 @@ namespace ams::ldr::hoc::pcv::mariko { void CalculateTimings(double tCK_avg, u32 freq); } - diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.cpp index d7187425..3f41ecf4 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.cpp @@ -57,13 +57,12 @@ namespace ams::ldr::hoc::pcv::mariko { WRITE_PARAM_ALL_REG(table, emc_cfg, 0xF3200000); } + CalculateTimings(tCK_avg, table->rate_khz); + u32 trefbw = refresh_raw + 0x40; trefbw = MIN(trefbw, static_cast(0x3FFF)); - const u32 dyn_self_ref_control = (static_cast(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000); - CalculateTimings(tCK_avg, table->rate_khz); - WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD)); WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD)); WRITE_PARAM_ALL_REG(table, emc_rc, MIN(GET_CYCLE_CEIL(tRC), static_cast(0xB9))); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.cpp deleted file mode 100644 index dea73cfc..00000000 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Lightos_ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "../../oc_common.hpp" -#include "timing_tables.hpp" - -namespace ams::ldr::hoc::pcv::mariko { - - const RextPatch g_rext_table[] = { - {2'133'000, 0x1A}, {2'166'000, 0x19}, {2'200'000, 0x19}, - {2'233'000, 0x19}, {2'266'000, 0x1A}, {2'300'000, 0x1B}, - {2'333'000, 0x1B}, {2'366'000, 0x1B}, {2'400'000, 0x1B}, - {2'433'000, 0x1B}, {2'466'000, 0x1B}, {2'500'000, 0x1A}, - {2'533'000, 0x1C}, {2'566'000, 0x1B}, {2'600'000, 0x1B}, - {2'633'000, 0x1B}, {2'666'000, 0x1B}, {2'700'000, 0x1C}, - {2'733'000, 0x1C}, {2'766'000, 0x1D}, {2'800'000, 0x1D}, - {2'833'000, 0x1D}, {2'866'000, 0x1D}, {2'900'000, 0x1D}, - {2'933'000, 0x1C}, {2'966'000, 0x1D}, {3'000'000, 0x1D}, - {3'033'000, 0x1D}, {3'066'000, 0x1D}, {3'100'000, 0x1D}, - {3'133'000, 0x1D}, {3'166'000, 0x1C}, {3'200'000, 0x1C}, - }; - - const u32 g_rext_table_size = sizeof(g_rext_table) / sizeof(g_rext_table[0]); - - const RextPatch *FindRext(u32 freq) { - for (u32 i = 0; i < g_rext_table_size; i++) { - if (g_rext_table[i].freq >= freq) { - return &g_rext_table[i]; - } - } - return nullptr; - } - -} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp index 49a60aa3..bdda0361 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp @@ -187,11 +187,6 @@ namespace ams::ldr::hoc::pcv { }; static_assert(sizeof(SharedClkBus) == 0x58); - struct RextPatch { - u32 freq; - u32 rext; - }; - constexpr u32 CpuClkOSLimit = 1785'000; constexpr u32 GpuClkOsLimit = 921'600; constexpr u32 EmcClkOSLimit = 1600'000; diff --git a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp index a5be9c83..91188eee 100644 --- a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp +++ b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp @@ -1308,7 +1308,10 @@ class RamSubmenuGui : public MiscGui { NamedValue("3300 MHz", 3300000, "±1760 speedo"), NamedValue("3333 MHz", 3333000, "±1780 speedo"), NamedValue("3366 MHz", 3366000, "±1800 speedo"), - NamedValue("3400 MHz", 3400000, "±1820 speedo") + NamedValue("3400 MHz", 3400000, "±1820 speedo"), + NamedValue("3433 MHz", 3433000, "±???? speedo"), + NamedValue("3466 MHz", 3466000, "±???? speedo"), + NamedValue("3500 MHz", 3500000, "±???? speedo"), }; } @@ -1532,6 +1535,9 @@ class RamTimingsSubmenuGui : public MiscGui { NamedValue("3333 MHz", 3333000, "±1780 speedo"), NamedValue("3366 MHz", 3366000, "±1800 speedo"), NamedValue("3400 MHz", 3400000, "±1820 speedo"), + NamedValue("3433 MHz", 3433000, "±???? speedo"), + NamedValue("3466 MHz", 3466000, "±???? speedo"), + NamedValue("3500 MHz", 3500000, "±???? speedo"), }; RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit]; @@ -2294,8 +2300,10 @@ class RamLatenciesSubmenuGui : public MiscGui { this->listElement->addItem(graph); } + const int readTierCount = IsMariko() ? 4 : 3; + this->listElement->addItem(new CompactCategoryHeader("Read Latency")); - for (int i = 0; i < 4; i++) + for (int i = 0; i < readTierCount; i++) addLatencyRow(kTierLabels[i], i, kLatencyRKeys); this->listElement->addItem(new CompactCategoryHeader("Write Latency")); @@ -2436,6 +2444,9 @@ class CpuSubmenuGui : public MiscGui { NamedValue("3333 MHz", 3333000, "±1780 speedo"), NamedValue("3366 MHz", 3366000, "±1800 speedo"), NamedValue("3400 MHz", 3400000, "±1820 speedo"), + NamedValue("3433 MHz", 3433000, "±???? speedo"), + NamedValue("3466 MHz", 3466000, "±???? speedo"), + NamedValue("3500 MHz", 3500000, "±???? speedo"), }; addConfigToggle(HocClkConfigValue_AutoRAMCPUOverclock, "Auto CPU RAM OC"); addConfigButton(HocClkConfigValue_AutoRamCpuCpuOCFreq, "Auto CPU RAM OC CPU clock", ValueRange(0, 0, 1, "", 1), "CPU Clock",