7

我正在编写一个与 VHDL 中定义的许多寄存器接口的应用程序。寄存器为 32 位宽并分配到组中。我得到了组的基地址和组中每个成员的 32 位偏移量。这是一个组的示例,组内的寄存器以及寄存器的结构。

第 1 组 |  基地址 |  偏移 |  数据端口

数据端口 |  alt_u32 数据0:12; |  alt_u32 数据1:1; |  ……

目前 I/O 使用以下位域结构处理,

typedef struct
{
   uint32_t   data0 : 12;
   uint32_t   data1 : 1;
   ...
}volatile data_port;

并使用指向地址的指针修改字段,

data_port *const p_data = (data_port *)0xc006380;

虽然这可能适用于这个平台,但使用当前的编译器,我担心可移植性。我想知道在被迫使用这些非常规数据类型时是否有更好的方法来处理硬件接口?

我能想到的另一种选择是在硬件和寄存器结构之间创建另一个层,一个易失的无符号整数指针,并在应用层中使用位域结构。问题是,数据仍然必须从位域(可能在另一个平台上以不同方式对齐)复制到 int,这可能是另一个主题。

编辑:
我认为我真正想要的是一种消除位域使用的方法。将具有位域成员的结构映射到硬件似乎确实是一种不好的方法。因此,为了消除这种情况,我将使用以下内容之一作为指向易失性内存地址的指针,

#define PeripheralBase ((uint32_t volatile *)BASE)

或者

uint32_t volatile *const peripheral_base  = (uint32_t *) BASE;

希望一旦我达到这一点,一切都会在 32 位内很好地对齐。我正在考虑的一种方法是创建相同的 data_port 结构,但删除位打包,然后为每个寄存器专门设置一个函数以将位转换为无符号整数,然后可以将其传递给寄存器使用易失性指针。

就像是,

static inline uint32_t struct_to_uint(data_port *data)
{
   return data->data0
        + ((uint32_t)data->data1 << 12)
        + ((uint32_t)data->data2 << 13)
        + .....;
}

我不确定语法是否正确,但想法是在不必担心编译器或平台的情况下将值移入。这是从那时起吗?这种方法是否存在可移植性问题?

4

3 回答 3

6

虽然位域非常依赖于实现,但您可以做的是使用宏来识别您的寄存器:

typedef struct
{
   uint32_t data0 : 12;
   uint32_t data1 : 1;
   ...
} data_port;

#define DATA_PORT (*(volatile data_port *) 0xc006380) 

然后以这种方式访问​​位:

 DATA_PORT.data0 = 1;  // set data0 bit of DATA_PORT to 1 
于 2013-10-09T22:29:51.837 回答
1

访问硬件寄存器中的字段的一种典型的独立于实现的方法是使用移位(和掩码)。例如:

#define DATA0_SHIFT   0
#define DATA0_MASK    0x3FF
#define DATA1_SHIFT   12
#define DATA1_MASK    0x1
#define DATA2_SHIFT   13
#define DATA2_MASK    0x1

// ...

uint32_t data = 0
   | ((data0 & DATA0_MASK) << DATA0_SHIFT)
   | ((data1 & DATA1_MASK) << DATA1_SHIFT)
   | ((data2 & DATA2_MASK) << DATA2_SHIFT);

对于寄存器本身,如下所示:

#define DATA_PORT_ADDR  0xc006380
#define DATA_PORT_REG  (*(volatile uint32_t *)(DATA_PORT_ADDR))

这意味着您可以这样做:

DATA_PORT_REG = data; // Value from above.

还:

  1. 不要将位域用于此类事情。它们依赖于实现,因此可以显示出意外的行为。上述方法应该适用于任何平台。
  2. #define寄存器应该使用与实现无关的类型,例如,uint32_t以便显式显示其大小。
于 2013-10-23T17:48:05.463 回答
0

最好的选择似乎是完全消除位域结构的使用。因此,要处理寄存器的输入,而不是分离位,只需创建一个包含寄存器组件的结构。

typedef struct data_port
{
   uint32_t   data0;
   uint32_t   data1;
   ....
}data_port;

虽然这个结构不会直接管理硬件接口,但它是处理应用层数据的有用方法。可以使用宏或指向 volatile const uint32_t 的指针来创建指向寄存器的指针。

uint32_t volatile *const peripheral_base  = (uint32_t *) BASE;

将数据从结构复制到无符号 32 位值的可移植解决方案是使用函数将每个值移动到寄存器中的正确位置,然后将这些值相加。

static inline uint32_t struct_to_uint(data_port *data)
{
   return data->data0
    + (data->data1 << 12)
    + (data->data2 << 13)
    + .....;
}

然后可以使用对该函数的调用来处理对寄存器的写入。

*peripheral_base = stuct_to_uint(&data_port);

这里需要注意的是,由于未使用位字段,因此必须检查分配给应用程序中 data_port 结构的值以确保它们不会超出其边界。否则,写入寄存器的数据会产生意想不到的结果。

于 2013-10-10T23:22:14.143 回答