Domanda

Sto cercando di eseguire un 32bit minore di lettura sul bus PCI ad un chip VME-bridge (Tundra universo II), che sarà poi sul bus VME e prelevati dal target.

L'applicazione VME di destinazione accetta solo D32 (una larghezza di dati lettura di 32bit) e ignorerà qualsiasi altra cosa.

Se uso la struttura campo di bit mappato su una finestra VME (nmap'd nella memoria principale) posso leggere i campi di bit> 24 bit, ma niente di meno non riesce. vale a dire: -

struct works {
    unsigned int a:24;
};

struct fails {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct main {
    works work;
    fails fail;
}
volatile *reg = function_that_creates_and_maps_the_vme_windows_returns_address()

Questo dimostra che il struct funziona viene letto come un 32 bit, ma una lettura tramite fallisce struct di a per esempio, reg -> fail.a è sempre scomposto fino a un po 'X lettura. (Dove X potrebbe essere 16 o 8?)

Quindi le domande sono:
a) casi in cui è presente ridimensionato? Compiler? OS? o il chip Tundra?
b) Qual è la dimensione effettiva della operazione di lettura eseguita?

I basiclly voglio escludere tutto, ma il chip. La documentazione su quel è sul web, ma se si può dimostrare che la larghezza dati richiesti tramite il bus PCI è 32bit allora il problema può essere imputato sul chip Tundra!

modifica: -
esempio concreto, codice era: -

struct SVersion
{
    unsigned title         : 8;
    unsigned pecversion    : 8;
    unsigned majorversion  : 8;
    unsigned minorversion  : 8;
} Version;

Così ora ho cambiato a questo: -

union UPECVersion
{
    struct SVersion
    {
        unsigned title         : 8;
        unsigned pecversion    : 8;
        unsigned majorversion  : 8;
        unsigned minorversion  : 8;
    } Version;
    unsigned int dummy;
};

E la struct principale base: -

typedef struct SEPUMap
{
    ...
    ...
    UPECVersion PECVersion;

};

Quindi devo ancora cambiare tutto il mio codice di base

// perform dummy 32bit read
pEpuMap->PECVersion.dummy;

// get the bits out
x = pEpuMap->PECVersion.Version.minorversion;

