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.