Přeskočit na obsah

Hardwarové limity: hodiny, SPI a displej

Jak spolu souvisí jádrový clock, periferní clock a display SPI na deskách RP2, kde jsou skutečné stropy a jak testovat, když je překračujete. Napsáno z měření na PicoPadu (RP2040) a na PicoPadu s čipem vyměněným za RP2350 (Pico 2).

Doplňuje HARDWARE.md (rozpočet RAM, nasazení) a FEATURES.md §18 (rychlý vs. přenositelný renderer, kam vlastně mizí FPS).


1. Řetězec hodin (co řídí co)

Sekce “1. Řetězec hodin (co řídí co)”
PLL_SYS ──> clk_sys ──> jádra CPU
└─────> clk_peri ──> SPI, UART, … (CircuitPython: clk_peri = clk_sys, neděleno)
XIP/QMI flash clock je odvozen také z clk_sys (samostatná dělička)
PLL_USB ──> 48 MHz USB (nezávislé — USB-CDC REPL není overclockingem ovlivněn)

Dva důsledky, které řídí vše níže:

  1. clk_peri následuje clk_sys. Na CircuitPythonu je periferní clock systémovým clockem, neděleno. Takže změna jádrového clocku mění vstupní clock SPI — a tedy i clock displeje. Nemůžete ladit jedno bez přemýšlení o druhém.
  2. Výchozí clk_sys se liší podle čipu: RP2040 = 125 MHz, RP2350 = 150 MHz. Konstanta vyladěná pro jeden čip dopadne na druhém jinak (viz §3).

2. Jak je odvozen clock display SPI

Sekce “2. Jak je odvozen clock display SPI”

Displej je ST7789 na čtyřvodičové sběrnici SPI. Jeho clock pochází z periferie PL022 SPI, která dělí clk_peri sudým celým číslem (CPSDVSR × (1+SCR)), a SDK vybírá děličku tak, aby skutečný clock byl nejvyšší dosažitelný, který NEPŘEKROČÍ váš požadavek:

actual_spi_hz = clk_peri / even_divider # největší výsledek <= požadovaný baudrate

Baudrate požadujete v board.c (common_hal_fourwire_fourwire_construct(... baudrate ...)). Požadavek je strop, ne přesná hodnota — pokud přesná hodnota není sudým dělením clk_peri, dostanete tu nejbližší nižší.

Propočtené příklady (ve všech případech požadavek 62,5 MHz):

clk_peri (= clk_sys)sudé dělitele kolem 62,5vybránoskutečné SPI
125 MHz (RP2040 stock)/2 = 62,5/262,5 MHz (přesně)
150 MHz (RP2350 stock)/2 = 75 (> 62,5, zamítnuto), /4 = 37,5/437,5 MHz
200 MHz/2 = 100 (zam.), /4 = 50/450 MHz
225 MHz/2 = 112,5 (zam.), /4 = 56,25/456,25 MHz
250 MHz/2 = 125 (zam.), /4 = 62,5/462,5 MHz (přesně)

Klíčová past: stejný požadavek 62,5 MHz dá 62,5 na RP2040 (125/2), ale jen 37,5 na stock-150 MHz RP2350 (protože 150/2 = 75 přestřelí a zaokrouhlí dolů na 150/4). Abyste dostali dobrý display clock na RP2350, musíte zvolit clk_sys, jehož sudá dělení dopadnou tam, kde chcete — např. 250 MHz → /4 = přesně 62,5 MHz, maximum v rámci specifikace (viz §3, §4).

Abyste dostali clock SPI, musíte požadovat request = actual nebo vyšší, ale pod 2×actual; bezpečné pravidlo je požadovat přesně clock, který chcete a ověřit, kterou děličku vybral.


3. Limit z datasheetu (a jeho překračování)

Sekce “3. Limit z datasheetu (a jeho překračování)”

Datasheet ST7789 udává sériový zápisový clock na tSCYCW = 16 ns min → max 62,5 MHz (čtení je mnohem pomalejší, ~6,6 MHz, ale displej je zde jen pro zápis). Takže 62,5 MHz je strop v rámci specifikace pro posílání pixelů.

