简体   繁体   English

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

[英]Mapping bit-field structure to volatile register

I'm writing an application which interfaces with a number of registers that are defined in VHDL. 我正在编写一个与VHDL中定义的许多寄存器接口的应用程序。 The registers are 32-bits wide and allocated into groups. 寄存器为32位宽,并分配成组。 I'm provided with the group's base address and with 32-bit offsets for each member of the group. 我为该组的每个成员提供了组的基址和32位偏移量。 Here is an example of one group, a register within the group, and the register's structure. 以下是一个组的示例,组内的寄存器和寄存器的结构。

第1组| base addr |偏移| DATA_PORT

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

Currently I/O is handled using the following bit field structure, 目前,使用以下位字段结构处理I / O,

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

and modifying the fields using a pointer to the addresses, 并使用指向地址的指针修改字段,

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

While this might work on this platform, with the current compiler, I'm concerned about portability. 虽然这可能在这个平台上有效,但是对于当前的编译器,我担心可移植性。 I was wondering if there is a better method for handling the interface to hardware when forced to use these unconventional data types? 我想知道在强制使用这些非常规数据类型时是否有更好的方法来处理硬件接口?

One alternative I can think of is to create another layer between the hardware and the register structures, a volatile unsigned int pointer, and to use the bit-field structure in the application layer. 我能想到的另一种选择是在硬件和寄存器结构之间创建另一层,使用易失性无符号int指针,并在应用层中使用位域结构。 The problem is, the data will still have to be copied from the bit-fields, which may be aligned differently on another platform, to the int, which may be another topic, altogether. 问题是,数据仍然必须从位字段(可能在另一个平台上以不同方式对齐)复制到int,这可能是另一个主题。

Edit: 编辑:
I think what I'm really looking for is a way to eliminate the use of bit-fields. 我认为我真正想要的是一种消除位域使用的方法。 Mapping a struct with bit-field members to hardware really seems to be a bad approach. 将具有位字段成员的结构映射到硬件实际上似乎是一种糟糕的方法。 So, to eliminate that I'll use one of the following instead as a pointer to the volatile memory address, 所以,为了消除这一点,我将使用以下之一作为指向易失性存储器地址的指针,

#define PeripheralBase ((uint32_t volatile *)BASE)

or 要么

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

Hopefully, once I get to this point everything will be well aligned within the 32-bits. 希望,一旦我达到这一点,一切都将在32位内完全一致。 One method I was thinking of for doing this was to create the same data_port structure, but remove the bit packing, and then right a function specifically for each register to shift the bits into an unsigned int, which could then be passed to the register using the volatile pointer. 我想要做的一个方法是创建相同的data_port结构,但是删除位打包,然后右边一个函数专门为每个寄存器将位移位到unsigned int,然后可以使用它传递给寄存器易失性指针。

Something like, 就像是,

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

I'm not sure if the syntax is right, but the idea is to shift the values in without having to worry about the compiler or platform. 我不确定语法是否正确,但我们的想法是将值转移而不必担心编译器或平台。 Does this make since? 这是否因为? Are there portability issues with this approach? 这种方法是否存在可移植性问题?

While bit-fields are terribly implementation dependent, what you could do is to use macros to identify your registers: 虽然位字段非常依赖于实现,但您可以使用宏来识别寄存器:

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

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

then access bit this way: 然后以这种方式访问​​位:

 DATA_PORT.data0 = 1;  // set data0 bit of DATA_PORT to 1 

A typical implementation-independent method for accessing fields within a hardware register is to use shifts (and masks). 用于访问硬件寄存器内的字段的典型的与实现无关的方法是使用移位(和掩码)。 For example: 例如:

#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);

For the register itself, something like this: 对于寄存器本身,这样的事情:

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

Which means you can then do this: 这意味着你可以这样做:

DATA_PORT_REG = data; // Value from above.

Also: 也:

  1. Do not use bitfields for this kind of thing. 不要将bitfields用于此类事情。 They are implementation-dependant, and so can show unexpected behaviour. 它们依赖于实现,因此可以显示意外行为。 The above method should work on any platform. 上述方法应适用于任何平台。
  2. The #define for the register should use an implementation-independent type like uint32_t , in order to explicitly show its size. 寄存器的#define应使用与uint32_t类似的与实现无关的类型,以便明确显示其大小。

The best option seems to be to just eliminate the use of the bit-field structure entirely. 最好的选择似乎是完全消除位域结构的使用。 So, to handle input to the register, instead of separating the bits, just create a structure with the components of the register. 因此,要处理寄存器的输入,而不是分离位,只需创建一个包含寄存器组件的结构。

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

While this struct won't manage the hardware interface directly, it's a useful way to handle the data in the application layer. 虽然此结构不直接管理硬件接口,但它是处理应用程序层中数据的有用方法。 A pointer to the register can be created using either a macro or a pointer to a volatile const uint32_t. 可以使用宏或指向易失性const uint32_t的指针创建指向寄存器的指针。

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

A portable solution for copying the data from the structure to an unsigned 32-bit value is by using a function to shift each value to its correct location in the register and then adding the values together. 用于将数据从结构复制到无符号32位值的便携式解决方案是使用函数将每个值移位到寄存器中的正确位置,然后将这些值一起添加。

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

Writing to the register can then be handled using a call to the function. 然后可以使用对函数的调用来处理对寄存器的写入。

*peripheral_base = stuct_to_uint(&data_port);

The caveat here is, because the bit-fields weren't used, the values being assigned to the data_port structure in the application, will have to be checked to ensure they don't over-run their boundary's. 需要注意的是,由于未使用位域,因此必须检查分配给应用程序中data_port结构的值,以确保它们不会超出其边界。 Otherwise, the data written to the register will create unexpected results. 否则,写入寄存器的数据将产生意外结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM