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øeruení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