结构和位域的奇怪行为

时间:2018-11-22 09:36:17

标签: c gcc assembly struct bit-fields

我正在尝试修改寄存器中的位域。这是我定义了位域的结构:

when(existingItems.size()).thenReturn(2);

当我使用以下行时:

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)

它不仅更改bitfield1,而且更改bitfield2。该寄存器的值为GROUP.myRegister.B.bitfield1 = 0x60;

代码被编译为以下汇编代码:

0x00006060

如果我尝试直接进行寄存器操作:

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]

寄存器的值为int volatile * reg = (int *) 0x400FE160; *reg = 0x60

我正在使用GCC编译器。

为什么在使用struct和bitfields时值会重复?

编辑

我发现了另一种奇怪的行为:

0x00000060

我更改寄存器值(带有struct和bitfield)的方法被编译为:

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)

1 个答案:

答案 0 :(得分:4)

啊,明白了。编译器使用strb两次将两个最低有效字节写入特殊功能寄存器。但是由于不支持对特殊功能寄存器的字节写入,因此硬件每次都会执行一次字写入(大概32位)。难怪它不起作用!

关于如何解决此问题,取决于编译器以及它对SFR的了解程度。作为快速而肮脏的修复,您可以仅对R使用位操作;代替

GROUP.myRegister.B.bitfield1 = 0x60;

使用例如

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

PS另一种可能性:您似乎已经关闭了优化(我在其中看到了一条多余的ldrb r2,[r3,#00]指令)。也许如果您打开它,编译器将会发挥作用吗?值得一试...

PPS,请将uint64_t更改为uint32_t。这让我的牙齿受伤!

PPPS来考虑一下,packed可能会抛弃编译器,导致它假定位域结构可能未按字对齐(并因此强制逐字节访问)。您是否尝试过删除它?