Question

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?

Was it helpful?

Solution 3

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.

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.

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. Otherwise, the data written to the register will create unexpected results.

OTHER TIPS

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. 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.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top