From 2f21f23266f678c8dc1ce5fe9c34b7adfff635c8 Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Mon, 11 May 2026 19:35:48 +0200 Subject: [PATCH] experimental: add improved dvb table (+ conversion script) --- .../loader/source/oc/pcv/pcv_mariko.cpp | 20 +- Source/scripts/convert_dvb.cpp | 95 ++++++++ Source/scripts/convert_dvb.py | 112 +++++++++ dist/README.md | 214 ------------------ dist/atmosphere/kips/hoc.kip | Bin 343116 -> 343180 bytes 5 files changed, 217 insertions(+), 224 deletions(-) create mode 100644 Source/scripts/convert_dvb.cpp create mode 100644 Source/scripts/convert_dvb.py delete mode 100644 dist/README.md diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp index 977ac881..7c6bbaf0 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -820,16 +820,16 @@ namespace ams::ldr::hoc::pcv::mariko { static_cast((v)[1]), \ static_cast((v)[2]) DvbEntry emcDvbTableNew[] = { - { 204000, { 637, 637, 637, } }, - { 1331200, { 650, 637, 637, } }, - { 1600000, { 675, 650, 637, } }, - { 1866000, { DVB(DvbVolt(700, 675, 650)) } }, - { 2133000, { DVB(DvbVolt(725, 700, 675)) } }, - { 2400000, { DVB(DvbVolt(750, 725, 700)) } }, - { 2666000, { DVB(DvbVolt(775, 750, 725)) } }, - { 2933000, { DVB(DvbVolt(800, 775, 750)) } }, - { 3200000, { DVB(DvbVolt(800, 800, 775)) } }, - { 0xFFFFFFFF, { } }, + { 204000, { 637, 637, 637, }, }, + { 1331200, { 650, 637, 637, }, }, + { 1600000, { 675, 650, 637, }, }, + { 1866000, { DVB(DvbVolt( 700, 675, 650)) }, }, + { 2133000, { DVB(DvbVolt( 725, 700, 675)) }, }, + { 2400000, { DVB(DvbVolt( 750, 725, 700)) }, }, + { 2666000, { DVB(DvbVolt( 850, 825, 800)) }, }, + { 2933000, { DVB(DvbVolt( 950, 925, 900)) }, }, + { 3200000, { DVB(DvbVolt(1050, 1025, 1000)) }, }, + { 0xFFFFFFFF, { }, }, }; #undef DVB diff --git a/Source/scripts/convert_dvb.cpp b/Source/scripts/convert_dvb.cpp new file mode 100644 index 00000000..e09f609f --- /dev/null +++ b/Source/scripts/convert_dvb.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + +typedef uint32_t u32; +typedef int32_t s32; + +struct DvbEntry { + u32 freq; + u32 volts[3]; +}; + +DvbEntry oldDvbTable[] = { + { 204000, { 637, 637, 637, }, }, + { 1331200, { 650, 637, 637, }, }, + { 1600000, { 675, 650, 637, }, }, + { 1866000, { 700, 675, 650, }, }, + { 2133000, { 725, 700, 675, }, }, + { 2400000, { 750, 725, 700, }, }, + { 2666000, { 775, 750, 725, }, }, + { 2933000, { 800, 775, 750, }, }, + { 3200000, { 800, 800, 775, }, }, +}; + +DvbEntry newDvbTable[] = { + { 204000, { 637, 637, 637, }, }, + { 1331200, { 650, 637, 637, }, }, + { 1600000, { 675, 650, 637, }, }, + { 1866000, { 700, 675, 650, }, }, + { 2133000, { 725, 700, 675, }, }, + { 2400000, { 750, 725, 700, }, }, + { 2666000, { 850, 825, 800, }, }, + { 2933000, { 950, 925, 900, }, }, + { 3200000, { 1050, 1025, 1000, }, }, +}; + +constexpr u32 DvbTableSize = std::size(oldDvbTable); + +u32 PrintAndScan(const char *message) { + u32 scanV; + printf("%s: ", message); + scanf("%i", &scanV); + return scanV; +} + +u32 GetProcessId(u32 speedo) { + if (speedo <= 1597) { + return 0; + } + + if (speedo <= 1708) { + return 1; + } + + /* >= 1709. */ + return 2; +} + +u32 GetVoltageAndIndex(u32 dvbShift, u32 emc, u32 processId, DvbEntry *dvbTable, u32 &index) { + for (u32 i = 0; i < DvbTableSize - 1; ++i) { + if (emc < dvbTable[i].freq || emc >= dvbTable[i + 1].freq) { + continue; + } + + index = i; + return dvbTable[i].volts[processId] + (25 * dvbShift); + } + + return 0; +} + +s32 GetShift(u32 oldVoltage, u32 processId, DvbEntry *dvbTable, u32 index) { + return (oldVoltage - dvbTable[index].volts[processId]) / 25; +} + +int main() { + u32 oldDvb = PrintAndScan("Enter old dvb shift"); + u32 emcMaxMhz = PrintAndScan("Enter max ram freq (MHz)"); + u32 speedo = PrintAndScan("Enter soc speedo"); + + u32 emcMaxKhz = emcMaxMhz * 1000; + u32 processId = GetProcessId(speedo); + + u32 tableIndex = 0; + u32 oldVoltage = GetVoltageAndIndex(oldDvb, emcMaxKhz, processId, oldDvbTable, tableIndex); + + if (oldVoltage == 0 || tableIndex == 0) { + printf("Invalid values!\n"); + return -1; + } + + s32 newShift = GetShift(oldVoltage, processId, newDvbTable, tableIndex); + + printf("New dvb table shift: %d", newShift); +} diff --git a/Source/scripts/convert_dvb.py b/Source/scripts/convert_dvb.py new file mode 100644 index 00000000..b6b59d17 --- /dev/null +++ b/Source/scripts/convert_dvb.py @@ -0,0 +1,112 @@ +from dataclasses import dataclass +from typing import List + + +u32 = int +s32 = int + + +@dataclass +class DvbEntry: + freq: u32 + volts: List[u32] + + +old_dvb_table = [ + DvbEntry(204000, [637, 637, 637]), + DvbEntry(1331200, [650, 637, 637]), + DvbEntry(1600000, [675, 650, 637]), + DvbEntry(1866000, [700, 675, 650]), + DvbEntry(2133000, [725, 700, 675]), + DvbEntry(2400000, [750, 725, 700]), + DvbEntry(2666000, [775, 750, 725]), + DvbEntry(2933000, [800, 775, 750]), + DvbEntry(3200000, [800, 800, 775]), +] + +new_dvb_table = [ + DvbEntry(204000, [637, 637, 637]), + DvbEntry(1331200, [650, 637, 637]), + DvbEntry(1600000, [675, 650, 637]), + DvbEntry(1866000, [700, 675, 650]), + DvbEntry(2133000, [725, 700, 675]), + DvbEntry(2400000, [750, 725, 700]), + DvbEntry(2666000, [850, 825, 800]), + DvbEntry(2933000, [950, 925, 900]), + DvbEntry(3200000, [1050, 1025, 1000]), +] + +DVB_TABLE_SIZE = len(old_dvb_table) + + +def print_and_scan(message: str) -> u32: + return int(input(f"{message}: ")) + + +def get_process_id(speedo: u32) -> u32: + if speedo <= 1597: + return 0 + + if speedo <= 1708: + return 1 + + # >= 1709 + return 2 + + +def get_voltage_and_index( + dvb_shift: u32, + emc: u32, + process_id: u32, + dvb_table: List[DvbEntry], +): + for i in range(DVB_TABLE_SIZE - 1): + if emc < dvb_table[i].freq or emc >= dvb_table[i + 1].freq: + continue + + voltage = dvb_table[i].volts[process_id] + (25 * dvb_shift) + return voltage, i + + return 0, 0 + + +def get_shift( + old_voltage: u32, + process_id: u32, + dvb_table: List[DvbEntry], + index: u32, +) -> s32: + return (old_voltage - dvb_table[index].volts[process_id]) // 25 + + +def main(): + old_dvb = print_and_scan("Enter old dvb shift") + emc_max_mhz = print_and_scan("Enter max ram freq (MHz)") + speedo = print_and_scan("Enter soc speedo") + + emc_max_khz = emc_max_mhz * 1000 + process_id = get_process_id(speedo) + + old_voltage, table_index = get_voltage_and_index( + old_dvb, + emc_max_khz, + process_id, + old_dvb_table, + ) + + if old_voltage == 0 or table_index == 0: + print("Invalid values!") + return + + new_shift = get_shift( + old_voltage, + process_id, + new_dvb_table, + table_index, + ) + + print(f"New dvb table shift: {new_shift}") + + +if __name__ == "__main__": + main() diff --git a/dist/README.md b/dist/README.md deleted file mode 100644 index ff3e6be6..00000000 --- a/dist/README.md +++ /dev/null @@ -1,214 +0,0 @@ - -
- -logo - ---- - -![License: GPL-2.0](https://img.shields.io/badge/GPL--2.0-red?style=for-the-badge) -![Nintendo Switch](https://img.shields.io/badge/Nintendo_Switch-E60012?style=for-the-badge\&logo=nintendo-switch\&logoColor=white) -[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge\&logo=discord\&logoColor=white)](https://dsc.gg/horizonoc) -![VSCode](https://img.shields.io/badge/VSCode-0078D4?style=for-the-badge\&logo=visual%20studio%20code\&logoColor=white) -![Made with Notepad++](assets/np++.png?raw=true) -![C++](https://img.shields.io/badge/C%2B%2B-00599C?style=for-the-badge\&logo=c%2B%2B\&logoColor=white) -![Downloads](https://img.shields.io/github/downloads/Horizon-OC/Horizon-OC/total.svg?style=for-the-badge) - ---- - -
- -## ⚠️ Disclaimer - -> **THIS TOOL CAN BE DANGEROUS IF MISUSED. PROCEED WITH CAUTION.** -> Due to the design of Horizon OS, **overclocking RAM can cause NAND OR SD CORRUPTION.** -> Ensure you have a **full NAND, PROINFO, EMUMMC and SD backup** before proceeding. - ---- - -## About - -**Horizon OC** is an open-source overclocking tool for Nintendo Switch consoles running **Atmosphere custom firmware**. -It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration tools. - ---- - -## Default clocks - -* **CPU:** Up to 1963MHz (Mariko) / 1785MHz (Erista) -* **GPU:** Up to 1075MHz (Mariko) / 921MHz (Erista) -* **RAM:** Up to 1866/2133MHz (Mariko) / 1600MHz (Erista) -* Over/undervolting support -* Built-in configurator -* Compatible with most homebrew - -> It is recommended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output - ---- - -## Installation - -1. Ensure you have the latest versions of - - * [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) - * [Ultrahand Overlay](https://github.com/ppkantorski/Ultrahand-Overlay) -2. Download and extract the **Horizon OC Package** to the root of your SD card. -3. If using **Hekate**, edit `hekate_ipl.ini` to include: - - ``` - kip1=atmosphere/kips/hoc.kip - ``` - - *(No changes needed if using fusee.)* - ---- - -## Configuration - -1. Open the Horizon OC Overlay -2. Open the settings menu -3. Adjust your overclocking settings as desired. A helpful guide can be found [here.](https://rentry.co/mariko#oc-settings-for-horizon-oc) -4. Click **Save KIP Settings** to apply your configuration. - ---- - -## Building from Source - -Refer to COMPILATION.md - ---- -## Clock table - -### MEM clocks (mhz) - -* 3200 → max on mariko, JEDEC. -* 3166 -* 3133 -* 3100 -* 3066 -* 3033 -* 3000 -* 2966 -* 2933 → JEDEC. -* 2900 -* 2866 -* 2833 -* 2800 -* 2766 -* 2733 -* 2700 -* 2666 → JEDEC. -* 2633 -* 2600 -* 2566 -* 2533 -* 2500 -* 2466 -* 2433 -* 2400 → max on erista, JEDEC. -* 2366 -* 2333 -* 2300 -* 2266 -* 2233 -* 2200 -* 2166 -* 2133 → Mariko JEDEC standard max (4266 Modules) -* 2100 -* 2066 -* 2033 -* 2000 -* 1996 → JEDEC standard -* 1966 -* 1933 -* 1900 -* 1866 → Mariko JEDEC standard max (3733 Modules) -* 1833 -* 1800 -* 1766 -* 1733 -* 1700 -* 1666 -* 1633 -* 1600 → official docked, boost mode, Erista JEDEC standard max (3200 Modules), JEDEC. -* 1331 → official handheld, JEDEC. -* 1065 -* 800 -* 665 - -### CPU clocks (mhz) -* 2703 → mariko absolute max, dangerous -* 2601 → unsafe -* 2499 -* 2397 → mariko safe max with UV (low speedo) -* 2295 -* 2193 -* 2091 -* 1963 → mariko no UV max clock -* 1887 -* 1785 → erista no UV max clock, boost mode -* 1683 -* 1581 -* 1428 -* 1326 -* 1224 → sdev oc -* 1122 -* 1020 → official docked & handheld -* 918 -* 816 -* 714 -* 612 → sleep mode - -### GPU clocks (mhz) -* 1536 → absolute max clock on mariko. very dangerous -* 1459 -* 1382 -* 1305 -* 1267 → NVIDIA T214(mariko) rating -* 1228 → mariko High UV safe clock -* 1152 → mariko hiOpt-15mV max clock -* 1075 → mariko hiOpt max clock. absolute max clock on erista. very dangerous -* 998 → NVIDIA T210 (erista) rating -* 960 (erista only) → erista high uv/hiOpt-15mV safe max clock -* 921 → erista no UV max clock -* 844 -* 768 → official docked -* 691 -* 614 -* 537 -* 460 → max handheld -* 384 → official handheld -* 307 → official handheld -* 230 -* 153 -* 76 → boost mode - -**Notes:** -1. On Erista, CPU in handheld is capped to 1581MHz -2. GPU overclock is capped at 460MHz on erista in handheld -3. On Mariko, cap with hiOpt is 614MHz, with hiOpt-15mV it is 691MHz and with High UV it's 768MHz -4. Clocks higher than 768MHz on erista need the official charger is plugged in. - ---- - -## Credits -* **Lightos's Cat** - Cat -* **Souldbminer** - hoc-clk and loader development -* **Lightos** - Loader patches development, hoc-clk development, guides -* **TDRR** - HOC Logo Design -* **tetetete-ctrl** - Website design -* **SciresM** - Atmosphere CFW -* **CTCaer** - L4T, Hekate, proper RAM timings -* **KazushiMe** - Switch OC Suite -* **Hanai3bi (Meha)** - Switch OC Suite, EOS, sys-clk-eos -* **NaGaa95** - L4T-OC kernel, Status Monitor fork -* **B3711 (halop)** - EOS, contributions -* **sys-clk team (m4xw, p-sam, natinusala)** - sys-clk -* **Dominatorul** - Soctherm driver, guides, general help -* **ppkantorski** - Ultrahand sys-clk & Status Monitor fork -* **MasaGratoR and ZachyCatGames** - General help -* **MasaGratoR** - Status Monitor & Display Refresh Rate driver -* **Dominatorul, Samybigio, Arcdelta, Miki, Happy, Winnerboi77, Blaise, Alvise, agjeococh, frost, letum00, and Xenshen** - Testing -* **Samybigio2011, Miki** - Italian translations -* **angelblaster** - Korean translations -* **q1332348216-glitch** - Chinese translations -* **Nvidia** - [Tegra X1 Technical Reference Manual](https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual), soctherm driver, L4T diff --git a/dist/atmosphere/kips/hoc.kip b/dist/atmosphere/kips/hoc.kip index d00cad7c983b11966be50072a5713e97f0ddd01b..f437d3832d4edd29d7212a1b625c31d4e05def58 100644 GIT binary patch delta 15919 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 bQIl1?`Q_C@NUd6x6%ddAQ+GWpU?BY;#*<3# delta 15760 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