заставить битовое поле читать до 32 бит - PullRequest
2 голосов
/ 04 декабря 2009

Я пытаюсь выполнить менее 32-битное считывание по шине PCI на микросхему моста VME (Tundra Universe II), которая затем перейдет на шину VME и будет забрана целью.

Целевое приложение VME принимает только D32 (чтение данных шириной 32 бита) и игнорирует все остальное.

Если я использую структуру битовых полей, отображаемую в окне VME (nmap'd в основную память), я МОГУ прочитать битовые поля> 24 бит, но все, что меньше, дает сбой. т.е.: -

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()

Это показывает, что структура работает читается как 32-битная, но чтение через не удается структура a , например, reg-> fail .a переводится в X-бит. (где Х может быть 16 или 8?)

Итак, вопросы:
а) Где это уменьшено? Компилятор? ОПЕРАЦИОННЫЕ СИСТЕМЫ? или чип тундры?
б) Каков фактический размер выполненной операции чтения?

Я просто хочу исключить все, кроме чипа. Документация по этому вопросу находится в Интернете, но если можно доказать, что запрашиваемая по шине PCI ширина данных составляет 32 бита, то проблема может быть связана с чипом Tundra!

редактирование: -
Конкретный пример, код был: -

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

Так что теперь я изменил это на: -

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

И базовая основная структура: -

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

};

Так что мне все еще нужно изменить весь базовый код

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

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

И как я узнаю, что при втором чтении на самом деле снова не произойдет реальное чтение, как это сделал мой исходный код? (Вместо использования уже прочитанных битов через объединение!)

Ответы [ 9 ]

6 голосов
/ 04 декабря 2009

Ваш компилятор корректирует размер вашей структуры, кратный его настройке выравнивания памяти. Почти все современные компиляторы делают это. На некоторых процессорах переменные и инструкции должны начинаться с на адресах памяти, кратных некоторому значению выравнивания памяти (часто 32-разрядным или 64-разрядным, но выравнивание зависит от архитектуры процессора). Большинство современных процессоров не требуют выравнивания памяти больше - но почти все они видят в существенном выигрыше в производительности. Таким образом, компиляторы выровняют ваши данные для повышения производительности.

Однако во многих случаях (например, в вашем) это не то поведение, которое вам нужно. Размер вашей структуры по разным причинам может оказаться чрезвычайно важным. В этих случаях существуют различные способы решения проблемы.

