组合 ARM SoC 的两个联合结构

Combining two union structs of ARM SoC

我正在尝试将 ARM SoC 的 GPIO 端口的两个 typedef 联合合并为一个,并将地址指针合并为一个。目前,我有这样的东西:

.h 文件:

//GPIO00 port
typedef union {
  struct {
    uint32_t GPIO000:1;
    uint32_t GPIO001:1;
    ...
    uint32_t GPIO0017:1;
  };
  struct {
    uint32_t w:18;
  };
} __GPIO00portbits_t;

volatile __GPIO00portbits_t * PTR_GPIO00portbits;
#define GPIO00portbits (*PTR_GPIO00portbits)


//GPIO01 port
typedef union {
  struct {
    uint32_t GPIO010:1;
    uint32_t GPIO011:1;
    ...
    uint32_t GPIO0117:1;
  };
  struct {
    uint32_t w:18;
  };
} __GPIO01portbits_t;

volatile __GPIO01portbits_t * PTR_GPIO01portbits;
#define GPIO01portbits (*PTR_GPIO01portbits)

.c 文件:

//GPIO 00 port
volatile __GPIO00portbits_t * PTR_GPIO00portbits = (__GPIO00portbits_t *) (AXIBRIDGE_BASE_ADDR + GPIO_00_BASE);


//GPIO 01 port
volatile __GPIO01portbits_t * PTR_GPIO01portbits = (__GPIO01portbits_t *) (AXIBRIDGE_BASE_ADDR + GPIO_01_BASE);
}

我可以用它来控制 ARM SoC 的 GPIO 端口。 IE。我可以通过更改 GPIO00portbits.GPIO00x 来控制 GPIO00 的单个引脚。它对 GPIO01 的工作原理相同。

实际上GPIO00和GPIO01实际上是一个端口,叫做GPIO0,其中GPIO00是pin 0-17,GPIO01是pin 18-35,所以我也想把GPIO00和GPIO01合二为一,可以控制通过更改 GPIO0portbits.GPIO0x.

所以我想要这样的东西:

typedef union {
  struct {
    uint64_t GPIO00:1 = GPIO00portbits.GPIO000;
    uint64_t GPIO01:1 = GPIO00portbits.GPIO001;
    ...
    uint64_t GPIO035:1 = GPIO01portbits.GPIO0117;
  };
  struct {
    uint32_t w:36;
  };
} __GPIO0portbits_t;

我该怎么做?

提前致谢。

一般数据类型

您定义了两个不同的类型,__GPIO00portbits_t__GPIO01portbits_t,它们具有相同的结构和密切相关的用途。这是毫无意义的,甚至可能妨碍您。我可能会这样做,而不是:

typedef union {
  struct {
    uint32_t GPIO0:1;
    uint32_t GPIO1:1;
    ...
    uint32_t GPIO17:1;
  };
  uint32_t w:18;
} __GPIOhalfportbits_t;

extern volatile __GPIOhalfportbits_t *PTR_GPIO00portbits;
#define GPIO00portbits (*PTR_GPIO00portbits)

extern volatile __GPIOhalfportbits_t * PTR_GPIO01portbits;
#define GPIO01portbits (*PTR_GPIO01portbits)

请注意,顺便说一下,如果 header 将在多个 .c 文件中使用,那么您需要 externs,并且在这种情况下恰好是其中一个.c 文件应包含您显示的定义。


您的具体要求

I would also like to combine GPIO00 and GPIO01 into one stuct which can be controlled by changing GPIO0portbits.GPIO0x

看来您可能没有在 object 及其数据类型之间保持适当的心理区分。这将解释您奇怪的数据类型重复,以及您描述您正在寻找的内容的方式。如果您希望能够选择将数据视为完整的 36 位或两个 18 位的一半,那么您可以 想象 继续上面的内容,如下所示:

// XXX: see below
typedef union {
  struct {
      __GPIOhalfportbits_t group0;
      __GPIOhalfportbits_t group1;
  };
  struct {
    uint32_t GPIO0:1;
    uint32_t GPIO1:1;
    ...
    uint32_t GPIO35:1;
  };
  uint64_t w:36;  // uint32_t isn't wide enough
} __GPIOportbits_t;

那么,原则上,您可以通过直接访问位来访问该类型的 object ...

__GPIOportbits_t portbits;

// ...

if (portbits.GPIO23) {
    // ...
}

... 或通过 half-port 件 ...

if (portbits.group1.GPIO5) {
    // ...
}

类似的方法可能会在不同的情况下起作用,但在您的情况下,这不会起作用。问题是 half-port 块中的位数不是 char 中位数的倍数(硬件上为 8)。 char 的大小是衡量 object 大小的单位,因此也是地址的最细粒度。

这意味着我的 __GPIOhalfportbits_t 和你的 __GPIO00portbits_t__GPIO01portbits_t 的大小至少是 24 位,而不是 18 位。因此,如果您将其中两个一个接一个地放置,则位域 不能 被布置为从 object 开头开始的连续 36 位范围。第一个 object 至少有 6 个(填充)位需要在第二个 half-port object.

的位之前到达某个地方

出于基本相同的原因,也没有可以完成您所追求的指针技巧。如果您有一个 36 位连续位的区域,那么后半部分不会从可寻址边界开始,因此您无法形成指向它的指针。

