redo timing

This commit is contained in:
souldbminersmwc
2025-09-22 15:45:21 -04:00
parent 342f9dd116
commit a13695f734
5 changed files with 1259 additions and 1291 deletions

View File

@@ -56,13 +56,13 @@ volatile CustomizeTable C = {
* - System instabilities * - System instabilities
* - NAND corruption * - NAND corruption
*/ */
.eristaEmcMaxClock = 2132640, .eristaEmcMaxClock = 2265280,
/* Mariko CPU: /* Mariko CPU:
* - Max Voltage in mV: * - Max Voltage in mV:
* Default voltage: 1120 * Default voltage: 1120
*/ */
.marikoCpuMaxVolt = 1257, .marikoCpuMaxVolt = 1120,
/* Mariko EMC(RAM): /* Mariko EMC(RAM):
* - RAM Clock in kHz: * - RAM Clock in kHz:
@@ -99,23 +99,17 @@ volatile CustomizeTable C = {
.enableEristaCpuUnsafeFreqs = ENABLED, .enableEristaCpuUnsafeFreqs = ENABLED,
.commonGpuVoltOffset = 0, // TODO: Split tRCD, tRP and tRAS into separate timings .commonGpuVoltOffset = 0,
.marikoEmcDvbShift = 4, .t1_tRCD = 4,
.t2_tRP = 5,
.ramTimingPresetOne = 0, .t3_tRAS = 9,
.t4_tRRD = 1,
.ramTimingPresetTwo = 1, .t5_tRFC = 2,
.t6_tRTW = 6,
.ramTimingPresetThree = 0, .t7_tWTR = 4,
.t8_tREFI = 6,
.ramTimingPresetFour = 2, .mem_burst_latency = 2,
.ramTimingPresetFive = 4,
.ramTimingPresetSix = 4, // Keep at 4, most optimal
.ramTimingPresetSeven = 0, // Sets the BL of the ram. Change to 2 to get 1866BL and set to 0 to keep the default 1600BL
// Erista default (HB-MGCH ST timing) // Erista default (HB-MGCH ST timing)
// //
@@ -161,7 +155,7 @@ volatile CustomizeTable C = {
875 /* 691 */, 875 /* 691 */,
900 /* 768 */, 900 /* 768 */,
950 /* 844 */, 950 /* 844 */,
975 /* 921 */, 900 /* 921 */,
910 /* 998 (Disabled by default) */, 910 /* 998 (Disabled by default) */,
950 /* 1075 (Disabled by default) */, 950 /* 1075 (Disabled by default) */,
1000 /* 1152 (Disabled by default) */, 1000 /* 1152 (Disabled by default) */,

View File

@@ -16,96 +16,100 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#define CUST_REV 11 #define CUST_REV 11
#include "oc_common.hpp" #include "oc_common.hpp"
#include "pcv/pcv_common.hpp" #include "pcv/pcv_common.hpp"
namespace ams::ldr::oc { namespace ams::ldr::oc {
#include "mtc_timing_table.hpp" #include "mtc_timing_table.hpp"
enum MtcConfig: u32 { enum MtcConfig: u32 {
AUTO_ADJ_ALL = 0, AUTO_ADJ_ALL = 0,
CUSTOM_ADJ_ALL = 1, CUSTOM_ADJ_ALL = 1,
NO_ADJ_ALL = 2, NO_ADJ_ALL = 2,
CUSTOMIZED_ALL = 4, CUSTOMIZED_ALL = 4,
}; };
using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit];
using CustomizeGpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; using CustomizeGpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit];
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(CustomizeGpuDvfsTable)); static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(CustomizeGpuDvfsTable));
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::DvfsTableEntryLimit); static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::DvfsTableEntryLimit);
constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC
constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC
typedef struct CustomizeTable { typedef struct CustomizeTable {
u8 cust[4] = {'C', 'U', 'S', 'T'}; u8 cust[4] = {'C', 'U', 'S', 'T'};
u32 custRev = CUST_REV; u32 custRev = CUST_REV;
u32 mtcConf = AUTO_ADJ_ALL; u32 mtcConf = AUTO_ADJ_ALL;
u32 commonCpuBoostClock; u32 commonCpuBoostClock;
u32 commonEmcMemVolt; u32 commonEmcMemVolt;
u32 eristaCpuMaxVolt; u32 eristaCpuMaxVolt;
u32 eristaEmcMaxClock; u32 eristaEmcMaxClock;
u32 marikoCpuMaxVolt; u32 marikoCpuMaxVolt;
u32 marikoEmcMaxClock; u32 marikoEmcMaxClock;
u32 marikoEmcVddqVolt; u32 marikoEmcVddqVolt;
u32 marikoCpuUV; u32 marikoCpuUV;
u32 marikoGpuUV; u32 marikoGpuUV;
u32 eristaCpuUV; u32 eristaCpuUV;
u32 eristaGpuUV; u32 eristaGpuUV;
u32 enableMarikoGpuUnsafeFreqs; u32 enableMarikoGpuUnsafeFreqs;
u32 enableEristaGpuUnsafeFreqs; u32 enableEristaGpuUnsafeFreqs;
u32 enableMarikoCpuUnsafeFreqs; u32 enableMarikoCpuUnsafeFreqs;
u32 enableEristaCpuUnsafeFreqs; u32 enableEristaCpuUnsafeFreqs;
u32 commonGpuVoltOffset; u32 commonGpuVoltOffset;
// advanced config
u32 marikoEmcDvbShift; u32 marikoEmcDvbShift;
u32 ramTimingPresetOne;
u32 ramTimingPresetTwo; // advanced config
u32 ramTimingPresetThree; u32 t1_tRCD;
u32 ramTimingPresetFour; u32 t2_tRP;
u32 ramTimingPresetFive; u32 t3_tRAS;
u32 ramTimingPresetSix; u32 t4_tRRD;
u32 ramTimingPresetSeven; u32 t5_tRFC;
// u32 t6_tRTW;
u32 marikoGpuVoltArray[24]; u32 t7_tWTR;
u32 eristaGpuVoltArray[15]; u32 t8_tREFI;
u32 mem_burst_latency;
CustomizeCpuDvfsTable eristaCpuDvfsTable;
CustomizeCpuDvfsTable marikoCpuDvfsTable; u32 marikoGpuVoltArray[24];
CustomizeCpuDvfsTable marikoCpuDvfsTableSLT; u32 eristaGpuVoltArray[15];
CustomizeGpuDvfsTable eristaGpuDvfsTable; CustomizeCpuDvfsTable eristaCpuDvfsTable;
CustomizeGpuDvfsTable eristaGpuDvfsTableSLT; CustomizeCpuDvfsTable marikoCpuDvfsTable;
CustomizeGpuDvfsTable eristaGpuDvfsTableHigh; CustomizeCpuDvfsTable marikoCpuDvfsTableSLT;
CustomizeGpuDvfsTable eristaGpuDvfsTable;
CustomizeGpuDvfsTable marikoGpuDvfsTable; CustomizeGpuDvfsTable eristaGpuDvfsTableSLT;
CustomizeGpuDvfsTable marikoGpuDvfsTableSLT; CustomizeGpuDvfsTable eristaGpuDvfsTableHigh;
CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT;
//EristaMtcTable* eristaMtcTable;
//MarikoMtcTable* marikoMtcTable; CustomizeGpuDvfsTable marikoGpuDvfsTable;
CustomizeGpuDvfsTable eristaGpuDvfsTableUv3UnsafeFreqs; CustomizeGpuDvfsTable marikoGpuDvfsTableSLT;
CustomizeGpuDvfsTable marikoGpuDvfsTableUv3UnsafeFreqs; CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT;
CustomizeCpuDvfsTable marikoCpuDvfsTableUnsafeFreqs; //EristaMtcTable* eristaMtcTable;
CustomizeCpuDvfsTable eristaCpuDvfsTableUnsafeFreqs; //MarikoMtcTable* marikoMtcTable;
CustomizeGpuDvfsTable eristaGpuDvfsTableUv3UnsafeFreqs;
} CustomizeTable; CustomizeGpuDvfsTable marikoGpuDvfsTableUv3UnsafeFreqs;
//static_assert(sizeof(CustomizeTable) == sizeof(u8) * 4 + sizeof(u32) * 10 + sizeof(CustomizeCpuDvfsTable) * 5 + sizeof(void*) * 2); CustomizeCpuDvfsTable marikoCpuDvfsTableUnsafeFreqs;
//static_assert(sizeof(CustomizeTable) == 7000); CustomizeCpuDvfsTable eristaCpuDvfsTableUnsafeFreqs;
extern volatile CustomizeTable C; } CustomizeTable;
//static_assert(sizeof(CustomizeTable) == sizeof(u8) * 4 + sizeof(u32) * 10 + sizeof(CustomizeCpuDvfsTable) * 5 + sizeof(void*) * 2);
//extern volatile EristaMtcTable EristaMtcTablePlaceholder; //static_assert(sizeof(CustomizeTable) == 7000);
//extern volatile MarikoMtcTable MarikoMtcTablePlaceholder;
extern volatile CustomizeTable C;
}
//extern volatile EristaMtcTable EristaMtcTablePlaceholder;
//extern volatile MarikoMtcTable MarikoMtcTablePlaceholder;
}

