Vedo diversi potenziali problemi con quelle sezioni critiche. Ci sono avvertimenti e soluzioni a tutti questi, ma come riassunto:
- Non c'è nulla che impedisca al compilatore di spostare il codice attraverso queste macro, per ottimizzazione o altri motivi casuali.
- Salvano e ripristinano alcune parti dello stato del processore che il compilatore si aspetta che l'assemblaggio in linea lasci da solo (se non diversamente indicato).
- Non c'è nulla che impedisca che si verifichi un interrupt nel mezzo della sequenza e che cambi lo stato tra quando viene letto e quando viene scritto.
Prima di tutto, hai sicuramente bisogno di alcune barriere di memoria del compilatore . GCC li implementa come clobbers . Fondamentalmente, questo è un modo per dire al compilatore "No, non puoi spostare gli accessi alla memoria attraverso questo pezzo di assembly inline perché potrebbe influenzare il risultato degli accessi alla memoria." In particolare, è necessario sia "memory"
e "cc"
clobbers, sia sul iniziano e macro finali. Ciò impedirà che altre cose (come le chiamate di funzione) vengano riordinate anche rispetto all'assembly inline, poiché il compilatore sa che potrebbero avere accessi alla memoria. Ho visto GCC per ARM tenere lo stato nei registri dei codici delle condizioni in tutto l'assemblaggio in linea con "memory"
i "cc"
clobber , quindi sicuramente hai bisogno del clobber.
In secondo luogo, queste sezioni critiche stanno salvando e ripristinando molto più del semplice abilitare gli interrupt. In particolare, stanno salvando e ripristinando la maggior parte del CPSR (Current Program Status Register) (il collegamento è per Cortex-R4 perché non sono riuscito a trovare un diagramma carino per un A9, ma dovrebbe essere identico). Esistono sottili restrizioni su quali parti di stato possano effettivamente essere modificate, ma qui è più che necessario.
Tra le altre cose, questo include i codici delle condizioni (in cui i risultati di istruzioni come cmp
sono memorizzati in modo che le successive istruzioni condizionali possano agire sul risultato). Il compilatore sarà sicuramente confuso da questo. Questo è facilmente risolvibile usando il "cc"
clobber come menzionato sopra. Tuttavia, questo farà fallire il codice ogni volta, quindi non sembra quello con cui stai riscontrando problemi. Un po 'come una bomba a orologeria, però, in quanto modificare casualmente un altro codice potrebbe causare al compilatore di fare qualcosa di leggermente diverso, che verrà rotto da questo.
Questo tenterà anche di salvare / ripristinare i bit IT, che vengono utilizzati per implementare l'esecuzione condizionale Thumb . Nota che se non esegui mai il codice Thumb, questo non ha importanza. Non ho mai capito come l'assemblaggio inline di GCC gestisca i bit IT, a parte la conclusione che non lo fa, il che significa che il compilatore non deve mai mettere l'assemblaggio inline in un blocco IT e si aspetta sempre che l'assembly termini al di fuori di un blocco IT. Non ho mai visto GCC generare codice in violazione di questi presupposti, e ho fatto un assemblaggio in linea abbastanza intricato con una forte ottimizzazione, quindi sono ragionevolmente sicuro che lo siano. Ciò significa che probabilmente non tenterà effettivamente di modificare i bit IT, nel qual caso tutto va bene. Il tentativo di modificare questi bit è classificato come "architettonicamente imprevedibile", quindi potrebbe fare tutti i tipi di cose cattive, ma probabilmente non farà nulla.
L'ultima categoria di bit che verranno salvati / ripristinati (oltre a quelli per disabilitare effettivamente gli interrupt) sono i bit della modalità. Questi probabilmente non cambieranno, quindi probabilmente non importerà, ma se si dispone di un codice che modifica deliberatamente le modalità, queste sezioni di interruzione potrebbero causare problemi. Il passaggio dalla modalità privilegiata a quella utente è l'unico caso che mi aspetto.
In terzo luogo, non c'è nulla che impedisca a un interrupt di cambiare altre parti del CPSR tra MRS
e MSR
in ARM_INT_LOCK
. Eventuali modifiche di questo tipo potrebbero essere sovrascritte. Nella maggior parte dei sistemi ragionevoli, gli interrupt asincroni non cambiano lo stato del codice che stanno interrompendo (incluso CPSR). Se lo fanno, diventa molto difficile ragionare su cosa farà il codice. Tuttavia, è possibile (la modifica del bit di disabilitazione FIQ mi sembra molto probabile), quindi dovresti considerare se il tuo sistema lo fa.
Ecco come li implementerei in un modo che affronti tutti i potenziali problemi che ho sottolineato:
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
Assicurati di compilare -mcpu=cortex-a9
perché almeno alcune versioni di GCC (come la mia) sono impostate su una CPU ARM precedente che non supporta cpsie
e cpsid
.
Ho usato ands
invece semplicemente and
in, ARM_INT_LOCK
quindi è un'istruzione a 16 bit se questo è usato nel codice Thumb. Il "cc"
clobber è comunque necessario, quindi è strettamente un vantaggio in termini di prestazioni / dimensioni del codice.
0
e 1
sono etichette locali , per riferimento.
Questi dovrebbero essere utilizzabili allo stesso modo delle tue versioni. È ARM_INT_LOCK
veloce / piccolo come quello originale. Sfortunatamente, non sono riuscito a trovare un modo per farlo in ARM_INT_UNLOCK
sicurezza ovunque vicino a poche istruzioni.
Se il tuo sistema ha dei vincoli su quando IRQ e FIQ sono disabilitati, questo potrebbe essere semplificato. Ad esempio, se sono sempre disabilitati insieme, è possibile combinare in uno cbz
+ in cpsie if
questo modo:
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
In alternativa, se non ti interessano affatto i FIQ, è simile a lasciarli abilitare / disabilitare completamente.
Se sai che nient'altro cambia mai nessuno degli altri bit di stato in CPSR tra il blocco e lo sblocco, puoi anche usare continue con qualcosa di molto simile al tuo codice originale, tranne con entrambi "memory"
e "cc"
clobber in entrambi ARM_INT_LOCK
eARM_INT_UNLOCK