views:

197

answers:

6

I'm working on an embedded project (PowerPC target, Freescale Metrowerks Codewarrior compiler) where the registers are memory-mapped and defined in nice bitfields to make twiddling the individual bit flags easy.

At the moment, we are using this feature to clear interrupt flags and control data transfer. Although I haven't noticed any bugs yet, I was curious if this is safe. Is there some way to safely use bit fields, or do I need to wrap each in DISABLE_INTERRUPTS ... ENABLE_INTERRUPTS?

To clarify: the header supplied with the micro has fields like

union {
        vuint16_t R;
        struct {
            vuint16_t MTM:1;        /* message buffer transmission mode */
            vuint16_t CHNLA:1;      /* channel assignement */
            vuint16_t CHNLB:1;      /* channel assignement */
            vuint16_t CCFE:1;       /* cycle counter filter enable */
            vuint16_t CCFMSK:6;     /* cycle counter filter mask */
            vuint16_t CCFVAL:6;     /* cycle counter filter value */
        } B;
    } MBCCFR;

I assume setting a bit in a bitfield is not atomic. Is this a correct assumption? What kind of code does the compiler actually generate for bitfields? Performing the mask myself using the R (raw) field might make it easier to remember that the operation is not atomic (it is easy to forget that an assignment like CAN_A.IMASK1.B.BUF00M = 1 isn't atomic).

Your advice is appreciated.

+3  A: 

Atomicity depends on the target and the compiler. AVR-GCC for example trys to detect bit access and emit bit set or clear instructions if possible. Check the assembler output to be sure ...

EDIT: Here is a resource for atomic instructions on PowerPC directly from the horse's mouth:

http://www.ibm.com/developerworks/library/pa-atom/

Peter G.
When it can, but not all addresses support this, and this can't be done unless the address is known at compile time.
nategoose
@nategoose Yes, better to spell it out like you did than to imply like I did.
Peter G.
+3  A: 

It is correct to assume that setting bitfields is not atomic. The C standard isn't particularly clear on how bitfields should be implemented and various compilers go various ways on them.

If you really only care about your target architecture and compiler, disassemble some object code.

Generally, your code will achieve the desired result but be much less efficient than code using macros and shifts. That said, it's probably more readable to use your bit fields if you don't care about performance here.

You could always write a setter wrapper function for the bits that is atomic, if you're concerned about future coders (including yourself) being confused.

Nathon
A: 

It totally depends on the architecture and compiler whether the bitfield operations are atomic or not. My personal experience tells: don't use bitfields if you don't have to.

mack369
+3  A: 

Yes, your assumption is correct, in the sense that you may not assume atomicity. On a specific platform you might get it as an extra, but you can't rely on it in any case.

Basically the compiler performs masking and things for you. He might be able to take advantage of corner cases or special instructions. If you are interested in efficiency look into the assembler that your compiler produces with that, usually it is quite instructive. As a rule of thumb I'd say that modern compilers produces code that is as efficient as medium programming effort would be. Real deep bit twiddeling for your specific compiler could perhaps gain you some cycles.

Jens Gustedt
A: 

I'm pretty sure that on powerpc this is not atomic, but if your target is a single core system then you can just:

void update_reg_from_isr(unsigned * reg_addr, unsigned set, unsigned clear, unsigned toggle) {
   unsigned reg = *reg_addr;
   reg |= set;
   reg &= ~clear;
   reg ^= toggle;
   *reg_addr = reg;
}

void update_reg(unsigned * reg_addr, unsigned set, unsigned clear, unsigned toggle) {
   interrupts_block();
   update_reg_from_isr(reg_addr, set, clear, toggle);
   interrupts_enable();
}

I don't remember if powerpc's interrupt handlers are interruptible, but if they are then you should just use the second version always.

If your target is a multiprocessor system then you should make locks (spinlocks, which disable interrupts on the local processor and then wait for any other processors to finish with the lock) that protect access to things like hardware registers, and acquire the needed locks before you access the register, and then release the locks immediately after you have finished updating the register (or registers).

I read once how to implement locks in powerpc -- it involved telling the processor to watch the memory bus for a certain address while you did some operations and then checking back at the end of those operations to see if the watch address had been written to by another core. If it hadn't then your operation was sucessful; if it had then you had to redo the operation. This was in a document written for compiler, library, and OS developers. I don't remember where I found it (probably somewhere on IBM.com) but a little hunting should turn it up. It probably also has info on how to do atomic bit twiddling.

nategoose
Wow, that sounds like a recipe for deadlock:Processor A starts critical section, looks at address. Processor B starts critical section, looks at address. Processor A finishes writing. Processor B finishes writing. Processor A looks at address. It's changed! Processor A rewrites data. Processor B looks at address. It's changed! Processor B rewrites data. Continue in infinite loop.(Sorry about the formatting. Comments don't seem to like it.)
Nathon
@Nathon: It's a little bit more complicated and simple than that, but it's been a while since I read the documentation. There really was something that kept it from being a deadlock situation, but also allowed more flexibility than the single atomic instruction approach used on other architectures. It may have been that the final write to RAM didn't happen if another processor had written to that address after this processor started watching it.
nategoose
It's known as "load linked/store conditional" on some architectures. PPC calls it `lwarx` (load word and reserve indexed) and `stwcx.` (store word conditional indexed (and record)). The store is conditional on nobody else having written to the memory; one processor will always win. Additionally, the "reservation" is cleared across context switches. Either way, you don't want to do it on hardware bitfields...
tc.
@tc.: Is the reason that you wouldn't do it directly on a memory mapped hardware register directly be because it may change without issuing a _store_ _address_ on the memory bus (where _address_ is the reserved address {the address of the bitfield} ). It's been a long time. I can't remember if when I wrote this before I was thinking of doing that or putting a lock around it, but neither would protect against the hardware register being changed off bus (truly volatile).
nategoose
I haven't done any hardware programming, but my experience is that atomic accesses are rarely useful in isolation. You'll probably want something more like "stick an address in register A, stick a length in register B, start DMA I/O by fiddling the bits in register C". The other thing is that a lot of hardware registers are single bytes, whereas PPC's load/store reserved instructions act on entire words (or doublewords); the bitfield in the question is only a halfword.
tc.
+2  A: 

I think that using bitfields to model hardware registers is not a good idea.

So much about how bitfields are handled by a compiler is implementation-defined (including how fields that span byte or word boundaries are handled, endianess issues, and exactly how getting, setting and clearing bits is implemented). See http://stackoverflow.com/questions/1490092/c-c-force-bit-field-order-and-alignment/1490669#1490669

To verify that register accesses are being handled how you might expect or need them to be handled, you would have to carefully study the compiler docs and/or look at the emitted code. I suppose that if the headers supplied with the microprocessor toolset uses them you can be assume that most of my concerns are taken care of. However, I'd guess that atomic access isn't necessarily...

I think it's best to handle these type of bit-level accesses of hardware registers using functions (or macros, if you must) that perform explicit read/modify/write operations with the bit mask that you need, if that's what your processor requires.

Those functions could be modified for architectures that support atomic bit-level accesses (such as the ARM Cortex M3's "bit-banding" addressing). I don't know if the PowerPC supports anything like this - the M3 is the only processor I've dealt with that supports it in a general fashion. And even the M3's bit-banding supports 1-bit accesses; if you're dealing with a field that's 6-bits wide, you have to go back to the read/modify/write scenario.

Michael Burr