= Using Bit-Fields in a MicroBlaze C Project = We use a very useful C programming tool in the [wiki:802.11 802.11 Reference Design] known as [http://en.wikipedia.org/wiki/Bit_field a Bit-Field]. Bits are not individually addressable in C. When unaligned access is enabled in a processor, the minimum addressable unit of data is a byte. Bit-fields provide a useful way of interpreting the bits that make up larger type definitions. They can help abstract away the low level bit shifts and bit masks needed for bit manipulation. This document is a tutorial on how to use bit-fields in MicroBlaze C projects. A typical situation where you might see a bit-field is a definition like the following: {{{ #!div style="font-size: 90%" {{{#!C /* * This is an example of a simple 1-byte bitfield with three members: A, B, and C * * Bit mask: * MSB _ _ _ _ _ _ _ _ LSB * C|-----B-----|A * * A, C are 1-bit flags. * B is a 6-bit integer. * */ typedef union{ u8 raw_value; struct __attribute__ ((__packed__)){ unsigned A :1; //b[0] unsigned B :6; //b[6:1] unsigned C :1; //b[7] }; } bitfield_example_type; }}} }}} The above type definition defines a bit-field named {{{bitfield_example_type}}}. There are four important features to notice in the above syntax: * A [http://en.wikipedia.org/wiki/Union_type union] is used between to designate that {{{raw_value}}} and the proceeding struct occupy the same space. * The {{{__attribute__ ((__packed__))}}} struct attribute informs the compiler that that each member of the structure should be placed such that the memory required is minimized. This minimizes the chances that the bit-field will be automatically padded to an unexpected length when it is placed into a larger structure with other elements. * By convention, {{{raw_value}}} is the same size as the proceeding struct (a single byte). If the code does not need to interpret the individual bits in the bit-field, it can instead access the fully byte itself by using {{{raw_value}}}. * In the struct definition, the {{{ :X }}} notation is used to tell the compiler that the element is {{{X}}} bits wide. {{{bitfield_example_type}}} defines three bit-fields: A, B, and C. B is a 6-bit integer that is surrounding by single-bit values A and C. Let's first define a function that will print this bit-field for us. This function will print the entire byte in hexadecimal and will then print the fields A, B, and C as decimal values. We'll use this function in the coming examples: {{{ #!div style="font-size: 90%" {{{#!C void print_bitfield_example_type(bitfield_example_type my_bitfield){ xil_printf("bitfield_example_type Contents:\n"); xil_printf(".raw_value = 0x%02x\n", my_bitfield.raw_value); xil_printf(".A = %d\n", my_bitfield.A); xil_printf(".B = %d\n", my_bitfield.B); xil_printf(".C = %d\n", my_bitfield.C); } }}} }}} == Case 1: Manually Initializing a Bit-field == One way to create and initialize a bit-field is to manually declare a local variable and assign values to the bit-field's elements. {{{ #!div style="font-size: 90%" {{{#!C bitfield_example_type my_bitfield; my_bitfield.raw_value = 0; //clear out the entire bitfield my_bitfield.A = 1; my_bitfield.B = 60; my_bitfield.C = 0; print_bitfield_example_type( my_bitfield ); }}} }}} The above code creates a {{{bitfield_example_type}}} named {{{my_bitfield}}} and sets the A, B, and C elements to 1, 60, and 0 respectively. It then calls the previously-defined print function to verify the contents of the bit-field. The output of that print function is the following: {{{ #!div style="font-size: 90%" {{{ bitfield_example_type Contents: .raw_value = 0x79 .A = 1 .B = 60 .C = 0 }}} }}} The above print verifies that the bit-field behaved the way we expected. == Case 2: Using a Constructor Function to Initialize a Bit-field == A second way to initialize a bit-field is to use a constructor. For the sake of argument, suppose that every use of {{{bitfield_example_type}}} required the C element to be set to 1. Rather than trying to remember this requirement with every declaration of the bit-field, we can use a simple constructor function that enforces this requirement while still allowing us to set the A and B fields independently. Here is our constructor: {{{ #!div style="font-size: 90%" {{{#!C bitfield_example_type constructor(bitfield_example_type bitfield_argument){ bitfield_example_type my_bitfield; my_bitfield = bitfield_argument; my_bitfield.C = 1; return my_bitfield; } }}} }}} The following code shows how to use this constructor and then print the contents of the bit-field: {{{ #!div style="font-size: 90%" {{{#!C bitfield_example_type my_bitfield; my_bitfield = constructor( (bitfield_example_type){ .B = 27 } ); print_bitfield_example_type( my_bitfield ); }}} }}} The above code uses an extremely useful syntax called a [https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html designated initializer]. We can create a {{{bitfield_example_type}}} directly in the argument of our call to {{{constructor}}} by explicitly naming the fields we want assigned. In this case, we have set B to 27 and have chosen not to set either A or C. This will create a {{{bitfield_example_type}}} where both A and C are set to 0 but B is set to 27. Our constructor, however, should explicitly set the C field to be 1 according to our requirements. Here is the resulting print: {{{ #!div style="font-size: 90%" {{{ bitfield_example_type Contents: .raw_value = 0xB6 .A = 0 .B = 27 .C = 1 }}} }}} This provides a concise way of initializing a bit-field where some fields are dynamic and need to change while other fields are required to be set a certain way every time. ---- The following two sections show "real-world" examples from the [wiki:802.11 802.11 Reference Design] that use bit-fields. = Example: Interpretation of PHY Header Byte Array = The 802.11n specification of the HT (High Throughput) PHY designates many parameters as collections of bits in front of every MAC payload. For the purposes of this document, we refer to this collection of parameters as the "PHY header." Specifically, an HT packet includes: a 3-byte legacy SIGNAL (L-SIG) field, a 6-byte HT-SIGNAL (H-SIG) field, and finally a 2-byte SERVICE field. Each of these fields are actually made up of many parameters of varying bit widths. Bit-fields provide an excellent way of allowing code to interpret the underlying lying parameters that are packed into the PHY header. To begin, the following is an actual array of bytes that make up the PHY header of a received 802.11n waveform: {{{ #!div style="font-size: 90%" {{{#!C u8 rx_bytes[] = { 0xab, 0x02, 0x00, 0x07, 0x64, 0x00, 0x07, 0x58, 0x03, 0x00, 0x00 }; }}} }}} {{{rx_bytes}}} is an 11-byte array that is composed of 3 bytes of L-SIG followed by 6 bytes of HT-SIG and finally 2 bytes of SERVICE. Our goal in this example is to use bit-fields to be able to interpret the underlying parameters in this array of bytes. Next, we define the bit-fields and structs that will allow us to perform this interpretation: {{{ #!div style="font-size: 90%" {{{#!C // L-SIG Bit-field typedef union{ u8 raw_array[3]; struct __attribute__ ((__packed__)){ unsigned rate :4; //b[3:0] unsigned reserved :1; //b[4] unsigned length :12; //b[16:5] unsigned parity :1; //b[17] unsigned tail :6; //b[23:18] }; } l_sig_bf; // HT-SIG Bit-field typedef union{ u8 raw_array[6]; struct __attribute__ ((__packed__)){ unsigned mcs :7; //b[6:0] unsigned bw :1; //b[7] unsigned len :16; //b[23:8] unsigned smoothing :1; //b[24] unsigned not_sounding :1; //b[25] unsigned reserved :1; //b[26] unsigned agg :1; //b[27] unsigned stbc :2; //b[29:38] unsigned fec :1; //b[30] unsigned short_gi :1; //b[31] unsigned n_ess :2; //b[33:32] unsigned crc :8; //b[41] unsigned tail :6; //b[47] }; } ht_sig_bf; // PHY Header Struct typedef struct __attribute__ ((__packed__)){ l_sig_bf l_sig; //B[2:0] ht_sig_bf ht_sig; //B[8:3] u16 service; //B[10:9] } phy_header; }}} }}} {{{phy_header}}} is a struct made up of two bit-fields ({{{l_sig_bf}}} and {{{ht_sig_bf}}}) followed by a 16-bit variable {{{service}}}. We now define a function that will print these various elements when given a pointer to a {{{phy_header}}} struct: {{{ #!div style="font-size: 90%" {{{#!C void print_phy_header(phy_header* my_header){ xil_printf("L-SIG Rate: %d\n", my_header->l_sig.rate); xil_printf("L-SIG Reserved: %d\n", my_header->l_sig.reserved); xil_printf("L-SIG Length: %d\n", my_header->l_sig.length); xil_printf("L-SIG Parity: %d\n", my_header->l_sig.parity); xil_printf("L-SIG Tail: %d\n", my_header->l_sig.tail); xil_printf("HT-SIG MCS: %d\n", my_header->ht_sig.mcs); xil_printf("HT-SIG BW: %d\n", my_header->ht_sig.bw); xil_printf("HT-SIG LEN: %d\n", my_header->ht_sig.len); xil_printf("HT-SIG SMOOTHING: %d\n", my_header->ht_sig.smoothing); xil_printf("HT-SIG NOT_SOUNDING: %d\n", my_header->ht_sig.not_sounding); xil_printf("HT-SIG RESERVED: %d\n", my_header->ht_sig.reserved); xil_printf("HT-SIG AGG: %d\n", my_header->ht_sig.agg); xil_printf("HT-SIG STBC: %d\n", my_header->ht_sig.stbc); xil_printf("HT-SIG FEC: %d\n", my_header->ht_sig.fec); xil_printf("HT-SIG SHORT_GI: %d\n", my_header->ht_sig.short_gi); xil_printf("HT-SIG N_ESS: %d\n", my_header->ht_sig.n_ess); xil_printf("HT-SIG CRC: %d\n", my_header->ht_sig.crc); xil_printf("HT-SIG TAIL: %d\n", my_header->ht_sig.tail); xil_printf("Service: 0x%x\n", my_header->service); } }}} }}} Finally, we use this struct to recast the {{{rx_bytes}}} array defined earlier and print the result: {{{ #!div style="font-size: 90%" {{{#!C void print_phy_header(phy_header* my_header){ print_phy_header( (phy_header*)rx_bytes ); } }}} }}} Here is the result of that print: {{{ #!div style="font-size: 90%" {{{ L-SIG Rate: 11 L-SIG Reserved: 0 L-SIG Length: 21 L-SIG Parity: 0 L-SIG Tail: 0 HT-SIG MCS: 7 HT-SIG BW: 0 HT-SIG LEN: 100 HT-SIG SMOOTHING: 1 HT-SIG NOT_SOUNDING: 1 HT-SIG RESERVED: 1 HT-SIG AGG: 0 HT-SIG STBC: 0 HT-SIG FEC: 0 HT-SIG SHORT_GI: 0 HT-SIG N_ESS: 0 HT-SIG CRC: 214 HT-SIG TAIL: 0 Service: 0x0 }}} }}}