Domanda

sto usando e core basato x86 per manipolare una memoria di 32 bit mappato registro. I miei si comporta hardware correttamente solo se la CPU genera 32 bit a livello di lettura e scrittura a tale registro. Il registro è allineato su un indirizzo a 32 bit e non è indirizzabile con granularità byte.

Che cosa posso fare per garantire che il mio C (o C99) compilatore saranno solo generare piena a 32-bit legge ampia e scrive in tutti i casi?

Ad esempio, se faccio un'operazione di lettura-modifica-scrittura in questo modo:

volatile uint32_t* p_reg = 0xCAFE0000;
*p_reg |= 0x01;

Non voglio che il compilatore per ottenere intelligente circa il fatto che solo le modifiche byte fondo e generano 8-bit a livello di lettura / scrittura. Dal momento che il codice macchina è spesso più densa per le operazioni a 8-bit su x86, ho paura di ottimizzazioni indesiderati. La disattivazione ottimizzazioni, in generale, non è un'opzione.

----- EDIT -------
Un documento interessante e molto rilevanti: http: //www.cs. utah.edu/~regehr/papers/emsoft08-preprint.pdf

È stato utile?

Soluzione

Le vostre preoccupazioni sono coperti dalla qualificazione volatile.

6.7.3 / 6 "Tipo di qualificazione", dice:

  

Un oggetto che ha tipo di volatili qualificato può essere modificata in modo sconosciuto alla realizzazione o avere altri effetti collaterali sconosciuti. Pertanto qualsiasi espressione riferita a tale oggetto una sarà valutato rigorosamente secondo le regole della macchina astratta, come descritto in 5.1.2.3. Inoltre, in ogni punto di sequenza l'ultimo valore memorizzato nell'oggetto concorderà con quanto prescritto dalla macchina astratta, ad eccezione di quanto modificato dalle incognite citati in precedenza. Ciò che costituisce un accesso a un oggetto che ha tipo volatili qualificato dipende dall'implementazione.

5.1.2.3 "L'esecuzione del programma", dice (tra le altre cose):

  

Nella macchina astratta, tutte le espressioni vengono valutati come specificato dalla semantica.

Questa è seguita da una frase che viene comunemente indicato come regola 'as-se', che permette un'implementazione di non seguire la semantica macchina astratte se il risultato finale è lo stesso:

  

Una necessità effettiva implementazione non valutare una parte di espressione se può dedurre che il suo valore non viene utilizzato e che effetti collaterali necessari vengono prodotte (incluso qualsiasi causato chiamando una funzione o accedere un oggetto volatile).

Ma, 6.7.3 / 6 dice in sostanza che i tipi volatili qualificato utilizzati in un'espressione non possono avere il 'come-se' regola applicata - l'attuale semantiche macchina astratta devono essere seguite. Pertanto, se il puntatore a un tipo a 32 bit volatili viene dereference, allora il valore intero di 32 bit deve essere letto o scritto (a seconda del funzionamento).

Altri suggerimenti

L'unico modo per garantire che il compilatore farà la cosa giusta è quello di scrivere la vostra routine di carico e memorizzare in assembler e li chiamano dal C. 100% dei compilatori che ho usato nel corso degli anni può e vuole sbagliare ( GCC incluso).

A volte l'ottimizzatore si ottiene, ad esempio, si desidera memorizzare una costante che sembra il compilatore come un piccolo numero 0x10 consente di dire, in un registro a 32 bit, che è quello che ha chiesto specificamente e quello che ho visto altrimenti buoni compilatori cerca di fare. Alcuni compilatori decideranno che è più conveniente per fare una scrittura 8 bit anziché a 32 bit di scrittura e modificare le istruzioni. Variabile target di lunghezza istruzione stanno andando a rendere questo peggio, come il compilatore sta cercando di risparmiare spazio programma e non solo cicli di memoria su ciò che può assumere il bus di essere. (Ax xor, ax anziché mov eax, 0 per esempio)

E con qualcosa che è in continua evoluzione, come gcc, codice che funziona oggi non ha garanzie di domani di lavoro (non puoi anche compilare alcune versioni di gcc con la versione corrente di gcc). Allo stesso modo il codice che funziona sul compilatore alla scrivania potrebbe non funzionare universalmente per gli altri.

Prendere la indovinare e la sperimentazione fuori di esso, e di creare funzioni di carico e memorizzare.

Il vantaggio collaterale di questo è che si crea un layer bella astrazione, se / quando si vuole simulare il codice in un modo o avere il codice di corsa nello spazio di applicazione invece che sul metallo, o viceversa, le funzioni assembler può essere sostituito con un bersaglio simulato o sostituito con il codice che attraversa una rete a un target con il dispositivo su di esso, ecc.

Bene, in generale non mi aspetto che per ottimizzare l'ordine alta byte se si dispone di registro digitato come un 32 bit volatile. A causa dell'uso della parola chiave volatile il compilatore non può assumere che i valori dei byte di ordine superiore sono 0x00. Così si deve scrivere i 32bit pieno, anche se si sta utilizzando solo un valore letterale 8bit. Non ho mai riscontrato problemi con questo sul 0x86 o processori Ti, o di altri processori embedded. In genere, la parola chiave volatile è sufficiente. Le uniche cose temporali si fanno un po 'strano è che se il processore non supporta nativamente la dimensione della parola che stai cercando di scrivere, ma che non dovrebbe essere un problema sulla 0x86 per un numero di 32 bit.

Anche se sarebbe possibile per il compilatore di generare un flusso di istruzioni che utilizza 4 bit scrive, che non sarebbe un'ottimizzazione sia in tempo di processore o lo spazio di istruzioni su un singolo a 32 bit di scrittura.

Se non si utilizzano i tipi di byte (unsigned char) quando si accede l'hardware, ci sarà una migliore possibilità del compilatore non genera istruzioni per il trasferimento dei dati a 8 bit.

volatile uint32_t* p_reg = 0xCAFE0000;
const uint32_t value = 0x01;  // This trick tells the compiler the constant is 32 bits.
*p_reg |= value;

Si dovrebbe leggere il porto come un valore a 32 bit, modificare il valore, quindi scrivere di nuovo:

uint32_t reg_value = *p_reg;
reg_value |= 0x01;
*p_reg = reg_value;

Dal momento che l'operazione di lettura-modifica-scrittura contro l'hardware è sempre un rischio enorme da fare in diversi istruzioni, la maggior parte dei processori offrono un'istruzione per manipolare un registro / memoria con una sola istruzione che non può essere interrotto.

A seconda del tipo di registro che si sta manipolando, potrebbe cambiare durante la fase di modifica, quindi si dovrebbe scrivere di nuovo un valore falso.

mi sento di raccomandare come dwelch suggerire di scrivere la propria funzione di lettura-modifica-scrittura in assemblea, se questo è fondamentale.

Non ho mai sentito parlare di un compilatore che ottimizza un tipo (facendo una conversione di tipo con lo scopo di ottimizzare). Se si è dichiarato come un Int32 è sempre un Int32 e sarà sempre allineata a destra in memoria. Controllare la documentazione del compilatore per vedere come i vari lavori ottimizzazioni.

Credo di sapere dove la vostra preoccupazione proviene, strutture. Strutture sono generalmente imbottiti per l'allineamento ottimale. Questo è il motivo per cui è necessario un pacchetto di Wrapp #pragma () intorno a loro per farli byte allineati.

Si può solo singolo passaggio attraverso il gruppo e poi si vedrà come il compilatore traduce il codice. Sono abbastanza sicuro che non è cambiato il tuo tipo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top