Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping bit-field structure to volatile register

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?

like image 624
dsell002 Avatar asked Oct 09 '13 20:10

dsell002


People also ask

What is bit field in register?

In the embedded system, bit-field is used to represent the GPIO Pins of a microcontroller or the bits of the hardware register. A bit-field is the property of the structure, it is useful to create a bitmap structure which directly maps with the bit of register.

How are bit fields stored in memory?

Again, storage of bit fields in memory is done with a byte-by-byte, rather than bit-by-bit, transfer.

What is a bit field Why are bit fields used with structures?

These space-saving structure members are called bit fields, and their width in bits can be explicitly declared. Bit fields are used in programs that must force a data structure to correspond to a fixed hardware representation and are unlikely to be portable.

What is Bitfield in C with example?

Declaration of bit-fields in CThe number of bits in the bit-field. The width must be less than or equal to the bit width of the specified type. Example: struct date { // month has value between 0 and 15, // so 4 bits are sufficient for month variable. int month : 4; };


2 Answers

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 
like image 63
ouah Avatar answered Sep 22 '22 02:09

ouah


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.
like image 40
Steve Melnikoff Avatar answered Sep 20 '22 02:09

Steve Melnikoff