将位域结构映射到易失性寄存器

dse*_*002 7 c embedded bit-fields

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

第1组|  base addr |  偏移|  DATA_PORT

data_port |  alt_u32 data0:12;  |  alt_u32 data1:1;  |  ....

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

typedef struct
{
   uint32_t   data0 : 12;
   uint32_t   data1 : 1;
   ...
}volatile data_port;
Run Code Online (Sandbox Code Playgroud)

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

data_port *const p_data = (data_port *)0xc006380;
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:
我认为我真正想要的是一种消除位域使用的方法.将具有位字段成员的结构映射到硬件实际上似乎是一种糟糕的方法.所以,为了消除这一点,我将使用以下之一作为指向易失性存储器地址的指针,

#define PeripheralBase ((uint32_t volatile *)BASE)
Run Code Online (Sandbox Code Playgroud)

要么

uint32_t volatile *const peripheral_base  = (uint32_t *) BASE;
Run Code Online (Sandbox Code Playgroud)

希望,一旦我达到这一点,一切都将在32位内完全一致.我想要做的一个方法是创建相同的data_port结构,但是删除位打包,然后右边一个函数专门为每个寄存器将位移位到unsigned int,然后可以使用它传递给寄存器易失性指针.

就像是,

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

我不确定语法是否正确,但我们的想法是将值转移而不必担心编译器或平台.这是否因为?这种方法是否存在可移植性问题?

oua*_*uah 6

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

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

#define DATA_PORT (*(volatile data_port *) 0xc006380) 
Run Code Online (Sandbox Code Playgroud)

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

 DATA_PORT.data0 = 1;  // set data0 bit of DATA_PORT to 1 
Run Code Online (Sandbox Code Playgroud)


dse*_*002 0

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

typedef struct data_port
{
   uint32_t   data0;
   uint32_t   data1;
   ....
}data_port;
Run Code Online (Sandbox Code Playgroud)

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

uint32_t volatile *const peripheral_base  = (uint32_t *) BASE;
Run Code Online (Sandbox Code Playgroud)

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

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

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

*peripheral_base = stuct_to_uint(&data_port);
Run Code Online (Sandbox Code Playgroud)

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