Hardware reg bitfields and struct/unions V defines of bitnames and bit positions - Page 1
Forum section: electronics / general

author: AlfBaz
author_rank: Super Contributor
timestamp: November 07, 2017, 02:13:09 am
content: We are all aware of the benefits decent code editors bring to the table with regards to auto-completion and pop-up lists of members.These things save a lot of time flipping back and forth from header files where register bits and pieces are defined.Microchip uses, or at least use to use bitfields, structures and unions to define bits and groups of bits for their hardware registers.Others such as ST use #defines for the position of the bits and masks for groups of related bits. To make matters worse they define all the regs at the top of the file and the bit defs at the bottom of the file, miles away from the register defsCode wise, setting hardware register bits where the vendor has supplied a header full of #define's is annoyingly painful compared to writing the name of the register-> and having a complete list of bits to choose fromWith defined bit names, bit positions and masks, for a group of bits, it involves ANDing a shifted and NOTted version of the mask to clear the bits of interest followed by ORing a shifted value to the register whereas with a structure of bitfields it’s as simple as Reg->BitGroup = x;Assembly code generated for bitfields (gcc on arm) take advantage of the Bit Field Clear (BFC) and Bit Field Insert (BFI) instructions.With the defines method, shifting etc is mostly done by the pre-processor but assembly shows a bunch of register loading presumably with immediate values and is more than twice as many assembly instructions.Here's some simple C code and select disassembly to demonstrateCode:volatile uint32_tbitreg;typedef union {struct {unsigned bit0:1;unsigned bit1:1;unsigned bit2:1;unsigned bit3:1;unsigned bgp1:3;unsigned bgp2:4;unsigned resv:21;};uint32_tReg;} BitReg_t;volatile BitReg_t * BitReg = (BitReg_t *) &bitreg;#define BGP1_POS4#define BGP1_MASK7 << BGP1_POSint main(void){BitReg->Reg = 0xFFFF;BitReg->bgp1 = 5;/* assembly for last lineldrr3, [pc, #56]; (5c <main+0x5c>)ldrr2, [r3, #0]ldrr3, [r2, #0]movsr1, #5bfir3, r1, #4, #3strhr3, [r2, #0]*/BitReg->Reg = 0xFFFF;BitReg->Reg &= ~(BGP1_MASK);/* assembly for last lineldrr3, [pc, #32]; (5c <main+0x5c>)ldrr3, [r3, #0]ldrr2, [pc, #28]; (5c <main+0x5c>)ldrr2, [r2, #0]ldrr2, [r2, #0]bic.wr2, r2, #112; 0x70strr2, [r3, #0]*/BitReg->Reg |= 5 << BGP1_POS;/* assembly for last lineldrr3, [pc, #16]; (5c <main+0x5c>)ldrr3, [r3, #0]ldrr2, [pc, #12]; (5c <main+0x5c>)ldrr2, [r2, #0]ldrr2, [r2, #0]orr.wr2, r2, #80; 0x50strr2, [r3, #0]*/while(1);}Now I realise what goes on under the hood is mostly irrelevant but from a coding perspective, to me, setting register bits is far easier using bitfields and less prone to error than searching for predefined bit names and the mess of shifting, inverting, ORing and ANDing.So, two questions...1. What is the main reason for avoiding bitfields. Is it non <insert standard here> compliant, are there pitfalls I am unaware of, are there portability issues2. Given vendor supplied register definitions of the type using bit name defs such as ST’s, is there an easier way of finding these defines other than opening the header and relying on ctrl+FThanks in advance

author: ataradov
author_rank: Super Contributor
timestamp: November 07, 2017, 04:32:53 am
content: Quote from: AlfBaz on November 07, 2017, 02:13:09 amC standard says that layout of bit fields is implementation defined. But compilers do tend to behave consistently and as you would expect.It is not a bad idea to use bit fields like this. It makes for a more optimal code.Quote from: AlfBaz on November 07, 2017, 02:13:09 amAtmel parts have bits named consistently with the datasheet, so you can figure out naming for everything simply by looking at the datasheet.But I personally like Ctrl-F.

author: boffin
author_rank: Unknown
timestamp: November 07, 2017, 04:43:52 am
content: Actually the best implementation is in Pascal. I remember writing an eprom programmer in Turbo Pascal probably 30 years ago, and if you defined the output ports as sets, it was just so damn easy to understand what was going onSets are defined using bits, and you could do stuff like this:Code:VAR x: SET OF [ bit0, bit1, bit3, bit3, bgp1, curtains, lights, action ];x := inportb(SomeRegister);x := x -  +  + ;outputb(SomeRegister, x);orIF (curtains IN x) THEN BEGIN...(I apologize for any Pascal syntax errors, it's been a while....)

author: andyturk
author_rank: Frequent Contributor
timestamp: November 07, 2017, 05:32:01 am
content: Quote from: AlfBaz on November 07, 2017, 02:13:09 amSpecifically for STM32, I've been using ST's new LL (low layer?) headers which take care of most of the register punching. That avoids a major problem of rolling your own register definitions and thus owning the responsibility to make sure they're correct. The LL is mostly a "library" of macro definitions or very thin functions. I like it because ST's peripherals are generally consistent between families, but they do occasionally move flags to different registers just to keep things interesting. The LL calls abstract those slight differences away. Note that the LL API is entirely different from the STM32 HAL, which is crap.ARM/Keil has a program calledSVDConv.exethat can be used to generate bitfield headers from .svd files, but I've never used it. What I have tried is writing a pile of Python code to parse those .svd files (they're XML) and generate headers to my own specifications. The problem there is that the SVD standard is way too loose and every ARM vendor seems to release .svd files that are somehow incompatible with everyone else's (when they even release .svd files at all).On the general question, I'm with you: I'd much rather use bitfields than shifts and masks. However, that desire is outweighed by the need to avoid creating custom register definitions by hand. That way madness lies.