V praxi panel na dané desce často běží nad specifikací:

  • Na tomto PicoPadu 75 MHz SPI (RP2350 na 150 MHz, /2) vytvořilo čistý obraz — ~20 % nad specifikací, na tomto kusu funguje při pokojové teplotě.
  • „Funguje tady” není „garantováno všude”: clocking nad specifikací může selhat na jiném panelu, při teplotních extrémech, při jiném napětí nebo na delší/horší kabeláži. Berte to jako experiment na konkrétním kusu, ne jako nasaditelný výchozí stav.

Protože PL022 dělí jen sudými celými čísly, z daného clk_sys máte obvykle jen dvě volby ohraničující specifikaci — např. ze 150 MHz: 75 (nad) nebo 37,5 (hluboko pod), nic na 62,5. Volba clk_sys je způsob, jak trefit dobrý clock v rámci specifikace (250 → 62,5).


Naměřený závěr na této desce: overclocking je ČISTĚ NEGATIVNÍ — nedělejte to. Zní to lákavě (většina práce v picogame je vázaná na CPU/MicroPython a 250 MHz by dokonce trefilo SPI na přesných 62,5 MHz v rámci specifikace), ale na reálném hardwaru každý režim rendereru vyšel ~2× pomaleji overclocknutý než na stock 150 MHz:

režimstock 150 MHzoverclock 225 MHz
HEAVY (vázáno na CPU/blit)~31 fps~15 fps
STRESS (celosnímkové, vázáno na SPI)~44 fps~16 fps
výchozí (dirty-rect)~95 fps~42 fps

Proč to selhává: zvýšení clk_sys bez přeladění flash timingu QMI/XIP způsobí, že se kód vykonává z flash s wait-states. Jádro tiká rychleji, ale propustnost instrukcí klesá — a ta ztráta převáží zisk z clocku. CircuitPythonové set_sys_clock_khz (ať přes microcontroller.cpu.frequency nebo volání board_init) flash nepřeladí; návrhy, které úspěšně overclockují (např. PicoDVI na 252 MHz), spouští svou horkou smyčku z RAM, ne z XIP. Přeladění timingu QMI pro nový clock je specifické pro daný flash čip a háklivé, a navíc 250 MHz nezávisle rozházelo displej (§4b) — takže praktická odpověď je zůstaňte na stock clocku a rychlost displeje získejte z děličky SPI (§2/§3).

Poznámky níže jsou ponechány, protože stále platí, pokud byste někdy overclockovali (a ta dvě pravidla jsou způsob, jak se vyhnout zničení displeje, než to zjistíte).

Dvě těžce vydobytá pravidla

Sekce “Dvě těžce vydobytá pravidla”

(a) Nastavte clock při BOOTU, v board.c board_init(), PŘED konstrukcí display SPI — NE za běhu. Změna microcontroller.cpu.frequency za běhu poškodí živý ST7789: skok napětí VREG (CircuitPython zvyšuje napětí jádra pro >133 MHz) a glitch při rekonfiguraci PLL naruší clk_peri, zatímco je panel uprostřed transakce, a rozhází ho. Naměřeno: změna za běhu na jakoukoli hodnotu ≥133 MHz rozházela displej; ≤120 MHz (bez změny VREG) bylo v pořádku. Provedení v board_init před existencí displeje glitch zcela odstraní:

#include "hardware/clocks.h"
#include "hardware/vreg.h"
#include "hardware/timer.h"
void board_init(void) {
vreg_set_voltage(VREG_VOLTAGE_1_20); // vyžadováno pro >133 MHz
busy_wait_us(10000); // nech napětí ustálit
set_sys_clock_khz(225000, true); // AŽ PAK zvyš clock
// ... teprve teď konstruuj display SPI (inicializuje se na finálním clk_peri) ...
}

Napěťové úrovně VREG, které CircuitPython používá: ≤133 MHz → 1,10 V, >133 MHz → 1,20 V, ≥300 MHz → 1,20 V, ≥400 MHz → 1,30 V.

