强制GCC访问带有单词的结构

在ARM处理器(HT32F1655)上,寄存器的特定部分需要字访问。 从用户手册:

请注意,AHB总线中的所有外设寄存器仅支持字访问。

但是gcc在打包的结构上生成了一些ldrb (加载字节)和strb (存储字节)指令。 结构看起来像这样:

 typedef union { struct { uint32_t CKOUTSRC : 3; //!< CKOUT Clock Source Selection uint32_t : 5; uint32_t PLLSRC : 1; //!< PLL Clock Source Selection uint32_t : 2; uint32_t CKREFPRE : 5; //!< CK_REF Clock Prescaler Selection uint32_t : 4; uint32_t URPRE : 2; //!< USART Clock Prescaler Selection uint32_t USBPRE : 2; //!< USB Clock Prescaler Selection uint32_t : 5; uint32_t LPMOD : 3; //!< Lower Power Mode Status } __attribute__((packed)) __attribute__ ((aligned(4))); uint32_t word; } reg; 

用法示例:

 (*(volatile uint32_t*)0x40088000)->CKOUTSRC = 1; 

产生类似于:

  ldrb r2, [r1] orr r2, r2, #1 strb r2, [r1] 

当我需要时:

  ldr r2, [r1] orr r2, r2, #1 str r2, [r1] 

有没有办法强制gcc只生成访问整个单词的指令? 一些选项( -mno-unaligned-access )使gcc生成字访问,但仅当字节不是4字对齐时。

有一个-mslow-bytes应该做正确的事情,但似乎arm-none-eabi-gcc不存在选项。

理想情况下,有一种方法可以仅对受影响的结构强制执行此操作。

请不要“不要使用位域”答案。 我知道缺点,但我有能力控制使用的编译器,所以我不担心可移植性。

您正在寻找的是GCC的-fstrict-volatile-bitfields选项:

如果访问易失性位域(或其他结构字段,尽管编译器通常会尊重这些类型),则应使用此选项,应使用字段类型宽度的单一访问,如果可能,则与自然对齐对齐。 例如,具有存储器映射外设寄存器的目标可能要求所有这些访问都是16位宽; 使用此标志,您可以将所有外设位字段声明为无符号短路(假设这些目标上的短路为16位),以强制GCC使用16位访问,而不是更高效的32位访问。

如果禁用此选项,编译器将使用最有效的指令。 在前面的示例中,可能是32位加载指令,即使它访问不包含位字段任何部分的字节,或者与正在更新的寄存器无关的存储器映射寄存器。

在某些情况下,例如将packed属性应用于结构字段时,可能无法通过为目标计算机正确对齐的单个读取或写入来访问该字段。 在这种情况下,GCC回退到生成多个访问,而不是在运行时将错误或截断结果的代码。

注意:由于C / C ++ 11内存模型的限制,不允许写访问接触非位字段成员。 因此,建议将字段类型的所有位定义为位字段成员。

此选项的默认值由目标处理器的应用程序二进制接口确定。

以及使用volatile关键字。 请参阅: https : //gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html

这正是volatile关键字的设计目标。