volatile, interrupts og atomicitet¶
Læringsmål
Forstå hvorfor state delt mellom ISR og main må behandles eksplisitt, hva volatile gjør og ikke gjør, hvorfor flerbyte-objekter ikke automatisk er atomiske på en 8-bits AVR, og hvordan et sammenhengende snapshot tas.
Forutsetninger
Fullfør først leksjonen om pekere, buffere og structs. Du bør også forstå interrupts, SREG og SRAM load/store.
Et interrupt kan endre programstate mellom to instruksjoner i main(). Det er nyttig, men betyr at delte data har regler som vanlig sekvensiell kode ikke har.
Det parede eksemplet¶
EduAVR har:
examples/c/shared-state/main.cexamples/asm/shared-state/main.S
Timer0 oppdaterer periodisk:
ISR-en øker shared_ticks og legger 0x0101 til shared_word. Main venter til minst tre interrupts har skjedd og lagrer deretter ett sammenhengende snapshot.
Hva volatile betyr¶
I C er de delte objektene deklarert volatile. Dette forteller compileren at verdiene kan endres av årsaker som ikke er synlige i vanlig kontrollflyt, og at aksessene er observerbare.
Det betyr ikke at:
- aksessen er atomisk;
- flere aksesser utgjør én transaksjon;
- interrupts er deaktivert;
- races er umulige;
- en flerbyte-verdi ikke kan endres midt i en lesing.
Inspiser generert disassembly og identifiser loads/stores som beholdes fordi objektene er volatile.
Hvorfor 16 bits betyr noe på en 8-bits CPU¶
shared_word er 16 bits bred, men AVR håndterer den som separate byte-operasjoner. Et interrupt kan oppstå mellom lesingen av low byte og high byte.
Et blandet resultat kan derfor oppstå hvis main leser et flerbyte-objekt som endres uten beskyttelse.
Dette er noe annet enn visibility. volatile handler om compiler-observerbarhet; atomicitet er en egenskap ved maskinen og kjøringen.
Atomisk snapshot i C¶
Eksemplet bevarer SREG, deaktiverer interrupts, kopierer de delte verdiene og gjenoppretter tidligere SREG:
Den beskyttede regionen er bevisst kort. Arbeid som ikke krever atomicitet skjer etter at interrupts er gjenopprettet.
Samme operasjon i Assembly¶
Assembly-versjonen gjør sekvensen eksplisitt:
Dette er maskinnivåforklaringen på hvorfor C-mønsteret virker.
Deterministisk Q1¶
Kjør:
Ved shared_state_ready verifiserer Q1 begge implementasjonene. Timeren må ha generert minst tre events, og low/high-byte i det beskyttede shared_word-snapshotet må samsvare med samme lagrede tick-verdi.
Kvalifisert resultat
Build #371 og container-kvalifikasjon #29 passerte for den parede shared-state-implementasjonen og den deterministiske Q1-proben.
Q1 demonstrerer interrupt-drevet CPU/SRAM-oppførsel i simulatoren. Den gir ingen påstand om fysiske interrupt-kilder eller elektrisk timing.
Designregler¶
Hold delt state liten og eierskap tydelig. Foretrekk korte critical sections. Ikke deaktiver interrupts rundt tregt arbeid bare for enkelhets skyld. For større datastrukturer bør du vurdere å kopiere et minimalt snapshot eller lage en producer/consumer-protokoll fremfor å beskytte en lang operasjon.
Oppgaver¶
- Forklar hvorfor
volatilealene ikke kan gjøre en 16-bits lesing atomisk. - Finn de to byte-loads som brukes for
shared_wordi begge implementasjonene. - Flytt unødvendig arbeid inn i den beskyttede regionen og forklar latency-kostnaden.
- Legg til enda en delt 16-bits verdi og utvid snapshotet korrekt.
- Beskriv et ring-buffer-design der ISR og main eier hver sin indeks.
Neste
Neste steg er å kvantifisere SRAM- og stack-bruk og studere hvordan optimalisering endrer generert AVR-kode uten å endre påkrevd programsemantikk.