2

使用架构:Xtensa-LX6

我有以下定义:

volatile typedef struct{
    union{
        struct{
            uint32_t FIELD1:16;
            uint32_t FIELD2:12;
            uint32_t FIELD3:4;
        };
        uint32_t _;
    }REGISTER_1; //0x00 offset, 4bytes width
}registers_t;
static registers_t* HARDWARE = (registers_t *)0x3FF43000;

然后我在代码的某处有一行:

HARDWARE->REGISTER_1.FIELD1 = 0b10101u;

此行由 xtensa gcc 编译为以下汇编代码(美化):

HARDWARE:
    .word   1073102848

THE_LINE_OF_CODE:
    .literal .LC120, HARDWARE ; .LC120 now holds the address of HARDWARE - 0x3FF43000
    .align  4                 ; assembler directive
    l32r    a2, .LC120        ; reg_a2 <= HARDWARE
    l32i.n  a2, a2, 0         ; reg_a2 <= memory_at(reg_a2) //reg_2a <= 0x3FF43000
    movi.n  a3, 0b10101       ; reg_a3 <= 0b10101
    memw                      ; wait for memory operations to finish
    s16i    a3, a2, 0         ; memory_at(reg_a2+0) <= a3 // *(0x3FF43000) = 0b10101

如您所见,汇编程序完全忽略了寄存器字段的其余部分,它只是假设将写入低 16 位,并在那里写入 16 位值。问题是 xtensa 架构中的寄存器总是 4 位宽并且如此对齐。因此,如果我们以二进制形式写入寄存器内容: REGISTER1: 0b----------------1111111111111111,“-”部分将被删除,而不是保留其值。寄存器被损坏,并且发生了大量错误。

这可以通过插入__attribute__((packed))结构声明来避免:

    union{
        struct{
            uint32_t FIELD1:16;
            uint32_t FIELD2:12;
            uint32_t FIELD:4;
        }__attribute__((packed));
        uint32_t _;
    }REGISTER_1; //0x00 offset, 4bytes width

现在汇编器知道我们在寻找什么,得到的汇编是(美化的):

HARDWARE:
    .word   1073102848

THE_LINE_OF_CODE:
    .literal .LC120, HARDWARE ; .LC120 now holds the address of HARDWARE - 0x3FF43000
    .literal .LC121, 0xFFFF0000 ; A bit mask for our portion
    .align  4                 ; assembler directive
    l32r    a2, .LC120        ; a2 <= HARDWARE
    l32i.n  a2, a2, 0         ; a2 <= memory_at(a2) //2a <= 0x3FF43000
    memw                      ; wait for memory operations to finish
    l32i.n  a4, a2, 0         ; a4 <= memory_at(a2 + 0 the struct offset)
    l32r    a3, .LC121        ; a3 <= 0xFFFF0000
    and a4, a4, a3            ; a4 <= a4 & a3 (zeroes the lower 16 bits we will be writing to, preserves rest)
    movi.n  a3, 0b10101       ; a3 <= 0b10101
    or  a3, a4, a3            ; a3 <= a4 | a3 (writes to the lower 16 bits)
    memw                      ; wait for memory operations to finish
    s32i.n  a3, a2, 0         ; memory_at(a2 + 0 the struct offset) <= a3

问题解决了!伟大的。但..

真正的问题从这里开始:

我的寄存器定义中可以有许多结构:

volatile typedef struct{
    union{
        struct{...}__attribute__((packed));
        uint32_t _;
    }REGISTER_1;
    union{
        struct{...};
        uint32_t _;
    }REGISTER_2;
    .
    .
    .
}registers_t;
static registers_t* HARDWARE = (registers_t *)0x3FF43000;

如何打包结构,以便保留整个 typedef 中的对齐方式,并正确编译每个位域操作?到处放置__attribute__((packed))可能会起作用,但它会破坏对齐 - sizeoftypedef 的整体大于要求。

4

0 回答 0