E come faccio a sapere se la seconda lettura wont effettivamente fare un vero e proprio letto di nuovo, come il mio codice originale ha fatto? (Invece di usare i bit già letto tramite l'unione!)

È stato utile?

Soluzione

A titolo di esempio, il kernel di Linux ha funzioni inline che esplicitamente handle di memoria mappata IO legge e scrive. In kernel più recenti è un grande involucro macro che si riduce ad un'istruzione movl assembly inline, ma vecchi kernel è stato definito in questo modo:

#define readl(addr) (*(volatile unsigned int *) (addr))
#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))

Altri suggerimenti

Il compilatore si sta adeguando la dimensione della vostra struttura per un multiplo della sua impostazione di allineamento memoria. Quasi tutti i compilatori moderni fanno questo. Su alcuni processori, variabili e le istruzioni sono per iniziare su indirizzi di memoria che sono multipli di un certo valore di allineamento memoria (spesso 32-bit o 64 bit, ma l'allineamento dipende l'architettura del processore). La maggior parte dei processori moderni non richiedere l'allineamento di memoria più - ma quasi tutti vedere sostanziale beneficio delle prestazioni da esso. Così i compilatori allineare i dati per voi per l'incremento delle prestazioni.

Tuttavia, in molti casi (come ad esempio il vostro) questo non è il comportamento desiderato. La dimensione della vostra struttura, per vari motivi, può rivelarsi estremamente importante. In questi casi, ci sono vari modi per aggirare il problema.

Una possibilità è quella di forzare il compilatore di utilizzare diverse impostazioni di allineamento. Le opzioni per fare questo varia da compilatore a compilatore, quindi dovrete controllare la documentazione. Di solito è una #pragma di qualche tipo. Su alcuni compilatori (i compilatori Microsoft, per esempio) è possibile modificare l'allineamento di memoria per solo una piccola sezione di codice. Per esempio (in VC ++):

#pragma pack(push)      // save the current alignment
#pragma pack(1)         // set the alignment to one byte
// Define variables that are alignment sensitive
#pragma pack(pop)       // restore the alignment

Un'altra opzione è quella di definire le variabili in altri modi. tipi intrinseci non vengono ridimensionati basano su allineamento, così invece del 24-bit bitfield, un altro approccio è quello di definire la variabile come una matrice di byte.

Infine, si può semplicemente lasciare che i compilatori rendono le struct qualsiasi dimensione che vogliono e registrano manualmente la dimensione che avete bisogno di lettura / scrittura. Finché non si è concatenare strutture insieme, questo dovrebbe funzionare bene. Ricordate, però, che il compilatore è dandovi le strutture imbottiti sotto il cofano, quindi se si fanno una struttura più ampia che comprende, per esempio, un funziona e fallisce struct, ci sarà imbottito bit tra di loro che potrebbero causare problemi.

Nella maggior parte dei compilatori, che sta per essere rammendare quasi impossibile per creare un tipo di dati più piccolo di 8 bit. La maggior parte delle architetture solo non pensano in questo modo. Questo non dovrebbe essere un problema enorme, perché la maggior parte dei dispositivi hardware che utilizzano i tipi di dati di più piccolo di 8-bit finiscono per organizzare i loro pacchetti in modo tale che ancora sono disponibili in multipli di 8 bit, in modo da poter fare le manipolazioni bit per estrarre o codificare i valori sul flusso di dati in quanto lascia o entra in gioco.

Per tutti i motivi sopra elencati, un sacco di codice che funziona con dispositivi hardware come questo lavoro con array di byte prime e solo codificare i dati all'interno degli array. Nonostante abbia perso un sacco di comodità di moderni costrutti del linguaggio, finisce per essere solo più facile.

Mi chiedo circa il valore di sizeof(struct fails). E '1? In questo caso, se si esegue la lettura da dereferenziazione un puntatore a un struct fails, sembra corretto di emettere un D8 leggere sul bus VME.

Si può cercare di aggiungere un unsigned int unused:29; campo al struct fails.

La dimensione di una struct non è uguale alla somma delle dimensioni dei suoi campi, tra cui i campi di bit. compilatori sono consentiti, dalla specifiche del linguaggio C e C ++, per inserire imbottitura tra i campi in un struct. Imbottitura è spesso inserito per fini di allineamento.

Il metodo comune annegati programmazione di sistemi è leggere i dati come intero senza segno quindi utilizzare mascheramento di bit per recuperare i bit interessanti. Ciò è dovuto alla regola di cui sopra che ho detto e il fatto che non esiste un parametro compilatore standard per i campi di "imballaggio" in una struttura.

Suggerisco la creazione di un oggetto (o class struct) per l'interfacciamento con l'hardware. Lasciate che l'oggetto leggere i dati, quindi estrarre i bit come membri bool. Questo mette l'implementazione più vicino all'hardware. Il restante software non deve preoccuparsi come i bit sono implementati.

Quando si definiscono le posizioni di campo bit / costanti con nome, suggerisco questo formato:

#define VALUE (1 << BIT POSITION)
// OR
const unsigned int VALUE = 1 << BIT POSITION;

Questo formato è più leggibile e ha il compilatore eseguire l'aritmetica. Il calcolo avviene durante la compilazione e non ha alcun impatto in fase di esecuzione.

Ian - se si vuole essere sicuri per quanto riguarda la dimensione delle cose che stai lettura / scrittura Io suggerirei di non utilizzare le strutture come questo per farlo - è possibile che il sizeof del fallisce struct è solo 1 byte - il compilatore è libero di decidere quello che dovrebbe essere basata su ottimizzazioni ecc- io suggerirei la lettura / scrittura in modo esplicito utilizzando int di o in generale le cose che è necessario assicurare le dimensioni e quindi fare qualcos'altro come la conversione a un sindacato / struct dove don 't hanno queste limitazioni.

E 'il compilatore che decide cosa dimensioni letto per il rilascio. Per forzare un 32 bit di lettura, è possibile utilizzare un union:

union dev_word {
    struct dev_reg {
        unsigned int a:1;
        unsigned int b:1;
        unsigned int c:1;
    } fail;
    uint32_t dummy;
};

volatile union dev_word *vme_map_window();

Se la lettura del sindacato tramite un puntatore volatili qualificato non è sufficiente per forzare una lettura di tutta l'Unione (penserei che sarebbe - ma che potrebbe essere compilatore-dipendente), allora si potrebbe utilizzare una funzione di fornire il riferimento indiretto richiesta:

volatile union dev_word *real_reg; /* Initialised with vme_map_window() */

union dev_word * const *reg_func(void)
{
    static union dev_word local_copy;
    static union dev_word * const static_ptr = &local_copy;

    local_copy = *real_reg;
    return &static_ptr;
}

#define reg (*reg_func())

... quindi (per la compatibilità con il codice esistente) i vostri accessi sono fatto come:

reg->fail.a

Il metodo descritto in precedenza di usare il flag gcc -fstrict volatile-bitfields e definente variabili BITFIELD come funziona U32 volatile, ma il numero totale di bit definiti deve essere maggiore di 16.

Ad esempio:

typedef     union{
    vu32    Word;
    struct{
        vu32    LATENCY     :3;
        vu32    HLFCYA      :1;
        vu32    PRFTBE      :1;
        vu32    PRFTBS      :1;  
    };
}tFlashACR;
.
tFLASH* const pFLASH    =   (tFLASH*)FLASH_BASE;
#define FLASH_LATENCY       pFLASH->ACR.LATENCY
.
FLASH_LATENCY = Latency;

provoca gcc per generare codice

.
ldrb r1, [r3, #0]
.

che è un byte leggere. Tuttavia, la modifica del typedef per

typedef     union{
    vu32    Word;
    struct{
        vu32    LATENCY     :3;
        vu32    HLFCYA      :1;
        vu32    PRFTBE      :1;
        vu32    PRFTBS      :1;
        vu32                :2;

        vu32    DUMMY1      :8;

        vu32    DUMMY2      :8;
    };
}tFlashACR;

cambia il codice risultante a

.
ldr r3, [r2, #0]
.

Credo che l'unica soluzione è quella di
1) modificare / creare il mio struct principale, come tutti gli interi a 32 bit (long senza segno)
2) osservano i miei struct bit-field originali
3) ogni accesso ho bisogno,
3.1) Devo leggere il membro struct come una parola a 32 bit, e la gettò nel struct campo bit,
3.2) articolo l'elemento campo bit ho bisogno. (E in scrittura, impostare questo bit-field, e scrivere la parola di nuovo!)

