Mapping bit-field structure to volatile register

dsell002 picture dsell002 · Oct 9, 2013 · Viewed 8.9k times · Source

I'm writing an application which interfaces with a number of registers that are defined in VHDL. The registers are 32-bits wide and allocated into groups. I'm provided with the group's base address and with 32-bit offsets for each member of the group. Here is an example of one group, a register within the group, and the register's structure.

Group 1 | base addr | offset | data_port

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

Currently I/O is handled using the following bit field structure,

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. 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.

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. 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.

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?

Answer

ouah picture ouah · Oct 10, 2013

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