(b) Skutečným stropem je displej, ne čip. Čip RP2350 nabootoval a běžel v pořádku na 250 MHz (jednoduchá hra jako Train byla čistá), ale náročné renderování ukázalo artefakty kolem dirty regionů: na 250 MHz vydává CPU příkazy nastavení okna pro jednotlivé obdélníky (CASET / RASET / RAMWR plus přepínání GPIO DC/CS) rychleji, než je ST7789 spolehlivě zachytí, takže občas okno dopadne špatně a strip se zapíše mírně mimo. To je efekt jádrového clocku, ne datové rychlosti SPI: objevil se na 250 MHz/62,5 MHz SPI, zatímco 150 MHz/75 MHz SPI bylo čisté (pomalejší SPI, rychlejší jádro → stejně se to rozbilo). Projevuje se všude, kde je mnoho nastavení okna na snímek (spousta dirty rectů, celosnímkové překreslení), a sotva u lehkých dirty-rect her.

Binární vyhledávání na této desce našlo strop displeje: 150 · 200 · 225 MHz čisté · 250 MHz dirty (limit příkazů panelu mezi 225 a 250). Ale viz rámeček výše — i na čistých 200/225 deska běžela celkově ~2× pomaleji (flash/XIP), takže se deska dodává jako stock 150 MHz, ne overclock. Pákou zde není clock; je to dělička SPI.

Overclocking zvedá CPU, ale (přes pravidlo sudé děličky) často snižuje SPI clock v rámci specifikace:

Buildjádrodisplay SPIvýsledek
stock + „spi75” (dodávaný)150 MHz75 MHz (nad specifikací, zde čisté)celkově nejrychlejší na této desce
boot-225 (zamítnuto)225 MHz56,25 MHz (v rámci specifikace)~2× pomalejší (flash/XIP — viz rámeček v §4)

Nižší SPI overclocku i jeho flash penalizace tlačily oba špatným směrem, takže doporučená konfigurace RP2350 PicoPadu je stock 150 MHz s požadavkem 75 MHz SPI (/2).

Takže vybírejte podle své zátěže: hra vázaná na CPU → overclock; celosnímkové/náročné na přenos → držte jádro nízko a SPI vysoko. Z jedné sudé děličky nemůžete maximalizovat obojí.


Použijte examples/picogame_bench.py — jeho přepínače izolují každý režim:

PřepínačIzolujeČtení
FAST = True/Falserychlý DMA vs. přenositelný rendererliší se? (liší se jen vícestripové přenosy)
STRESS = Truevynutí celosnímkové překreslení každý snímekstrop vázaný na SPI (∝ SPI clock)
HEAVY = Truepár velkých scale+rotate spritůpřípad vázaný na CPU/blit (∝ jádrový clock)
OVERCLOCK = …jádrový clock za běhuna PicoPadu nechte None — změna za běhu poškodí displej; overclockujte při bootu ve firmwaru

Čtení výsledků:

  • Výchozí režim (malé sprity) je špatná metrika — 25 rozházených spritů přestřelí strop 6 dirty rectů, sloučí se do téměř celoobrazovkového ohraničujícího boxu a stanou se proměnlivě velkým celosnímkovým přenosem. FPS divoce kolísá podle toho, jak se sprity náhodou seskupí. Použijte STRESS (deterministický celý snímek) nebo HEAVY (deterministická zátěž CPU) pro čistá čísla.
  • STRESS FPS škáluje s SPI clockem (vázáno na přenos). Pokud zdvojnásobení SPI clocku ho ~zdvojnásobí, jste tam vázáni na SPI.
  • HEAVY FPS škáluje s jádrovým clockem (vázáno na blit). Zde se overclock vyplatí.

Rozpoznání overclocku, který je pro displej příliš vysoký:

  • Artefakty kolem / na okrajích dirty regionů, rozmazání nebo posunuté stripy v náročném renderování — zatímco lehká scéna (nebo jednoduchá hra) stále vypadá dobře. To je strop časování příkazů okna (§4b), ne pád.
  • Clock příliš vysoký pro čip/flash naopak selže tvrději: buď nenabootuje, nebo hard-faultne.
  • Záchrana je vždy BOOTSEL: podržte BOOTSEL, znovu naflashujte známý funkční .uf2. Overclock nemůže desku trvale zničit.

Hledání stropu: buildněte firmwary na sestupných jádrových clockách (250 → 225 → 200 → …), naflashujte každý, spusťte STRESS + HEAVY a ponechte nejvyšší clock bez artefaktů v dirty regionech. Měňte clock jen v board.c board_init (ne za běhu) a znovu zkontrolujte, kterou SPI děličku nový clk_peri vybral (§2).