Struct and bitfield strange behaviour

1

I am trying to modify bitfields in register. Here is my struct with bitfields defined:

struct GROUP_tag
{
    ...
    union
    {
        uint32_t R;
        struct
        {
            uint64_t bitfield1:10;
            uint64_t bitfield2:10;
            uint64_t bitfield3:3;
            uint64_t bitfield4:1;
        } __attribute__((packed)) B;
    } __attribute__((aligned(4))) myRegister;
    ...
}

#define GROUP (*(volatile struct GROUP_tag *) 0x400FE000)

When I use the following line:

GROUP.myRegister.B.bitfield1 = 0x60;

it doesn't change only bitfield1, but bitfield2 as well. The register has value 0x00006060.

Code gets compiled to the following assembly code:

ldr r3,[pc,#005C]
add r3,r3,#00000160
ldrb r2,[r3,#00]
mov r2,#00
orr r2,#00000060
strb r2,[r3,#00]
ldrb r2,[r3,#01]
bic r2,r2,#00000003
strb r2,[r3,#01]

If I try with direct register manipulation:

int volatile * reg = (int *) 0x400FE160;
*reg = 0x60

the value of register is 0x00000060.

I am using GCC compiler.

Why is the value duplicated when I use struct and bitfields?

EDIT

I found another strange behaviour:

GROUP.myRegister.R = 0x12345678; // value of register is 0x00021212
*reg = 0x12345678; // value of register is 0x0004567, this is correct (I am programming microcontroller and some bits in register can't be changed)

My approach to change register value (with struct and bitfield) gets compiled to:

ldr r3,[pc,#00B4]
ldrb r2,[r3,#0160]
mov r2,#00
orr r2,#00000078
strb r2,[r3,#0160]
ldrb r2,[r3,#0160]
mov r2,#00
orr r2,#00000056
strb r2,[r3,#0161]
ldrb r2,[r3,#0162]
mov r2,#00
orr r2,#00000034
strb r2,[r3,#0162]
ldrb r2,[r3,#0163]
mov r2,#00
orr r2,#00000012
strb r2,[r3,#0163]
c
gcc
assembly
struct
bit-fields
asked on Stack Overflow Nov 22, 2018 by Mark • edited Nov 22, 2018 by Mark

1 Answer

4

Ah, I get it. The compiler is using strb twice to write the two least significant bytes to a Special Function Register. But the hardware is performing a word write (presumably 32 bits) each time, because byte writes to Special Function Registers are unsupported. No wonder it doesn't work!

As to how you can fix this, that depends on your compiler, and how much it knows about SFRs. As a quick and dirty fix, you can just use bit manipulation on R; instead of

GROUP.myRegister.B.bitfield1 = 0x60;

use e.g.

GROUP.myRegister.R = (GROUP.myRegister.R & ~0x3FF) | 0x60;

PS Another possibility: it looks like you have turned off optimisation (I see a redundant ldrb r2,[r3,#00] instruction in there). Perhaps if you turn it on, the compiler will come to its senses? Worth a try...

PPS Please change uint64_t to uint32_t. It's making my teeth hurt!

PPPS Come to think of it, that packed may be throwing the compiler off, causing it to assume that the bitfield struct may not be word-aligned (and thus forcing byte-by-byte acesss). Have you tried removing it?

answered on Stack Overflow Nov 22, 2018 by TonyK • edited Nov 22, 2018 by TonyK

User contributions licensed under CC BY-SA 3.0