<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body dir="auto"><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 align="left" dir="auto" style="font-size:100%;color:#000000"><div>-------- Původní zpráva --------</div><div>Od: Petr Labaj <labaj@volny.cz> </div><div>Datum: 26.12.23  22:27  (GMT+01:00) </div><div>Komu: hw-list@list.hw.cz </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>>><br>>> PL<br><br>_______________________________________________<br>HW-list mailing list  -  sponsored by www.HW.cz<br>Hw-list@list.hw.cz<br>http://list.hw.cz/mailman/listinfo/hw-list<br></body></html>