Fwd: STM32F103 UART DMA

František Burian BuFran na seznam.cz
Úterý Prosinec 26 22:06:08 CET 2023


Omlouvám se, asi jsem to neposlal do konference ale p. Labajovi, ale mířilo to do konference.




Zdravíčko,

   Obvykle nepřispívám ale tady musím, protože zde je nastíněna "rada nad zlato", která tomu, kdo se jí bude řídit způsobí mnoho bezesných nocí.

   Nezapomínejte, že ARM je architektura mnoha sběrnic, různých keší a to, co napíšete do programu nikdy není to, co se provede. Zkuste si udělat
rychlý program, který bude pouze zapisovat do GPIO portu a nahazovat všechny bity na 1 a shazovat všechny bity do 0, připojte osciloskop k pinu
a pozorujte, co uvidíte.

Operace = je převedena do instrukce STRB která nezapisuje na paměťové místo, ale do cache, která to posílá na pomalejší sběrnici. A pokud se
sejdou dva STRB do stejné paměťové pozice, je předchozí zápis ztracen a na pomalejší sběrnici se populuje později zapsaná hodnota. GPIO jsou na
STMku za dvěma takovými cachemi, AHB, APB a dokážete si představit jakýpak krásný náhodný děj se na GPIO povede vyrobit.

Aby byly zápisy jednoznačně orderované, musí mezi nimi být LDRB který počká na úplnou invalidaci všech keší po cestě před další instrukcí. Když tedy
převedete původní sekvenci (pseudokód)

while (1) { PORTB = 0; PORTB=255;  }

na

while (1) { PORTB &=0; PORTB|=255; }

dosáhnete výrazně pomalejšího programu, ale krásného obdélníku ikdyž z logiky věci by ty programy měly dělat totéž. Ještě krásnější je, že
druhý příklad se může přeložit na první kvůli optimalizacím překladače když někdo při definici PORTB zapoměl na volatile u každého prvku.

Toto samosemou platí pro každý přístup na jakoukoliv paměť která má po cestě nějakou cache (tedy je na pomalejší sběrnici). Proto musí být
všechny přístupy k řídícím registrům které musí být vždy sekvenční (nejprv nastav tento bit, pak tento bit) buďto provedeny pomocí operátorů
|= nebo &= nebo použít instrukci dmb, která ale čeká na všechny cache, ne jen na tu, kterou chcete zapsat, takže ještě víc zpomalí.

Registry konfigurace periferií jsou přesně ty, které mají v podmínkách nejprv toto pak toto a když na tento postup nemyslíte, "ono to nějak funguje"
ale jednou za čas to selže a daná periferie se jeví jako zmrzlá protože jste nastavili bity ve stejný okamžik, ne jeden po druhém.

Cache mezi sběrnicemi má omeznou velokost, takže pokud za sebou zapíšete dostatečný počet zápisů do stejné sběrnice, tak můžete zase ten
první, protože se čeká na ostatní zápisy. Jak je cache dlouhá je nedokumentovaná funkce a mění se to mezi výrobními dávkami (odpozorováno 2-8
zápisů u stejného čipu jen z jiných dávek, dokonce jsem nabyl dojmu že se hloubka mění podle poměru frekvencí ale tam nemám ověřeno) takže
nelze spoléhat na konkrétní velikost cache.

Stejný problém je když takto přistoupíte na dvě po sobě jdoucí paměťové místa, tak ty čipy s delší cachí zápis uvnitř keše převedou na jeden přístup
do paměti s delším slovem a na některých architekturách hard fault jako vyšitý, když ale použijete RMW, hardfault zmizí. (Nevím jestli to je aktuální,
takto se chovaly staré čipy)

Pokud se podíváte do implementace CMSIS, je tam vždy RMW operace s registry, nikdy to není čistý zápis právě kvůli tomuto fenoménu.

S tímto jsem si při psaní vlastních rychlých knihoven užil více než dost. Probíralo se to i v intelektuálním klubu v Patronce, je dobré číst backlogy
nebo se účastnit :-)

S pozdravem a přáním pěkných svátků

   František Burian


Dne 26. 12. 23 v 17:14 Petr Labaj napsal(a):
> Můžu drobnou poznámku, která je ale obecnější a s daným problémem nesouvisí?
>
> Vy všude používáte "orování" parametrů, které zadáváte do řídících registrů.
> Např.:
> USART2->CR1 |= USART_CR1_TE | USART_CR1_RE;
> nebo
> DMA1_Channel6->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_TCIE;
>
> Ale používáte to i při prvním zápisu do daného registru.
> Tedy jinými slovy spoléháte na to, že předtím tam byly samé nuly.
> Což může být velice zrádné, když tu konstrukci použijete třeba i někdy později, kdy už v těch registrech něco je.
>
> Takže by podle mě měl první zápis vypadat takhle:
> USART2->CR1 = USART_CR1_TE | USART_CR1_RE;
> nebo
> DMA1_Channel6->CCR = DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_TCIE;
>
> A orování použít až když tam už dodatečně něco přidáváte.
>
> PL
> _______________________________________________
> HW-list mailing list  -  sponsored by www.HW.cz
> Hw-list na list.hw.cz
> http://list.hw.cz/mailman/listinfo/hw-list


Další informace o konferenci Hw-list