使用架构: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))
可能会起作用,但它会破坏对齐 - sizeof
typedef 的整体大于要求。