另一方面,如果两半一开始就不是连续的,那么您也许可以这样处理:

typedef struct {
    __GPIOhalfportbits_t group0;
    __GPIOhalfportbits_t group1;
} __GPIOportbits_t;

您必须注意两个 half-port 部分的对齐,但可能有一种 implementation-specific 方法可以做到这一点。鉴于底层数据(我们现在已经假设)首先没有呈现为 36 位的连续跨度,因此与 36 位位域形成联合没有意义。尽管如此,通过插入适当大小的显式填充,可以使用联合将单个 single-bit 位域映射到该对结构的顶部,但是您需要考虑这是否真的值得做。具体请参见下文。


其他重要注意事项

位域通常是一项棘手的工作,C 对其行为的保证很少——比很多人想象或期望的要少得多。使用位域写入硬件端口是一个特别糟糕的主意,因为您不能一次写入少于 CHAR_BIT 位,并且如果您通过大小不是 power-of-two 倍数的位域写入CHAR_BIT 那么你也将写入额外的位,其值未指定。

我通常建议完全避免使用位域,除非可能在相关硬件制造商提供的 C-language 编程接口中以与这些接口文档一致的方式使用位域。


备选方案

您可以想出一些包装宏来访问两个半端口的 GPIO 端口,甚至是这些端口中的各个位。但是这个答案已经很长了,这样的 macro-centric 方法将是另一回事。

  1. 你不能这样做,因为它们位于内存中的不同地址。
  2. 使用对象访问硬件寄存器效率很低。在这个级别的编程上,你需要尽可能优化代码。

https://godbolt.org/z/ncbr8o

你只能通过拥有额外的对象来“组合”它们,你将从实际寄存器中读取数据,并在更改后将其保存到寄存器中。

#include <stdint.h>

#define AXIBRIDGE_BASE_ADDR 0x12340000
#define GPIO_00_BASE  0x400
#define GPIO_01_BASE  0x800

//GPIO00 port
typedef union {
  struct {
    uint32_t GPIO000:1;
    uint32_t GPIO001:1;
    uint32_t GPIO002:1;
    uint32_t GPIO003:1;
    uint32_t GPIO004:1;
    uint32_t GPIO005:1;
    uint32_t GPIO006:1;
    uint32_t GPIO007:1;
    uint32_t GPIO008:1;
    uint32_t GPIO009:1;
    uint32_t GPIO010:1;
    uint32_t GPIO011:1;
    uint32_t GPIO012:1;
    uint32_t GPIO013:1;
    uint32_t GPIO014:1;
    uint32_t GPIO015:1;
    uint32_t GPIO016:1;
    uint32_t GPIO017:1;
  };
  struct {
    uint32_t w:18;
  };
} __GPIO00portbits_t;



typedef union {
  struct {
    uint32_t GPIO000:1;
    uint32_t GPIO001:1;
    uint32_t GPIO002:1;
    uint32_t GPIO003:1;
    uint32_t GPIO004:1;
    uint32_t GPIO005:1;
    uint32_t GPIO006:1;
    uint32_t GPIO007:1;
    uint32_t GPIO008:1;
    uint32_t GPIO009:1;
    uint32_t GPIO010:1;
    uint32_t GPIO011:1;
    uint32_t GPIO012:1;
    uint32_t GPIO013:1;
    uint32_t GPIO014:1;
    uint32_t GPIO015:1;
    uint32_t GPIO016:1;
    uint32_t GPIO017:1;
    uint32_t GPIO100:1;
    uint32_t GPIO101:1;
    uint32_t GPIO102:1;
    uint32_t GPIO103:1;
    uint32_t GPIO104:1;
    uint32_t GPIO105:1;
    uint32_t GPIO106:1;
    uint32_t GPIO107:1;
    uint32_t GPIO108:1;
    uint32_t GPIO109:1;
    uint32_t GPIO110:1;
    uint32_t GPIO111:1;
    uint32_t GPIO112:1;
    uint32_t GPIO113:1;
    uint32_t GPIO114:1;
    uint32_t GPIO115:1;
    uint32_t GPIO116:1;
    uint32_t GPIO117:1;
  };
  struct {
    uint64_t GPIO1w:18;
    uint64_t GPIO2w:18;
  };
} __GPIO12portbits_t;


#define GPIO1       ((volatile __GPIO00portbits_t *)(AXIBRIDGE_BASE_ADDR + GPIO_00_BASE))
#define GPIO2       ((volatile __GPIO00portbits_t *)(AXIBRIDGE_BASE_ADDR + GPIO_01_BASE))

#define COMBINE()   (&(__GPIO12portbits_t){.GPIO1w = GPIO1 -> w, .GPIO2w = GPIO2 -> w})
#define UPDATEGPIO(ptr)  do{GPIO1 -> w = ptr -> GPIO1w; GPIO2 -> w = ptr -> GPIO2w;}while(0)

void foo()
{
    __GPIO12portbits_t *ptr = COMBINE();

    ptr -> GPIO014 = 1;
    ptr -> GPIO110 = 1;

    UPDATEGPIO(ptr);
}

void bar()
{
    GPIO1 -> GPIO014 = 1;
    GPIO2 -> GPIO010 = 1;
}

但是效率很低https://godbolt.org/z/jMsc7j