我正在 32 位 ARM mcu(Atmel SAM4SD32C,Cortex-M4/ARMv7E-M 部件)上实现二进制日志系统,并且正在设计我的数据结构。我的目标是将日志格式描述为打包结构,并简单地将结构与字符数组结合起来,用于写入日志设备(在本例中是通过 FatFS 的 SD 卡)。
基本上,我有一个非常简单的结构:
typedef struct adc_samples_t
{
int32_t adc_samples[6];
uint64_t acq_time;
int8_t overrun;
uint8_t padding_1;
uint8_t padding_2;
uint8_t padding_3;
} __attribute__((packed, aligned(4))) adc_sample_set;
Run Code Online (Sandbox Code Playgroud)
现在,我的架构是32位的,据我了解,访问任何成员/其他/那么该overrun成员应该是32位对齐的,因此不会有额外的开销。此外,该aligned(4)属性应强制结构体的任何实例化位于 32 位对齐的边界上。
然而,编译上面的结构定义会产生一堆警告:
In file included from ../src/main.c:13:0:
<snip>\src\fs\fs-logger.h(10,10): warning: packed attribute causes inefficient alignment for 'adc_samples' [-Wattributes]
int32_t adc_samples[6];
^
<snip>\src\fs\fs-logger.h(12,11): warning: packed attribute causes inefficient alignment for 'acq_time' [-Wattributes]
uint64_t acq_time;
Run Code Online (Sandbox Code Playgroud)
据我所知(我现在意识到这是一个很大的假设),我假设 32 位对齐是在 32 位臂上实现最佳组件定位所需的全部内容。奇怪的是,唯一/不/产生警告的成员是(好吧,ARM 文档说overrun和padding_X成员,我不明白其原因。Byte accesses are always aligned.)
这里究竟发生了什么?我假设(可能是错误的)结构实例化将在 4 字节边界上。编译器是否需要更广泛的对齐(在 8 字节边界上)?
编辑:好的,深入研究 ARM 文档(这里的神奇词是“Cortex-M4 对齐”:
3.3.5. 地址对齐
对齐访问是一种操作,其中字对齐地址用于字、双字或多字访问,或者半字对齐地址用于半字访问。字节访问始终是对齐的。
Cortex-M4 处理器仅支持以下指令的未对齐访问:
LDR、LDRT
LDRH、LDRHT
LDRSH、LDRSHT
STR、STRT
STRH、STRHT所有其他加载和存储指令如果执行未对齐的访问,则会生成UsageFault异常,因此它们的访问必须是地址对齐的。有关UsageFaults 的更多信息,请参阅故障处理。
未对齐的访问通常比对齐的访问慢。此外,某些内存区域可能不支持未对齐访问。因此,ARM 建议程序员确保访问对齐。要捕获意外生成的未对齐访问,请使用配置和控制寄存器中的 UNALIGN_TRP 位,请参阅配置和控制寄存器。
我的 32 位对齐值为何不是字对齐的?用户指南将“对齐”定义如下:
对齐
存储在可被定义数据大小的字节数整除的地址处的数据项称为对齐的。对齐字和半字的地址分别可被四和二整除。因此,术语“字对齐”和“半字对齐”规定分别可被四和二整除的地址。
好吧,在这一点上,我有点确信警告是错误发出的。
我有一个静态定义的结构实例,并且在某一时刻我将其归零:
adc_sample_set running_average;
int accumulated_samples;
inline void zero_average_buf(void)
{
accumulated_samples = 0;
running_average.adc_samples[0] = 0;
running_average.adc_samples[1] = 0;
running_average.adc_samples[2] = 0;
running_average.adc_samples[3] = 0;
running_average.adc_samples[4] = 0;
running_average.adc_samples[5] = 0;
running_average.overrun = 0;
running_average.acq_time = 0;
}
Run Code Online (Sandbox Code Playgroud)
该函数的反汇编如下:
{
004005F8 push {r3, lr}
accumulated_samples = 0;
004005FA movs r2, #0
004005FC ldr r3, [pc, #36]
004005FE str r2, [r3]
running_average.adc_samples[0] = 0;
00400600 ldr r3, [pc, #36]
00400602 str r2, [r3]
running_average.adc_samples[1] = 0;
00400604 str r2, [r3, #4]
running_average.adc_samples[2] = 0;
00400606 str r2, [r3, #8]
running_average.adc_samples[3] = 0;
00400608 str r2, [r3, #12]
running_average.adc_samples[4] = 0;
0040060A str r2, [r3, #16]
running_average.adc_samples[5] = 0;
0040060C str r2, [r3, #20]
running_average.overrun = 0;
0040060E strb.w r2, [r3, #32]
running_average.acq_time = 0;
00400612 movs r0, #0
00400614 movs r1, #0
00400616 strd r0, r1, [r3, #24]
Run Code Online (Sandbox Code Playgroud)
注意r3上面的 is 0x2001ef70,它确实是 4 字节对齐的。r2是字面值0。
str操作码需要4字节对齐。操作strd码也只需要 4 字节对齐,因为它看起来确实是两个连续的 4 字节操作,尽管我不知道它在内部实际上是如何工作的。
如果我故意错误对齐我的结构,以强制执行慢速路径复制操作:
typedef struct adc_samples_t
{
int8_t overrun;
int32_t adc_samples[6];
uint64_t acq_time;
uint8_t padding_1;
uint8_t padding_2;
uint8_t padding_3;
} __attribute__((packed, aligned(8))) adc_sample_set;
Run Code Online (Sandbox Code Playgroud)
我得到以下程序集:
{
00400658 push {r3, lr}
accumulated_samples = 0;
0040065A movs r3, #0
0040065C ldr r2, [pc, #84]
0040065E str r3, [r2]
running_average.adc_samples[0] = 0;
00400660 ldr r2, [pc, #84]
00400662 strb r3, [r2, #1]
00400664 strb r3, [r2, #2]
00400666 strb r3, [r2, #3]
00400668 strb r3, [r2, #4]
running_average.adc_samples[1] = 0;
0040066A strb r3, [r2, #5]
0040066C strb r3, [r2, #6]
0040066E strb r3, [r2, #7]
00400670 strb r3, [r2, #8]
running_average.adc_samples[2] = 0;
00400672 strb r3, [r2, #9]
00400674 strb r3, [r2, #10]
00400676 strb r3, [r2, #11]
00400678 strb r3, [r2, #12]
running_average.adc_samples[3] = 0;
0040067A strb r3, [r2, #13]
0040067C strb r3, [r2, #14]
0040067E strb r3, [r2, #15]
00400680 strb r3, [r2, #16]
running_average.adc_samples[4] = 0;
00400682 strb r3, [r2, #17]
00400684 strb r3, [r2, #18]
00400686 strb r3, [r2, #19]
00400688 strb r3, [r2, #20]
running_average.adc_samples[5] = 0;
0040068A strb r3, [r2, #21]
0040068C strb r3, [r2, #22]
0040068E strb r3, [r2, #23]
00400690 strb r3, [r2, #24]
running_average.overrun = 0;
00400692 mov r1, r2
00400694 strb r3, [r1], #25
running_average.acq_time = 0;
00400698 strb r3, [r2, #25]
0040069A strb r3, [r1, #1]
0040069C strb r3, [r1, #2]
0040069E strb r3, [r1, #3]
004006A0 strb r3, [r1, #4]
004006A2 strb r3, [r1, #5]
004006A4 strb r3, [r1, #6]
004006A6 strb r3, [r1, #7]
Run Code Online (Sandbox Code Playgroud)
因此,很明显,我通过原始结构定义获得了正确的对齐复制行为,尽管编译器显然错误地警告这将导致访问效率低下。
| 归档时间: |
|
| 查看次数: |
4169 次 |
| 最近记录: |