【问题标题】:Manually insert padding bytes between the struct elements in 'C'在“C”中的结构元素之间手动插入填充字节
【发布时间】:2014-04-05 03:27:18
【问题描述】:

我有一组 32 位寄存器用于嵌入式应用程序(ARM 裸机)中的外围设备,具有以下字节地址。

CTL 0x0;
统计 0x4
TXR 0x8 RXR 0x20
DAT1 0x30 DAT2 0x40 等等

我想将所有这些寄存器组合成一个 C 结构(它是一个打包结构)

struct my_peri {
     uint32_t CTL;
     uint32_t STAT;
     uint32_t TXR;
     uint32_t RXR;
     uint32_t DAT1;
     uint32_t DAT2;
};

struct my_peri* pPeri0 = (uint32_t*) (BASE_ADDRESS_OF_MY_PERI_0);

现在如果我访问

pPeri->RXR;  // This will point to (BASE_ADDRESS + 0x10)
             // But the actual address i want to refer is (BASE_ADDRESS + 0x20)

为了获得正确的地址,我手动在它们之间添加了一些元素

struct my_peri {
     uint32_t CTL;
     uint32_t STAT;
     uint32_t TXR;
     uint32_t RESERVED[4]; // 0x10 to 0x1c
     uint32_t RXR;
     uint32_t RESERVED_1[3]; // 0x24-0x2c
     uint32_t DAT1;
     uint32_t RESERVED_2[3]; // 0x34-0x3c
     uint32_t DAT2;
};

但是根据外围设备规范,对 RESERVED、RESERVED_1 和 RESERVED_2 的任何访问都会出错。

Is there a way to add address spacing between the struct elements?
Without adding RESERVED elements

If not, is there a way to group these registers into a single data structure?.
With each register pointing to the right address.

我正在使用 ARM-GCC 工具链。

【问题讨论】:

  • 您可能还需要volatile(是的,“易失性被认为是有害的”等等)。

标签: c gcc struct memory-alignment bare-metal


【解决方案1】:

是的,您可以使用“位字段”在结构中创建未命名字段:

struct my_peri {
     uint32_t CTL;
     uint32_t STAT;
     uint32_t TXR;
     uint32_t : 32;
     uint32_t RXR;
     uint32_t : 32;
     uint32_t : 32;
     uint32_t : 32;
     uint32_t DAT1;
     uint32_t : 32;
     uint32_t : 32;
     uint32_t : 32;
     uint32_t DAT2;
};

不幸的是,没有数组语法;如果需要,您可以通过将类型更改为 uint64_t 来折叠成对的 :32:64

如果您的字段都与本示例中的类型相同,则另一种方法是将整个事物视为整数数组并使用 enum { CTL = 0, STAT = 1, TXR = 2, RXR = 4, ... } 对其进行索引。

【讨论】:

  • 只有在对齐正确的情况下才能将它们折叠成 uint64_t ;)
  • @tc:如果对齐错误,您可以使用属性(打包)但是它在您的平台上拼写...如果您正在处理硬件,您可能无论如何都需要这样做。 :)
【解决方案2】:

要回答您的两个问题, 你最终会不可避免地得到某种虚假的填充元素,因为 C 没有规定为结构强制特定布局。

但是,如果您最终拥有一个稀疏布局,那么实现这一点的更好方法可能是使用一个充满指针的结构。这不再以任何方式限制您的布局,并且在大多数情况下,如果您将指针定义为 const gcc 将完全优化取消引用,甚至应该优化整个数组。我承认这不是您所要求的,但我认为这仍然是解决问题的好方法。您也不再依赖 C 扩展并希望编译器尊重您的打包和布局要求。

【讨论】:

    【解决方案3】:
    #include <stdio.h>
    #include <stddef.h>
    #include <inttypes.h>
    
    struct my_peri {
         uint32_t CTL;
         uint32_t STAT;
         uint32_t TXR;
         uint32_t RXR __attribute__((aligned (32)));
         uint32_t DAT1 __attribute__((aligned (16)));
         uint32_t DAT2 __attribute__((aligned (16)));
    };
    
    int main(void)
    {
         printf("RXR  @%x\n", (unsigned) offsetof(struct my_peri, RXR));
         printf("DAT1 @%x\n", (unsigned) offsetof(struct my_peri, DAT1));
         printf("DAT2 @%x\n", (unsigned) offsetof(struct my_peri, DAT2));
         return 0;
    }
    

    输出:

    RXR  @20
    DAT1 @30
    DAT2 @40
    

    请注意,aligned 值必须是 2 的幂。但这在这里行得通。

    【讨论】:

      猜你喜欢
      • 2020-04-30
      • 2023-03-18
      • 2020-11-07
      • 2015-04-25
      • 2018-03-30
      • 2014-09-14
      • 1970-01-01
      • 2019-03-08
      • 1970-01-01
      相关资源
      最近更新 更多