(1) che è una stessa cosa, perché poi perdo i tipi intrinseci che ogni membro della struct "main / SEPUMap" sono.

soluzione end: -
Invece di: -

printf("FirmwareVersionMinor: 0x%x\n", pEpuMap->PECVersion);

Questo: -

SPECVersion ver = *(SPECVersion*)&pEpuMap->PECVersion;

printf("FirmwareVersionMinor: 0x%x\n", ver.minorversion);

L'unico problema che ho è scrittura! (Scritture sono ora Lettura / modifica / scrittura!)

// Read - Get current
_HVPSUControl temp = *(_HVPSUControl*)&pEpuMap->HVPSUControl;

// Modify - set to new value
temp.OperationalRequestPort = true;

// Write
volatile unsigned int *addr = reinterpret_cast<volatile unsigned int*>(&pEpuMap->HVPSUControl);

*addr = *reinterpret_cast<volatile unsigned int*>(&temp);

resta che riordinare quel codice su in un metodo!

#define writel(addr, data) ( *(volatile unsigned long*)(&addr) = (*(volatile unsigned long*)(&data)) )

Ho avuto lo stesso problema su ARM usando il compilatore GCC, dove scrivere in memoria è solo attraverso i byte, piuttosto che la parola a 32 bit.

La soluzione è definire bit-field utilizzando uint32_t volatili (dimensione o necessario per scrivere):

union {
    volatile uint32_t XY;
    struct {
        volatile uint32_t XY_A : 4;
        volatile uint32_t XY_B : 12;
    };
};

, ma durante la compilazione è necessario aggiungere al gcc o g ++ questo parametro:

-fstrict-volatile-bitfields

di più nella documentazione gcc.

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