arm cm3 dsb instrukce

Miroslav Mraz mrazik na volny.cz
Pondělí Červen 15 18:28:27 CEST 2020


Tak mě tahle diskuze vyprovokovala to celé otestovat. Největší problém 
není ani s tím, že by std::atomic nefungovalo, ale přesvědčit se, že 
pouhá volatilní proměnná fungovat nemůže, pokud do ní zapisuji ze dvou 
různých konců.
To by jeden ani nevěřil, co to dá práce najít způsob jak se s přerušením 
trefit do těch dvou kritických instrukcí, kde se proměnná modifikuje a 
ukázat, že je to špatně. Zdálo by se, že když udělám hlavní smyčku 
dostatečně krátkou a budu přerušovat hodně svižně, musí to zhavarovat 
dost často. Není tomu tak. Obvyklé použití je asi takovéto:
static volatile int lock;
static const int step = 4;
//...
int main () {
   for (;;) {
     if (lock >= step) {
       lock -= step; // kritická operace
     }
   }
}
a přitom lock v přerušení inkrementuji o 1. A právě ten test na velikost 
lock ve většině případů kritickou operaci úplně vyhodí, takže chyba se 
projeví tak zhruba v jednom případě přerušení z několika milionů. O to 
je to záludnější.

Ale proč jsem si s tím vlastně hrál. To primitivum std::atomic normálně 
používám, trochu tuším, že na Cortex-M3,4 používá dvojici LDREX/STREX, 
nikdy mě však nenapadlo se v tom nějak víc šťourat. Proto mě dost 
překvapilo, že v přerušení (a podobně v main) se používá asi takováto 
sekvence
200001aa:	f3bf 8f5f 	dmb	sy
200001ae:	e851 0f00 	ldrex	r0, [r1]
200001b2:	3001      	adds	r0, #1	@ v main bude naopak subs
200001b4:	e841 0e00 	strex	lr, r0, [r1]
200001b8:	f1be 0f00 	cmp.w	lr, #0
200001bc:	d1f7      	bne.n	200001ae
200001be:	f3bf 8f5f 	dmb	sy
Jenže když mám proměnnou v main uzamčenu, takhle na první pohled by to 
mělo skončit v nekonečné smyčce. Není tomu tak - je to docela chytře 
vymyšleno, výjimka exkluzivitu zruší, takže zápis v přerušení je vždy 
úspěšný. V main se samozřejmě zjistí exkluzivní zápis a proměnná se 
znovu načte. Takže to funguje v pohodě a bez velkého přemýšlení i v 
bare-metal programování.
Metoda se zákazem a opětovným povolením přerušení v kritické sekci 
funguje ovšem také i když malinko a nepodstatně odlišně (při změně v 
main se do přerušení prostě nedostanete), u Cortex-M0 a jiných procesorů 
stejně nic jiného většinou nezbývá.

Mrazík


Dne 05. 06. 20 v 10:20 Jan Waclawek napsal(a):
>> Jinak øeèeno - pokud máte napøíklad globální
>> promìnnou, kterou sdílíte mezi smyèkou hlavního programu a pøerušením,
>> nestaèí jí deklarovat jako volatile, musíte použít nìco jako
>> std::atomic<type>.
> 
> U Cortex-M3/M4 toto nie je potrebne riesit (s vynimkou LDM/STM, ktore su z
> principu problematicke operacie, hojne sa vyskytujuce aj v ARM erratach, a
> pochybujem, ze by ich nejaky prekladac generoval pre volatile premenne),
> vid napr. Cortex-M4 TRM, 3.5 Write buffer:
> 
> If an interrupt
> comes in while  DMB or  DSB is waiting for the write buffer to drain, the
> processor returns to the
> instruction following the  DMB or  DSB after the interrupt completes. This
> is because interrupt
> processing acts as a memory barrier operation.
> 
> wek


Další informace o konferenci Hw-list