Один из вариантов - заставить компилятор использовать разные настройки выравнивания. Варианты для этого варьируются от компилятора к компилятору, поэтому вам придется проверить свою документацию. Обычно это какая-то прагма. В некоторых компиляторах (например, в компиляторах Microsoft) выравнивание памяти можно изменить только для очень небольшого фрагмента кода. Например (в 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

Другой вариант - определить ваши переменные другими способами. Внутренние типы не меняются в зависимости от выравнивания, поэтому вместо вашего 24-битного битового поля другой подход заключается в определении вашей переменной в виде массива байтов.

Наконец, вы можете просто позволить компиляторам создавать структуры любого желаемого размера и вручную записывать размер, необходимый для чтения / записи. Пока вы не объединяете структуры вместе, это должно работать нормально. Помните, однако, что компилятор предоставляет вам мягкие структуры под капотом, поэтому, если вы создадите более крупную структуру, которая включает, скажем, works и fail struct, будет биты между ними, которые могут вызвать проблемы.

На большинстве компиляторов почти невозможно создать тип данных меньше 8 бит. Большинство архитектур просто так не думают. Это не должно быть большой проблемой, потому что большинство аппаратных устройств, которые используют типы данных менее 8 бит, в конечном итоге упорядочивают свои пакеты таким образом, что они все еще имеют 8-битные кратные, поэтому вы можете выполнять битовые манипуляции для извлечения или закодируйте значения в потоке данных, когда он уходит или входит.

По всем причинам, перечисленным выше, большая часть кода, который работает с аппаратными устройствами, такими как эта, работает с необработанными байтовыми массивами и просто кодирует данные внутри массивов. Несмотря на то, что теряются многие удобства современных языковых конструкций, в итоге все становится проще.

2 голосов
/ 05 декабря 2009

Размер struct не равен сумме размера его полей, включая битовые поля. Компиляторам разрешено, согласно спецификациям языка C и C ++, вставлять заполнение между полями в struct. Обивка часто вставляется для выравнивания.

Обычный метод программирования встроенных систем состоит в том, чтобы считать данные как целое число без знака , а затем использовать битовую маскировку для получения интересных битов. Это связано с приведенным выше правилом, а также с тем фактом, что стандартного параметра компилятора для «упаковки» полей в структуре не существует.

Я предлагаю создать объект ( class или struct) для взаимодействия с оборудованием. Позвольте объекту прочитать данные, а затем извлечь биты как bool членов. Это ставит реализацию как можно ближе к аппаратному обеспечению. Оставшееся программное обеспечение не должно заботиться о том, как биты реализованы.

При определении позиций битовых полей / именованных констант, я предлагаю этот формат:

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

Этот формат более читабелен, и компилятор выполняет арифметику. Расчет происходит во время компиляции и не оказывает влияния во время выполнения.

2 голосов
/ 04 декабря 2009

Меня интересует значение sizeof(struct fails). Это 1? В этом случае, если вы выполняете чтение, разыменовывая указатель на struct fails, выглядит правильным выпустить чтение D8 на шине VME.

Вы можете попробовать добавить поле unsigned int unused:29; к вашему struct fails.

1 голос
/ 11 марта 2015

Описанный ранее метод использования флага gcc -fstrict-volatile-bitfields и определения переменных битовых полей как volatile u32 работает, но общее число определенных бит должно быть больше 16.

Например:

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;

заставляет gcc генерировать код

.
ldrb r1, [r3, #0]
.

, который читается в байтах. Однако изменение typedef на

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;

изменяет результирующий код на

.
ldr r3, [r2, #0]
.
1 голос
/ 05 декабря 2009

Компилятор решает, какой размер читать, чтобы выпустить. Чтобы вызвать 32-битное чтение, вы можете использовать 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();

Если чтения объединения через указатель с изменчивой квалификацией недостаточно для принудительного чтения всего объединения (я думаю, что это будет, но это может зависеть от компилятора), тогда вы можете использовать функцию требуемая косвенность:

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())

... тогда (для совместимости с существующим кодом) ваш доступ осуществляется как:

reg->fail.a
1 голос
/ 04 декабря 2009

В качестве примера, ядро ​​Linux имеет встроенные функции, которые явно обрабатывают операции чтения и записи с отображением в памяти. В более новых ядрах это большая оболочка макроса, которая сводится к инструкции movl для встроенной сборки, но в более старых ядрах она была определена так:

#define readl(addr) (*(volatile unsigned int *) (addr))
#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
1 голос
/ 04 декабря 2009

Ян - если вы хотите быть уверены в размере вещей, которые вы читаете / пишете, я бы предложил не использовать такие структуры, как это, - возможно, размер структуры fails составляет всего 1 байт - компилятор свободен решать, что он должен основывать на оптимизации и т. д. Я бы предложил читать / писать явно, используя int или вообще вещи, которые вам нужны, чтобы гарантировать размеры, а затем делать что-то еще, например, преобразование в union / struct, где вы делаете не имеют этих ограничений.

0 голосов
/ 02 сентября 2014

У меня была такая же проблема на ARM с использованием компилятора GCC, где запись в память осуществляется только через байты, а не 32-битное слово.

Решение состоит в том, чтобы определить битовые поля, используя volatile uint32_t (или требуемый размер для записи):

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

но при компиляции вам нужно добавить в gcc или g ++ этот параметр:

-fstrict-volatile-bitfields

больше в документации gcc.

0 голосов
/ 07 декабря 2009

Я считаю, что единственное решение -
1) редактировать / создавать мою основную структуру как все 32-битные целые числа (беззнаковые длинные)
2) сохранить мои оригинальные структуры битового поля
3) каждый доступ, который мне требуется,
3.1) Я должен прочитать член структуры как 32-битное слово и преобразовать его в структуру битового поля,
3.2) прочитать нужный мне элемент битового поля. (а для записи установите это битовое поле и запишите слово назад!)

(1) Это то же самое, потому что тогда я теряю внутренние типы, которыми являются каждый член структуры "main / SEPUMap".

Конечное решение: -
Вместо: -

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

Это: -

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

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

Единственная проблема, которую я имею, - это писать! (Пишет теперь Чтение / Модификация / Пишет!)

// 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);

Просто приведите этот код в метод!

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