View File

@@ -16,220 +16,223 @@
* from GCC preprocessor output * from GCC preprocessor output
*/ */
#pragma once
#include "oc_common.hpp"
namespace ams::ldr::oc { #pragma once
#define MAX(A, B) std::max(A, B)
#define MIN(A, B) std::min(A, B)
#define CEIL(A) std::ceil(A)
#define FLOOR(A) std::floor(A)
//Preset One #include "oc_common.hpp"
const std::array<u32, 8> tRCD_values = {18, 17, 16, 15, 14, 13, 12, 11};
const std::array<u32, 8> tRP_values = {18, 17, 16, 15, 14, 13, 12, 11}; namespace ams::ldr::oc {
const std::array<u32, 10> tRAS_values = {42, 36, 34, 32, 30, 28, 26, 24, 22, 20}; #define MAX(A, B) std::max(A, B)
#define MIN(A, B) std::min(A, B)
// Preset Two #define CEIL(A) std::ceil(A)
const std::array<double, 8> tRRD_values = {10, 7.5, 6, 5, 4, 3, 2, 1}; #define FLOOR(A) std::floor(A)
const std::array<double, 5> tFAW_values = {40, 30, 24, 16, 12};
//Preset One
// Preset Three const std::array<u32, 8> tRCD_values = {18, 17, 16, 15, 14, 13, 12, 11};
const std::array<u32, 6> tWR_values = {18, 15, 15, 12, 12, 8}; const std::array<u32, 8> tRP_values = {18, 17, 16, 15, 14, 13, 12, 11};
const std::array<double, 6> tRTP_values = {7.5, 7.5, 6, 6, 4, 4}; const std::array<u32, 10> tRAS_values = {42, 36, 34, 32, 30, 28, 26, 24, 22, 20};
// Preset Four // Preset Two
const std::array<u32, 6> tRFC_values = {140, 120, 100, 80, 70, 60}; const std::array<double, 8> tRRD_values = {10, 7.5, 6, 5, 4, 3, 2, 1};
const std::array<double, 5> tFAW_values = {40, 30, 24, 16, 12};
// Preset Five
const std::array<u32, 10> tWTR_values = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; // Preset Three
const std::array<u32, 6> tWR_values = {18, 15, 15, 12, 12, 8}; // TODO: identify what exactly eos tRTW even is (is it even real?)
// Preset Six const std::array<double, 6> tRTP_values = {7.5, 7.5, 6, 6, 4, 4};
const std::array<u32, 5> tREFpb_values = {488, 976, 1952, 3256, 9999};
// Preset Four
const u32 TIMING_PRESET_ONE = C.ramTimingPresetOne; const std::array<u32, 6> tRFC_values = {140, 120, 100, 80, 70, 60};
const u32 TIMING_PRESET_TWO = C.ramTimingPresetTwo;
const u32 TIMING_PRESET_THREE = C.ramTimingPresetThree; // Preset Five
const u32 TIMING_PRESET_FOUR = C.ramTimingPresetFour; const std::array<u32, 10> tWTR_values = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
const u32 TIMING_PRESET_FIVE = C.ramTimingPresetFive;
const u32 TIMING_PRESET_SIX = C.ramTimingPresetSix; // Preset Six
const u32 TIMING_PRESET_SEVEN = C.ramTimingPresetSeven; const std::array<u32, 6> tREFpb_values = {488, 976, 1952, 3256, 9999, 9999};
// Burst Length // const u32 TIMING_PRESET_ONE = C.ramTimingPresetOne;
const u32 BL = 16; // const u32 TIMING_PRESET_TWO = C.ramTimingPresetTwo;
const u32 TIMING_PRESET_THREE = 0;
// tRFCpb (refresh cycle time per bank) in ns for 8Gb density // const u32 TIMING_PRESET_FOUR = C.ramTimingPresetFour;
const u32 tRFCpb = !TIMING_PRESET_FOUR ? 140 : tRFC_values[TIMING_PRESET_FOUR-1]; // const u32 TIMING_PRESET_FIVE = C.ramTimingPresetFive;
// const u32 TIMING_PRESET_SIX = C.ramTimingPresetSix;
// tRFCab (refresh cycle time all banks) in ns for 8Gb density // const u32 TIMING_PRESET_SEVEN = C.ramTimingPresetSeven;
const u32 tRFCab = !TIMING_PRESET_FOUR ? 280 : 2*tRFCpb;
// Burst Length
// tRAS (row active time) in ns const u32 BL = 16;
const u32 tRAS = !TIMING_PRESET_ONE ? 42 : tRAS_values[TIMING_PRESET_ONE-1];
// tRFCpb (refresh cycle time per bank) in ns for 8Gb density
// tRPpb (row precharge time per bank) in ns const u32 tRFCpb = !C.t5_tRFC ? 140 : tRFC_values[C.t5_tRFC-1];
const u32 tRPpb = !TIMING_PRESET_ONE ? 18 : tRP_values[TIMING_PRESET_ONE-1];
// tRFCab (refresh cycle time all banks) in ns for 8Gb density
// tRPab (row precharge time all banks) in ns const u32 tRFCab = !C.t5_tRFC ? 280 : 2*tRFCpb;
const u32 tRPab = !TIMING_PRESET_ONE ? 21 : tRPpb + 3;
// tRAS (row active time) in ns
// tRC (ACTIVATE-ACTIVATE command period same bank) in ns const u32 tRAS = !C.t3_tRAS ? 42 : tRAS_values[C.t3_tRAS-1];
const u32 tRC = tRPpb + tRAS;
// tRPpb (row precharge time per bank) in ns
// DQS output access time from CK_t/CK_c const u32 tRPpb = !C.t2_tRP ? 18 : tRP_values[C.t2_tRP-1];
const double tDQSCK_min = 1.5;
// DQS output access time from CK_t/CK_c // tRPab (row precharge time all banks) in ns
const double tDQSCK_max = 3.5; const u32 tRPab = !C.t2_tRP ? 21 : tRPpb + 3;
// Write preamble (tCK)
const double tWPRE = 1.8; // tRC (ACTIVATE-ACTIVATE command period same bank) in ns
// Read postamble (tCK) const u32 tRC = tRPpb + tRAS;
const double tRPST = 0.4;
// WRITE command to first DQS transition(max) (tCK) // DQS output access time from CK_t/CK_c
const double tDQSS_max = 1.25; const double tDQSCK_min = 1.5;
// DQ-to-DQS offset(max) (ns) // DQS output access time from CK_t/CK_c
const double tDQS2DQ_max = 0.8; const double tDQSCK_max = 3.5;
// DQS_t, DQS_c to DQ skew total, per group, per access (DBI Disabled) // Write preamble (tCK)
const double tDQSQ = 0.18; const double tWPRE = 1.8;
// Read postamble (tCK)
// Write-to-Read delay const double tRPST = 0.4;
const u32 tWTR = !TIMING_PRESET_FIVE ? 10 : tWTR_values[TIMING_PRESET_FIVE-1]; // WRITE command to first DQS transition(max) (tCK)
const double tDQSS_max = 1.25;
// Internal READ-to-PRE-CHARGE command delay in ns // DQ-to-DQS offset(max) (ns)
const double tRTP = !TIMING_PRESET_THREE ? 7.5 : tRTP_values[TIMING_PRESET_THREE-1]; const double tDQS2DQ_max = 0.8;
// DQS_t, DQS_c to DQ skew total, per group, per access (DBI Disabled)
// write recovery time const double tDQSQ = 0.18;
const u32 tWR = !TIMING_PRESET_THREE ? 18 : tWR_values[TIMING_PRESET_THREE-1];
// Write-to-Read delay
// Read to refresh delay const u32 tWTR = !C.t7_tWTR ? 10 : tWTR_values[C.t7_tWTR-1];
const u32 tR2REF = tRTP + tRPpb;
// Internal READ-to-PRE-CHARGE command delay in ns
// tRCD (RAS-CAS delay) in ns const double tRTP = !TIMING_PRESET_THREE ? 7.5 : tRTP_values[TIMING_PRESET_THREE-1];
const u32 tRCD = !TIMING_PRESET_ONE ? 18 : tRCD_values[TIMING_PRESET_ONE-1];
// write recovery time
// tRRD (Active bank-A to Active bank-B) in ns const u32 tWR = !TIMING_PRESET_THREE ? 18 : tWR_values[TIMING_PRESET_THREE-1];
const double tRRD = !TIMING_PRESET_TWO ? 10. : tRRD_values[TIMING_PRESET_TWO-1];
// Read to refresh delay
// tREFpb (average refresh interval per bank) in ns for 8Gb density const u32 tR2REF = tRTP + tRPpb;
const u32 tREFpb = !TIMING_PRESET_SIX ? 488 : tREFpb_values[TIMING_PRESET_SIX-1];
// tREFab (average refresh interval all 8 banks) in ns for 8Gb density // tRCD (RAS-CAS delay) in ns
// const u32 tREFab = tREFpb * 8; const u32 tRCD = !C.t1_tRCD ? 18 : tRCD_values[C.t1_tRCD-1];
// tPDEX2WR, tPDEX2RD (timing delay from exiting powerdown mode to a write/read command) in ns // tRRD (Active bank-A to Active bank-B) in ns
// const u32 tPDEX2 = 10; const double tRRD = !C.t4_tRRD ? 10. : tRRD_values[C.t4_tRRD-1];
// Exit power-down to next valid command delay
const double tXP = 10; // tREFpb (average refresh interval per bank) in ns for 8Gb density
const u32 tREFpb = !C.t8_tREFI ? 488 : tREFpb_values[C.t8_tREFI-1];
// Delay from valid command to CKE input LOW in ns // tREFab (average refresh interval all 8 banks) in ns for 8Gb density
const double tCMDCKE = 1.75; // const u32 tREFab = tREFpb * 8;
// tACT2PDEN (timing delay from an activate, MRS or EMRS command to power-down entry) in ns // tPDEX2WR, tPDEX2RD (timing delay from exiting powerdown mode to a write/read command) in ns
// Valid clock and CS requirement after CKE input LOW after MRW command // const u32 tPDEX2 = 10;
const u32 tMRWCKEL = 14; // Exit power-down to next valid command delay
const double tXP = 10;
// Valid CS requirement after CKE input LOW
const double tCKELCS = 5; // Delay from valid command to CKE input LOW in ns
const double tCMDCKE = 1.75;
// Valid CS requirement before CKE input HIGH
const double tCSCKEH = 1.75; // tACT2PDEN (timing delay from an activate, MRS or EMRS command to power-down entry) in ns
// Valid clock and CS requirement after CKE input LOW after MRW command
// tXSR (SELF REFRESH exit to next valid command delay) in ns const u32 tMRWCKEL = 14;
const double tXSR = tRFCab + 7.5;
// Valid CS requirement after CKE input LOW
// tCKE (minimum pulse width(HIGH and LOW pulse width)) in ns const double tCKELCS = 5;
const double tCKE = 7.5;
// Valid CS requirement before CKE input HIGH
// Minimum self refresh time (entry to exit) const double tCSCKEH = 1.75;
const u32 tSR = 15;
// tXSR (SELF REFRESH exit to next valid command delay) in ns
// tFAW (Four-bank Activate Window) in ns const double tXSR = tRFCab + 7.5;
const u32 tFAW = !TIMING_PRESET_TWO ? 40 : tFAW_values[TIMING_PRESET_TWO-1];
// tCKE (minimum pulse width(HIGH and LOW pulse width)) in ns
// Valid Clock requirement before CKE Input HIGH in ns const double tCKE = 7.5;
const double tCKCKEH = 1.75;
// Minimum self refresh time (entry to exit)
// p78 The first valid data is available RL × t CK + t DQSCK + t DQSQ const u32 tSR = 15;
//const u32 QUSE = RL + CEIL(tDQSCK_min/tCK_avg + tDQSQ);
// tFAW (Four-bank Activate Window) in ns
namespace pcv::erista { const u32 tFAW = 40;// !TIMING_PRESET_TWO ? 40 : tFAW_values[TIMING_PRESET_TWO-1]; TOGO
// tCK_avg (average clock period) in ns
const double tCK_avg = 1000'000. / C.eristaEmcMaxClock; // Valid Clock requirement before CKE Input HIGH in ns
const double tCKCKEH = 1.75;
// Write Latency
const u32 WL = 14 + TIMING_PRESET_SEVEN; // p78 The first valid data is available RL × t CK + t DQSCK + t DQSQ
// Read Latency //const u32 QUSE = RL + CEIL(tDQSCK_min/tCK_avg + tDQSQ);
const u32 RL = 32 + TIMING_PRESET_SEVEN;
namespace pcv::erista {
// minimum number of cycles from any read command to any write command, irrespective of bank // tCK_avg (average clock period) in ns
const u32 R2W = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST)) + 6; const double tCK_avg = 1000'000. / C.eristaEmcMaxClock;
// Delay Time From WRITE-to-READ // Write Latency
const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg) - 6; const u32 WL = 14 + C.mem_burst_latency;
// Read Latency
// write-to-precharge time for commands to the same bank in cycles const u32 RL = 32 - C.mem_burst_latency;
const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg) - 8;
// minimum number of cycles from any read command to any write command, irrespective of bank
// #_of_rows per die for 8Gb density const u32 R2W = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST)) + 6;
const u32 numOfRows = 65536;
// {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%] // Delay Time From WRITE-to-READ
// emc_clk_period = dram_clk / 2; const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg) - 6;
// 1600 MHz: 5894, but N' set to 6176 (~4.8% margin)
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.eristaEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4); // write-to-precharge time for commands to the same bank in cycles
const u32 REFBW = MIN((u32)65536, REFRESH+64); const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg) - 8;
// Write With Auto Precharge to to Power-Down Entry // #_of_rows per die for 8Gb density
const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6; const u32 numOfRows = 65536;
// {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%]
// Additional time after t XP hasexpired until the MRR commandmay be issued // emc_clk_period = dram_clk / 2;
const double tMRRI = tRCD + 3 * tCK_avg; // 1600 MHz: 5894, but N' set to 6176 (~4.8% margin)
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.eristaEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4);
// tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns const u32 REFBW = MIN((u32)65536, REFRESH+64);
const double tPDEX2MRR = tXP + tMRRI;
} // Write With Auto Precharge to to Power-Down Entry
namespace pcv::mariko { const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6;
// tCK_avg (average clock period) in ns
const double tCK_avg = 1000'000. / C.marikoEmcMaxClock; // Additional time after t XP hasexpired until the MRR commandmay be issued
// Write Latency const double tMRRI = tRCD + 3 * tCK_avg;
const u32 WL = 14 + TIMING_PRESET_SEVEN;
// Read Latency // tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns
const u32 RL = 32 + TIMING_PRESET_SEVEN; const double tPDEX2MRR = tXP + tMRRI;
}
// minimum number of cycles from any read command to any write command, irrespective of bank namespace pcv::mariko {
const u32 R2W = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST)); // tCK_avg (average clock period) in ns
const double tCK_avg = 1000'000. / C.marikoEmcMaxClock;
// Delay Time From WRITE-to-READ // Write Latency
const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg); const u32 WL = 14 + C.mem_burst_latency;
// Read Latency
// write-to-precharge time for commands to the same bank in cycles const u32 RL = 32 - C.mem_burst_latency;
const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg);
// minimum number of cycles from any read command to any write command, irrespective of bank
// Read-To-MRW delay const u32 R2W = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST));
const u32 RTM = RL + BL/2 + CEIL(tDQSCK_max/tCK_avg) + FLOOR(tRPST) + CEIL(7.5/tCK_avg);
// Delay Time From WRITE-to-READ
// Write-To-MRW/MRR delay const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg);
const u32 WTM = WL + 1 + BL/2 + CEIL(7.5/tCK_avg);
// write-to-precharge time for commands to the same bank in cycles
// Read With AP-To-MRW/MRR delay const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg);
const u32 RATM = RTM + CEIL(tRTP/tCK_avg) - 8;
// Read-To-MRW delay
// Write With AP-To-MRW/MRR delay const u32 RTM = RL + BL/2 + CEIL(tDQSCK_max/tCK_avg) + FLOOR(tRPST) + CEIL(7.5/tCK_avg);
const u32 WATM = WTM + CEIL(tWR/tCK_avg);
// Write-To-MRW/MRR delay
// #_of_rows per die for 8Gb density const u32 WTM = WL + 1 + BL/2 + CEIL(7.5/tCK_avg);
const u32 numOfRows = 65536;
// {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%] // Read With AP-To-MRW/MRR delay
// emc_clk_period = dram_clk / 2; const u32 RATM = RTM + CEIL(tRTP/tCK_avg) - 8;
// 1600 MHz: 5894, but N' set to 6176 (~4.8% margin)
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4); // Write With AP-To-MRW/MRR delay
const u32 REFBW = MIN((u32)65536, REFRESH+64); const u32 WATM = WTM + CEIL(tWR/tCK_avg);
// Write With Auto Precharge to to Power-Down Entry // #_of_rows per die for 8Gb density
const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6; const u32 numOfRows = 65536;
// {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%]
// Additional time after t XP hasexpired until the MRR commandmay be issued // emc_clk_period = dram_clk / 2;
const double tMRRI = tRCD + 3 * tCK_avg; // 1600 MHz: 5894, but N' set to 6176 (~4.8% margin)
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4);
// tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns const u32 REFBW = MIN((u32)65536, REFRESH+64);
const double tPDEX2MRR = tXP + tMRRI;
} // Write With Auto Precharge to to Power-Down Entry
} const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6;
// Additional time after t XP hasexpired until the MRR commandmay be issued
const double tMRRI = tRCD + 3 * tCK_avg;
// tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns
const double tPDEX2MRR = tXP + tMRRI;
}
}

