Domanda

Su un sistema incorporato abbiamo una configurazione che ci consente di leggere dati arbitrari su un'interfaccia della riga di comando a fini diagnostici. Per la maggior parte dei dati, funziona bene, usiamo memcpy () per copiare i dati all'indirizzo richiesto e rispedirli attraverso una connessione seriale.

Tuttavia, per i registri hardware a 16 bit, memcpy () causa alcuni problemi. Se provo ad accedere a un registro hardware a 16 bit utilizzando due accessi a 8 bit, il byte di ordine superiore non viene letto correttamente.

Qualcuno ha riscontrato questo problema? Sono un ragazzo di "alto livello" (C # / Java / Python / Ruby) che si sta avvicinando all'hardware e questo è un territorio alieno.

Qual è il modo migliore per affrontarlo? Vedo alcune informazioni, in particolare, un post un po 'confuso [per me] qui . L'autore di questo post ha esattamente lo stesso problema che faccio ma odio implementare una soluzione senza comprendere appieno quello che sto facendo.

Ogni luce che puoi far luce su questo problema è molto apprezzata. Grazie!

È stato utile?

Soluzione

Oltre a quanto Eddie ha detto , in genere è necessario utilizzare un puntatore volatile per leggere un registro hardware (presupponendo un registro mappato in memoria, il che non è il caso di tutti i sistemi, ma sembra vero per il tuo). Qualcosa del tipo:

// using types from stdint.h to ensure particular size values
// most systems that access hardware registers will have typedefs
// for something similar (for 16-bit values might be uint16_t, INT16U,
// or something)

uint16_t volatile* pReg = (int16_t volatile*) 0x1234abcd;  // whatever the reg address is

uint16_t val = *pReg;  // read the 16-bit wide register

Ecco una serie di articoli di Dan Saks che dovrebbero fornirti praticamente tutto ciò che devi sapere per essere in grado di utilizzare in modo efficace i registri mappati in memoria in C / C ++:

Altri suggerimenti

Ogni registro in questo hardware è esposto come un array a due byte, il primo elemento è allineato a un limite di due byte (il suo indirizzo è pari). memcpy () esegue un ciclo e copia un byte ad ogni iterazione, quindi copia da questi registri in questo modo (tutti i loop sono srotolati, il carattere è un byte):

*((char*)target) = *((char*)register);// evenly aligned - address is always even
*((char*)target + 1) = *((char*)register + 1);//oddly aligned - address is always odd

Tuttavia, la seconda riga funziona in modo errato per alcuni motivi specifici dell'hardware. Se copi due byte alla volta anziché uno alla volta, viene invece eseguito in questo modo (int breve è due byte):

*((short int*)target) = *((short*)register;// evenly aligned

Qui copi due byte in una sola operazione e il primo byte viene allineato uniformemente. Dal momento che non esiste una copia separata da un indirizzo stranamente allineato, funziona.

Il memcpy modificato controlla se gli indirizzi sono venutamente allineati e copia in blocchi di byte di rimorchio se lo sono.

Se si richiede l'accesso a registri hardware di dimensioni specifiche, sono disponibili due opzioni:

  • Scopri come il tuo compilatore C genera codice in modo da poter usare il tipo intero appropriato per accedere alla memoria, oppure
  • Incorporare alcuni assembly per eseguire l'accesso con la dimensione corretta di byte o parola.

La lettura dei registri hardware può avere effetti collaterali, a seconda del registro e della sua funzione, ovviamente, quindi è importante accedere ai registri hardware con l'accesso di dimensioni adeguate in modo da poter leggere l'intero registro in una sola volta.

Di solito è sufficiente utilizzare un tipo intero della stessa dimensione del registro. Sulla maggior parte dei compilatori , un corto è di 16 bit.

void wordcpy(short *dest, const short *src, size_t bytecount)
{
    int i;
    for (i = 0;  i < bytecount/2;  ++i)
        *dest++ = *src++;
}

Penso che tutti i dettagli siano contenuti in quella discussione che hai pubblicato, quindi proverò a scomporlo un po ';

In particolare;

If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.

Quindi il problema qui è che i registri hardware riportano il valore corretto solo se i loro valori vengono letti in una sola lettura a 16 bit. Ciò equivarrebbe a fare;

uint16 value = *(regAddress);

Legge dall'indirizzo nel registro valori usando una sola lettura a 16 byte. D'altra parte hai memcpy che sta copiando i dati un singolo byte alla volta. Qualcosa del tipo;

while (n--)
{
  *(uint8*)pDest++ = *(uint8*)pSource++;
}

Quindi questo fa sì che i registri vengano letti 8 bit (1 byte) alla volta, risultando non validi i valori.

La soluzione pubblicata in quel thread consiste nell'utilizzare una versione di memcpy che copierà i dati usando letture a 16 bit in cui l'origine e la destinazione sono allineate a 6 bit.

Cosa devi sapere? Hai già trovato un post separato che lo spiega. Apparentemente la documentazione della CPU richiede che si accede ai registri hardware a 16 bit con letture e scritture a 16 bit, ma l'implementazione di memcpy utilizza letture / scritture a 8 bit. Quindi non lavorano insieme.

La soluzione è semplicemente di non utilizzare memcpy per accedere a questo registro. Invece, scrivi la tua routine che copia i valori a 16 bit.

Non sono sicuro di quale sia la domanda: penso che il post abbia la soluzione giusta. Come hai affermato, il problema è che la routine memcpy () standard legge un byte alla volta, che non funziona correttamente per i registri hardware mappati in memoria. Questa è una limitazione del processore - semplicemente non c'è modo di ottenere un valore valido che legge un byte alla volta.

La soluzione suggerita è scrivere il tuo memcpy () che funziona solo su indirizzi allineati a parole e legge parole a 16 bit alla volta. Questo è abbastanza semplice: il collegamento fornisce sia una versione c che una versione assembly. L'unico problema è assicurarti di fare sempre le copie a 16 bit da un indirizzo validamente allineato. Puoi farlo in 2 modi: usa i comandi del linker o i pragmi per assicurarti che le cose siano allineate o aggiungi un caso speciale per il byte extra nella parte anteriore di un buffer non allineato.

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