author: ataradov
author_rank: Super Contributor
timestamp: November 07, 2017, 05:36:10 am
content: Quote from: andyturk on November 07, 2017, 05:32:01 amYes, don't create things by hand. All major vendors supply SVD files.I also have some tools for parsing SVD files, and don't find them to be too inconsistent. But different vendors use "advanced" features to different extents. But SVDConv does the basic job. Although I'm not a huge fan of its output, it beats making thing be hand.

author: westfw
author_rank: Super Contributor
timestamp: November 07, 2017, 06:19:51 am
content: QuoteThere aren't any bitfield instructions on CM0. :-( Code generated to deal with bitfields can be horribly inefficient compared to that using masks. Bitfields also can'd be easily combined. USART->CFGA = PARITY_BM+STOPB_BM+DATAL_BM;Bitfields can generate an illusion of bitwise independence.Code:SERCOM5->USART.INTENCLR.bit.ERROR = 1;is quite wrong and causes "mysterious" behavior, for example. (https://community.atmel.com/forum/problem-clearingsetting-bit-interrupt-flag-register)I do rather like the Atmel style of having both bitfields AND masks available in the .h file. Though perhaps the algorithm they use to generate them should avoid creating identical fields like:Code:typedef union {struct {uint32_t OUT:32; /*!< bit: 0..31 Port Data Output Value */} bit; /*!< Structure used for bit access */uint32_t reg; /*!< Type used for register access */} PORT_OUT_Type;

author: hcglitte
author_rank: Regular Contributor
timestamp: November 07, 2017, 11:55:29 am
content: Is there any way to use this approach for a communication protocol where the payload may be 15 bytes or whatever, and the bit fields don´t align on 8 bit boundaries?

author: ataradov
author_rank: Super Contributor
timestamp: November 07, 2017, 05:22:50 pm
content: Quote from: hcglitte on November 07, 2017, 11:55:29 amIf you use uint64_t, then your payloads must align at 64 bit boundaries. If absolutely nothing aligns, then you would have to reassemble fields on the edges. But in 15 byte payload, you will get 1 edge at most.On the other hand, most protocols I've seen have a very natural size for a bit-field members. It may not be a byte, but 16 or 32 bits.

author: ehughes
author_rank: Frequent Contributor
timestamp: November 07, 2017, 05:39:18 pm
content: The primary reason is that bitfield struct implementations are not defined in the C standard.Code written with them is not portable. If you are sticking with the same compiler/architecture then it shouldn't matter.

author: ataradov
author_rank: Super Contributor
timestamp: November 07, 2017, 05:41:44 pm
content: Quote from: ehughes on November 07, 2017, 05:39:18 pmMCU register sets are inherently not portable. And all major existing compilers implement bit fields the same way. An you would be crazy to make a new compiler that behaves differently.

author: ehughes
author_rank: Frequent Contributor
timestamp: November 07, 2017, 09:15:45 pm
content: QuoteMy experience has been that this is not the case. Moving from GCC to IAR for example has yielded oddball behaviors with bitfields not being 100% identical. I understand the appeal but code at the register level often requires the reference manual open anyway. Bitfields provide a bit of automation but have never saved me enough time to be worth the hassle of not part of an implementation standard.I have seen bitfields also used in certain communications protocols, for example VMF, where the streams are highly packaged to oddball bit boundaries. In my case of porting code that used bit fields was a nightmare in that it worked for the 6-sigma of cases but failed in some corner cases to be portable. Had the code be just written with bit masks, it would have just worked with a lot less hassle of hunting down the corner case.That and they are not allowed by any formal certification process.

author: AlfBaz
author_rank: Super Contributor
timestamp: November 08, 2017, 12:14:05 am
content: @ehughesThank you for providing an insight as to where bit-fields may fall down, however if we limit the use case to peripheral register bit definition, don't IAR, Keil, etc have bitfield implementation sufficiently stable to be effective in this space?

author: ataradov
author_rank: Super Contributor
timestamp: November 08, 2017, 12:24:35 am
content: Quote from: AlfBaz on November 08, 2017, 12:14:05 amThat is my experience. I worked on large projects that use bit fields to describe protocols and interfaces. And there were absolutely no problems porting between IAR and GCC.And peripheral header files as generated by tool vendors are the same for all compilers, and they work fine.

author: westfw
author_rank: Super Contributor
timestamp: November 08, 2017, 01:03:14 am
content: Quotethe biggest incompatibility I've seen is wrt endianness; you might have problems if you're using one of those arm chips with variable endianness. I used at least one compiler on x86 that couldn't represent the ip "fragment offset" as a direct-mapped structure bitfield (13bits), because it would have needed to byteswap before doing the bitfield extraction to get all the bits in the same bitfield.