<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    Některé MCU jsou na cvičení s porty i optimalizované.<br>
    <br>
    Třeba LPC11U68, tam udělám programem v C na pinu přes 20 MHz, tedy
    blízko CLK/2. Taky jsem toho hojně využíval. Displej v paralelním
    módu, data i všechny řídící signály na jednom 32b portu, pak je
    rychlost komunikace taková, že doba překreslení závisí výhradně na
    sw co data generuje.<br>
    <br>
    Ale jsou i opačná překvápka: Potřeboval jsem na AM3358 nenáročný
    přístup k SPI flash a byl jsem línej rozchozovat HW SPI. Zápis šel
    dle očekávání, někde v řádu 100 Mbit, ale čtení jsem si napřed
    myslel, že zatuhlo ... ne, jen bylo rychlé asi jako vytáčený
    internet před 25 lety:-) Přitom CLK do CPU bylo 650 MHz.<br>
    <br>
    PH<br>
    <br>
    <div class="moz-cite-prefix">Dne 27.12.2023 v 18:58 Jaroslav Buchta
      napsal(a):<br>
    </div>
    <blockquote type="cite"
      cite="mid:ac27bc56-b467-4e1a-9abd-e33aec211e8d@hascomp.cz">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <div class="moz-cite-prefix">Mam spravny pocit, ze dokonceni vsech
        zapisu by mela zajistit instrukce DSB? Nebo nejaka podobna z
        tech cca 3, se mi furt pletou.</div>
      <div class="moz-cite-prefix">Ale asi az u novejsich.</div>
      <div class="moz-cite-prefix">Mam ale vyzkouseno, ze kdyz to melo
        byt rychle (simulace SPI kdyz se netrefily piny...), nasazel
        jsem za sebe instrukce zapisu do 32b registru S/R, kazda menila
        hodiny , kazda druha data a chodilo to jak vino maximalni
        rychlosti, kdyz jsem vyhazel DSB, tak rychleji  ale stale dobre.
        Sbernice mely stejnou frekvenci a byl to tusim M4.<br>
      </div>
      <div class="moz-cite-prefix">Tak se toho asi netreba uplne bat a
        cekam, ze to je nejak interne poresene.<br>
      </div>
      <div class="moz-cite-prefix"><br>
      </div>
      <div class="moz-cite-prefix">Dne 27.12.2023 v 18:06 dresler
        napsal(a):<br>
      </div>
      <blockquote type="cite"
        cite="mid:20231227170623.28915223604@alik.hw.cz">
        <meta http-equiv="Content-Type"
          content="text/html; charset=UTF-8">
        <div dir="auto">Bavíme-li se klasických STM32, zápisy se
          neztrácejí. Podle typu architektury je tam 0-1 write buffer
          mezi jádrem a hlavní sběrnicí, pak (AXI-)AHB-APB bridge. Zde
          je zpoždění dané tím, že se data resynchronizují mezi různými
          hodinami, kde platí nejdelší zpoždění (1+ratio) a zpoždění
          vnější periferie. Není tedy potřeba psát opakovaně. </div>
        <div dir="auto"><br>
        </div>
        <div dir="auto">Aby se zajistilo, že program pokračuje po
          dokončení zápisu do registru, prostě ten registr přečtěte
          zpět. </div>
        <div dir="auto"><br>
        </div>
        <div dir="auto">Tomáš </div>
        <div dir="auto"><br>
        </div>
        <div dir="auto"><br>
        </div>
        <div dir="auto"><br>
        </div>
        <div id="composer_signature" dir="auto">
          <div style="font-size:12px;color:#575757" dir="auto">Odesláno
            z mého zařízení Galaxy</div>
        </div>
        <div dir="auto"><br>
        </div>
        <div><br>
        </div>
        <div dir="auto" style="font-size:100%;color:#000000"
          align="left">
          <div>-------- Původní zpráva --------</div>
          <div>Od: Petr Labaj <a class="moz-txt-link-rfc2396E"
              href="mailto:labaj@volny.cz" moz-do-not-send="true"><labaj@volny.cz></a>
          </div>
          <div>Datum: 26.12.23 22:27 (GMT+01:00) </div>
          <div>Komu: <a
              class="moz-txt-link-abbreviated moz-txt-link-freetext"
              href="mailto:hw-list@list.hw.cz" moz-do-not-send="true">hw-list@list.hw.cz</a>
          </div>
          <div>Předmět: Re: Fwd: STM32F103 UART DMA </div>
          <div><br>
          </div>
        </div>
        Tak v tom případě i já pošlu odpověď, kterou jsem Vám posílal
        soukromě, <br>
        do konference:<br>
        <br>
        Hmmm.<br>
        A díval jste se prosím na to, čeho se Vámi vysmívaná "rada nad
        zlato" <br>
        vlastně týkala?<br>
        Psal jsem o prvním zápisu do nějakého registru, kdy obecně
        nemusím <br>
        vědět, jaký je předchozí stav daného registru.<br>
        Takže pokud tam zapíšu ORem, tak tam můžou zůstat nahozené
        nějaké bity, <br>
        které nechci, ale byly tam v jedničce už dřív.<br>
        <br>
        V tomto případě přece naplatí, že by se mohl ztratit nějaký
        zápis kvůli <br>
        asynchronnosti přístupu na danou periferii.<br>
        Jedná se o _první_ zápis na tuto periferii.<br>
        Všechny případné další zápisy už samozřejmě musí být ORem,
        protože jinak <br>
        by zmizela předchozí konfigurace.<br>
        <br>
        PL<br>
        <br>
        *****************<br>
        <br>
        Dne 26.12.2023 v 22:06 František Burian napsal(a):<br>
        > Omlouvám se, asi jsem to neposlal do konference ale p.
        Labajovi, ale <br>
        > mířilo to do konference.<br>
        ><br>
        ><br>
        ><br>
        ><br>
        > Zdravíčko,<br>
        ><br>
        >   Obvykle nepřispívám ale tady musím, protože zde je
        nastíněna "rada <br>
        > nad zlato", která tomu, kdo se jí bude řídit způsobí mnoho
        bezesných <br>
        > nocí.<br>
        ><br>
        >   Nezapomínejte, že ARM je architektura mnoha sběrnic,
        různých keší a <br>
        > to, co napíšete do programu nikdy není to, co se provede.
        Zkuste si <br>
        > udělat<br>
        > rychlý program, který bude pouze zapisovat do GPIO portu a
        nahazovat <br>
        > všechny bity na 1 a shazovat všechny bity do 0, připojte
        osciloskop k <br>
        > pinu<br>
        > a pozorujte, co uvidíte.<br>
        ><br>
        > Operace = je převedena do instrukce STRB která nezapisuje
        na paměťové <br>
        > místo, ale do cache, která to posílá na pomalejší sběrnici.
        A pokud se<br>
        > sejdou dva STRB do stejné paměťové pozice, je předchozí
        zápis ztracen <br>
        > a na pomalejší sběrnici se populuje později zapsaná
        hodnota. GPIO jsou na<br>
        > STMku za dvěma takovými cachemi, AHB, APB a dokážete si
        představit <br>
        > jakýpak krásný náhodný děj se na GPIO povede vyrobit.<br>
        ><br>
        > Aby byly zápisy jednoznačně orderované, musí mezi nimi být
        LDRB který <br>
        > počká na úplnou invalidaci všech keší po cestě před další
        instrukcí. <br>
        > Když tedy<br>
        > převedete původní sekvenci (pseudokód)<br>
        ><br>
        > while (1) { PORTB = 0; PORTB=255;  }<br>
        ><br>
        > na<br>
        ><br>
        > while (1) { PORTB &=0; PORTB|=255; }<br>
        ><br>
        > dosáhnete výrazně pomalejšího programu, ale krásného
        obdélníku ikdyž z <br>
        > logiky věci by ty programy měly dělat totéž. Ještě
        krásnější je, že<br>
        > druhý příklad se může přeložit na první kvůli optimalizacím
        překladače <br>
        > když někdo při definici PORTB zapoměl na volatile u každého
        prvku.<br>
        ><br>
        > Toto samosemou platí pro každý přístup na jakoukoliv paměť
        která má po <br>
        > cestě nějakou cache (tedy je na pomalejší sběrnici). Proto
        musí být<br>
        > všechny přístupy k řídícím registrům které musí být vždy
        sekvenční <br>
        > (nejprv nastav tento bit, pak tento bit) buďto provedeny
        pomocí operátorů<br>
        > |= nebo &= nebo použít instrukci dmb, která ale čeká na
        všechny cache, <br>
        > ne jen na tu, kterou chcete zapsat, takže ještě víc
        zpomalí.<br>
        ><br>
        > Registry konfigurace periferií jsou přesně ty, které mají v
        podmínkách <br>
        > nejprv toto pak toto a když na tento postup nemyslíte, "ono
        to nějak <br>
        > funguje"<br>
        > ale jednou za čas to selže a daná periferie se jeví jako
        zmrzlá <br>
        > protože jste nastavili bity ve stejný okamžik, ne jeden po
        druhém.<br>
        ><br>
        > Cache mezi sběrnicemi má omeznou velokost, takže pokud za
        sebou <br>
        > zapíšete dostatečný počet zápisů do stejné sběrnice, tak
        můžete zase ten<br>
        > první, protože se čeká na ostatní zápisy. Jak je cache
        dlouhá je <br>
        > nedokumentovaná funkce a mění se to mezi výrobními dávkami
        <br>
        > (odpozorováno 2-8<br>
        > zápisů u stejného čipu jen z jiných dávek, dokonce jsem
        nabyl dojmu že <br>
        > se hloubka mění podle poměru frekvencí ale tam nemám
        ověřeno) takže<br>
        > nelze spoléhat na konkrétní velikost cache.<br>
        ><br>
        > Stejný problém je když takto přistoupíte na dvě po sobě
        jdoucí <br>
        > paměťové místa, tak ty čipy s delší cachí zápis uvnitř keše
        převedou <br>
        > na jeden přístup<br>
        > do paměti s delším slovem a na některých architekturách
        hard fault <br>
        > jako vyšitý, když ale použijete RMW, hardfault zmizí.
        (Nevím jestli to <br>
        > je aktuální,<br>
        > takto se chovaly staré čipy)<br>
        ><br>
        > Pokud se podíváte do implementace CMSIS, je tam vždy RMW
        operace s <br>
        > registry, nikdy to není čistý zápis právě kvůli tomuto
        fenoménu.<br>
        ><br>
        > S tímto jsem si při psaní vlastních rychlých knihoven užil
        více než <br>
        > dost. Probíralo se to i v intelektuálním klubu v Patronce,
        je dobré <br>
        > číst backlogy<br>
        > nebo se účastnit :-)<br>
        ><br>
        > S pozdravem a přáním pěkných svátků<br>
        ><br>
        >   František Burian<br>
        ><br>
        ><br>
        > Dne 26. 12. 23 v 17:14 Petr Labaj napsal(a):<br>
        >> Můžu drobnou poznámku, která je ale obecnější a s daným
        problémem <br>
        >> nesouvisí?<br>
        >><br>
        >> Vy všude používáte "orování" parametrů, které zadáváte
        do řídících <br>
        >> registrů.<br>
        >> Např.:<br>
        >> USART2->CR1 |= USART_CR1_TE | USART_CR1_RE;<br>
        >> nebo<br>
        >> DMA1_Channel6->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC |
        DMA_CCR_TCIE;<br>
        >><br>
        >> Ale používáte to i při prvním zápisu do daného
        registru.<br>
        >> Tedy jinými slovy spoléháte na to, že předtím tam byly
        samé nuly.<br>
        >> Což může být velice zrádné, když tu konstrukci
        použijete třeba i <br>
        >> někdy později, kdy už v těch registrech něco je.<br>
        >><br>
        >> Takže by podle mě měl první zápis vypadat takhle:<br>
        >> USART2->CR1 = USART_CR1_TE | USART_CR1_RE;<br>
        >> nebo<br>
        >> DMA1_Channel6->CCR = DMA_CCR_MINC | DMA_CCR_CIRC |
        DMA_CCR_TCIE;<br>
        >><br>
        >> A orování použít až když tam už dodatečně něco
        přidáváte.<br>
      </blockquote>
    </blockquote>
  </body>
</html>