View File

@@ -16,330 +16,311 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "pcv.hpp" #include "pcv.hpp"
#include "../mtc_timing_value.hpp" #include "../mtc_timing_value.hpp"
namespace ams::ldr::oc::pcv::erista namespace ams::ldr::oc::pcv::erista
{ {
Result CpuVoltRange(u32 *ptr) Result CpuVoltRange(u32 *ptr)
{ {
u32 min_volt_got = *(ptr - 1); u32 min_volt_got = *(ptr - 1);
for (const auto &mv : CpuMinVolts) for (const auto &mv : CpuMinVolts)
{ {
if (min_volt_got != mv) if (min_volt_got != mv)
continue; continue;
if (!C.eristaCpuMaxVolt) if (!C.eristaCpuMaxVolt)
R_SKIP(); R_SKIP();
PATCH_OFFSET(ptr, C.eristaCpuMaxVolt); PATCH_OFFSET(ptr, C.eristaCpuMaxVolt);
R_SUCCEED(); R_SUCCEED();
} }
R_THROW(ldr::ResultInvalidCpuMinVolt()); R_THROW(ldr::ResultInvalidCpuMinVolt());
} }
Result GpuFreqMaxAsm(u32 *ptr32) Result GpuFreqMaxAsm(u32 *ptr32)
{ {
// Check if both two instructions match the pattern // Check if both two instructions match the pattern
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1); u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1]))) if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
// Both instructions should operate on the same register // Both instructions should operate on the same register
u8 rd = asm_get_rd(ins1); u8 rd = asm_get_rd(ins1);
if (rd != asm_get_rd(ins2)) if (rd != asm_get_rd(ins2))
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
u32 max_clock; u32 max_clock;
switch (C.eristaGpuUV) switch (C.eristaGpuUV)
{ {
case 0: case 0:
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
break; break;
case 1: case 1:
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq; max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq;
break; break;
case 2: case 2:
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHigh)->freq; max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHigh)->freq;
break; break;
case 3: case 3:
if(C.enableEristaGpuUnsafeFreqs) if(C.enableEristaGpuUnsafeFreqs)
{ {
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableUv3UnsafeFreqs)->freq; max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableUv3UnsafeFreqs)->freq;
} }
else else
{ {
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
} }
break; break;
default: default:
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
break; break;
} }
u32 asm_patch[2] = { u32 asm_patch[2] = {
asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd), asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd),
asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)}; asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)};
PATCH_OFFSET(ptr32, asm_patch[0]); PATCH_OFFSET(ptr32, asm_patch[0]);
PATCH_OFFSET(ptr32 + 1, asm_patch[1]); PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
R_SUCCEED(); R_SUCCEED();
} }
Result GpuFreqPllLimit(u32 *ptr) Result GpuFreqPllLimit(u32 *ptr)
{ {
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr); clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
// All zero except for freq // All zero except for freq
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++)
{ {
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
} }
// Double the max clk simply // Double the max clk simply
u32 max_clk = entry->freq * 2; u32 max_clk = entry->freq * 2;
entry->freq = max_clk; entry->freq = max_clk;
R_SUCCEED(); R_SUCCEED();
} }
void MemMtcTableAutoAdjust(EristaMtcTable *table) void MemMtcTableAutoAdjust(EristaMtcTable *table)
{ {
if (C.mtcConf != AUTO_ADJ_ALL) if (C.mtcConf != AUTO_ADJ_ALL)
return; return;
#define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \
TABLE->burst_regs.PARAM = VALUE; \ TABLE->burst_regs.PARAM = VALUE; \
TABLE->shadow_regs_ca_train.PARAM = VALUE; \ TABLE->shadow_regs_ca_train.PARAM = VALUE; \
TABLE->shadow_regs_quse_train.PARAM = VALUE; \ TABLE->shadow_regs_quse_train.PARAM = VALUE; \
TABLE->shadow_regs_rdwr_train.PARAM = VALUE; TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC)); WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab)); WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb)); WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS)); WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb)); WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb));
WRITE_PARAM_ALL_REG(table, emc_r2w, R2W); WRITE_PARAM_ALL_REG(table, emc_r2w, R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP)); WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD)); 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_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD)); WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH); WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4); WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE_CEIL(tXP)); WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE_CEIL(tXP));
WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(tXP)); WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(tXP));
WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE_CEIL(tCMDCKE)); WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE_CEIL(tCMDCKE));
WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE_CEIL(tMRWCKEL)); WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE_CEIL(tMRWCKEL));
WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE_CEIL(tCMDCKE)); WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE_CEIL(tCMDCKE));
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN); WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE_CEIL(tCKELCS)); WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE_CEIL(tCKELCS));
WRITE_PARAM_ALL_REG(table, emc_pdex2cke, GET_CYCLE_CEIL(tCSCKEH)); WRITE_PARAM_ALL_REG(table, emc_pdex2cke, GET_CYCLE_CEIL(tCSCKEH));
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR)); WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR));
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tCKE)); WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tCKE));
WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR)); WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR));
WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tCKE)); WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tCKE));
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW)); WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab)); WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_tclkstable, GET_CYCLE_CEIL(tCKCKEH)); WRITE_PARAM_ALL_REG(table, emc_tclkstable, GET_CYCLE_CEIL(tCKCKEH));
WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE_CEIL(tCKE) + 8); WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE_CEIL(tCKE) + 8);
WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW); WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW);
#define WRITE_PARAM_BURST_MC_REG(TABLE, PARAM, VALUE) TABLE->burst_mc_regs.PARAM = VALUE; #define WRITE_PARAM_BURST_MC_REG(TABLE, PARAM, VALUE) TABLE->burst_mc_regs.PARAM = VALUE;
constexpr u32 MC_ARB_DIV = 4; constexpr u32 MC_ARB_DIV = 4;
constexpr u32 MC_ARB_SFA = 2; constexpr u32 MC_ARB_SFA = 2;
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2; table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA; table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1; table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2; table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1; table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1; table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV); table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV); table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
// table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA; // table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA;
// table->burst_mc_regs.mc_emem_arb_timing_w2w = CEIL(table->burst_regs.emc_wext / MC_ARB_DIV) - 1 + MC_ARB_SFA; // table->burst_mc_regs.mc_emem_arb_timing_w2w = CEIL(table->burst_regs.emc_wext / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA; table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA; table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV); table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV);
// table->burst_mc_regs.mc_emem_arb_timing_ccdmw = CEIL(tCCDMW / MC_ARB_DIV) -1 + MC_ARB_SFA; // table->burst_mc_regs.mc_emem_arb_timing_ccdmw = CEIL(tCCDMW / MC_ARB_DIV) -1 + MC_ARB_SFA;
} }
void MemMtcTableCustomAdjust(EristaMtcTable *table) void MemMtcTableCustomAdjust(EristaMtcTable *table)
{ {
if (C.mtcConf != CUSTOM_ADJ_ALL) if (C.mtcConf != CUSTOM_ADJ_ALL)
return; return;
constexpr u32 MC_ARB_DIV = 4; constexpr u32 MC_ARB_DIV = 4;
constexpr u32 MC_ARB_SFA = 2; constexpr u32 MC_ARB_SFA = 2;
if (TIMING_PRESET_ONE) WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
{ WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS));
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC)); WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS)); WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb)); WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab)); WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD)); WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR)); table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV - 2);
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV - 1);
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV - 2); table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV - 1 + MC_ARB_SFA);
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV - 1); table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV - 2);
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV - 1 + MC_ARB_SFA);
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV - 2); WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
} WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
if (TIMING_PRESET_TWO) table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
{ table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1; WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1; WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
}
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV);
if (TIMING_PRESET_THREE) table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
{
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN); WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV); WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
} table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV);
if (TIMING_PRESET_FOUR)
{ WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb)); table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV); WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW);
} WRITE_PARAM_ALL_REG(table, emc_r2w, R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
if (TIMING_PRESET_FIVE) WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
{ WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA; table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
} table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
if (TIMING_PRESET_SIX) u32 DA_TURNS = 0;
{ DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16; // R2W TURN
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH); DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; // W2R TURN
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4); WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_turns, DA_TURNS);
WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW); u32 DA_COVERS = 0;
} u8 R_COVER = (table->burst_mc_regs.mc_emem_arb_timing_rap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2;
u8 W_COVER = (table->burst_mc_regs.mc_emem_arb_timing_wap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2;
if (TIMING_PRESET_SEVEN) DA_COVERS |= (u8)(table->burst_mc_regs.mc_emem_arb_timing_rc / 2); // RC COVER
{ DA_COVERS |= (R_COVER << 8); // RCD_R COVER
WRITE_PARAM_ALL_REG(table, emc_r2w, R2W); DA_COVERS |= (W_COVER << 16); // RCD_W COVER
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_covers, DA_COVERS);
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); }
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
Result MemFreqMtcTable(u32 *ptr)
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV); {
table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA; u32 khz_list[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800};
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA; u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
}
// Generate list for mtc table pointers
u32 DA_TURNS = 0; EristaMtcTable *table_list[khz_list_size];
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16; // R2W TURN for (u32 i = 0; i < khz_list_size; i++)
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; // W2R TURN {
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_turns, DA_TURNS); u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable);
u32 DA_COVERS = 0; table_list[i] = reinterpret_cast<EristaMtcTable *>(table);
u8 R_COVER = (table->burst_mc_regs.mc_emem_arb_timing_rap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2; R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
u8 W_COVER = (table->burst_mc_regs.mc_emem_arb_timing_wap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2; R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
DA_COVERS |= (u8)(table->burst_mc_regs.mc_emem_arb_timing_rc / 2); // RC COVER }
DA_COVERS |= (R_COVER << 8); // RCD_R COVER
DA_COVERS |= (W_COVER << 16); // RCD_W COVER if (C.eristaEmcMaxClock <= EmcClkOSLimit)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_covers, DA_COVERS); R_SKIP();
}
// Make room for new mtc table, discarding useless 40.8 MHz table
Result MemFreqMtcTable(u32 *ptr) // 40800 overwritten by 68000, ..., 1331200 overwritten by 1600000, leaving table_list[0] not overwritten
{ for (u32 i = khz_list_size - 1; i > 0; i--)
u32 khz_list[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800}; std::memcpy(static_cast<void *>(table_list[i]), static_cast<void *>(table_list[i - 1]), sizeof(EristaMtcTable));
u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
MemMtcTableAutoAdjust(table_list[0]);
// Generate list for mtc table pointers PATCH_OFFSET(ptr, C.eristaEmcMaxClock);
EristaMtcTable *table_list[khz_list_size];
for (u32 i = 0; i < khz_list_size; i++) // Handle customize table replacement
{ // if (C.mtcConf == CUSTOMIZED_ALL) {
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable); // MemMtcCustomizeTable(table_list[0], const_cast<EristaMtcTable *>(C.eristaMtcTable));
table_list[i] = reinterpret_cast<EristaMtcTable *>(table); //}
R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); R_SUCCEED();
} }
if (C.eristaEmcMaxClock <= EmcClkOSLimit) Result MemFreqMax(u32 *ptr)
R_SKIP(); {
if (C.eristaEmcMaxClock <= EmcClkOSLimit)
// Make room for new mtc table, discarding useless 40.8 MHz table R_SKIP();
// 40800 overwritten by 68000, ..., 1331200 overwritten by 1600000, leaving table_list[0] not overwritten
for (u32 i = khz_list_size - 1; i > 0; i--) PATCH_OFFSET(ptr, C.eristaEmcMaxClock);
std::memcpy(static_cast<void *>(table_list[i]), static_cast<void *>(table_list[i - 1]), sizeof(EristaMtcTable));
R_SUCCEED();
MemMtcTableAutoAdjust(table_list[0]); }
PATCH_OFFSET(ptr, C.eristaEmcMaxClock);
void Patch(uintptr_t mapped_nso, size_t nso_size)
// Handle customize table replacement {
// if (C.mtcConf == CUSTOMIZED_ALL) { u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
// MemMtcCustomizeTable(table_list[0], const_cast<EristaMtcTable *>(C.eristaMtcTable)); u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
//}
PatcherEntry<u32> patches[] = {
R_SUCCEED(); {"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, CpuCvbDefaultMaxFreq},
} {"CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn},
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq},
Result MemFreqMax(u32 *ptr) {"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn},
{ {"GPU Freq PLL", &GpuFreqPllLimit, 1, nullptr, GpuClkPllLimit},
if (C.eristaEmcMaxClock <= EmcClkOSLimit) {"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit},
R_SKIP(); {"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit},
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit},
PATCH_OFFSET(ptr, C.eristaEmcMaxClock); {"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS},
};
R_SUCCEED();
} for (uintptr_t ptr = mapped_nso;
ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable);
void Patch(uintptr_t mapped_nso, size_t nso_size) ptr += sizeof(u32))
{ {
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); for (auto &entry : patches)
{
PatcherEntry<u32> patches[] = { if (R_SUCCEEDED(entry.SearchAndApply(ptr32)))
{"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, CpuCvbDefaultMaxFreq}, break;
{"CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn}, }
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq}, }
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn},
{"GPU Freq PLL", &GpuFreqPllLimit, 1, nullptr, GpuClkPllLimit}, for (auto &entry : patches)
{"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit}, {
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit}, LOGGING("%s Count: %zu", entry.description, entry.patched_count);
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit}, if (R_FAILED(entry.CheckResult()))
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS}, CRASH(entry.description);
}; }
}
for (uintptr_t ptr = mapped_nso;
ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); }
ptr += sizeof(u32))
{
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
for (auto &entry : patches)
{
if (R_SUCCEEDED(entry.SearchAndApply(ptr32)))
break;
}
}
for (auto &entry : patches)
{
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
if (R_FAILED(entry.CheckResult()))
CRASH(entry.description);
}
}
}