我真的不知道如何简洁地问这个问题,所以如果这个问题已经被问到并回答,我深表歉意.

我正在一个ARM平台上工作,该平台具有对外围设备先进先出的32位伪寄存器访问权限. 根据读取或写入的大小,会向FIFA中删除或添加可变数量的字节.

我编写了以下C代码来执行16位(半字)读取:

a_u16_var = *(uint16_t*)&FIFO_REG;

并确认它确实导致对象文件中出现ldrh操作码.

这有保证吗? 如果我对uint32_tuint8_t遵循相同的模式,我可以依赖编译器分别生成ldr/strldrb/strb吗? 如果没有,确保适当大小访问的最佳方法是什么? 在线组装?

编辑:更多平台细节:

  • 核心:ARM Cortex-M33(具体来说是STM 32 H563 ZZ)
  • 编译器:GNUarm-no-eabi-* 版本11.3.1(用于STM 32 11.3的NU工具)
  • 操作系统:FreeRTOS,如果算的话

推荐答案

对于volatile,是的,如果是机器本地可以执行的大小,那么GCC将try 仅使用宽度与volatile访问类型相匹配的单个访问.

我不记得我在哪里读到了这篇文章;它不在https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html中(这主要是关于什么算作挥发性访问,以及当您执行像x = volatile_var = y;这样棘手的事情时是否有单独的读取)

否则不,零保证,并且您可以构建测试用例,引诱海湾合作委员会进行单个更广泛的访问,或者仅访问低或高字节例如,IIRC var &= 0xFF之类的内容将使其执行零扩展字节加载,而不是半字和单独的零扩展. 100还有另一个反例,它可以使用一对32位半中的stp个来进行非挥发性,在ARMv8.4之前,这并不能保证是单个64位原子访问. 使用volatile uint64_t使其成为单一访问,例如Linux内核可以安全地滚动自己的原子.


对于MMIO,无论如何你肯定想要volatile!! 但您正在铸造指向plan-uint 16的指针,而不是指向volatile的指针,所以这很糟糕.

a_u16_var = *(volatile uint16_t*)&FIFO_REG;
// should be safe in GNU C for what you're looking for

围绕volatile次访问的规则应该足以让您不需要-fno-strict-aliasing次( comments 中提到了这一点). Volatile访问无法与其他volatile访问一起重新排序(在编译时),并且编译器不会假设有关加载或存储的值的任何内容. 只要您对MMIO寄存器的所有访问都是volatile,那么GCC就不能做出任何假设.

C++相关问答推荐

如何将一个enum类型类型转换为另一个类型?

函数指针始终为零,但在解除引用和调用时有效

错误:在.h程序中重新定义 struct

为什么在4.9.37版的内核中,kfio还需要smp_wmb呢?

拥有3x3二维数组并访问数组[1][3]等同于数组[2][0]?

非正规化边缘毛刺

进程已完成,退出代码为138 Clion

实现简单字典时C语言中的段错误

如何计算打印二叉搜索树时每行所需的空间?

用C语言计算文本文件中的整数个数

在C中包装两个数组?

添加函数会 destruct 嵌入式C代码(无IDE)

预处理器宏扩展(ISO/IEC 9899:1999(E)§;6.10.3.5示例3)

类型定义 struct 与简单的类型定义 struct

如何逐位读取二进制文件?

如何修复我的qsort()算法?它每次都给出不同的结果

为什么这个代码的最后一次迭代不能正常工作?

从文件到链表读取日期

execve 不给出which命令的输出

C 错误:对 int 数组使用 typedef 时出现不兼容的指针类型问题