tags:

views:

288

answers:

4

I've got 32-bit registers with field defined as bit-masks, e.g.

#define BM_TEST_FIELD 0x000F0000

I need a macro that allows me to set a field (defined by its bit-mask) of a register (defined by its address) to a given value. Here's what I came up with:

#include <stdio.h>
#include <assert.h>

typedef unsigned int u32;

/* 
 * Set a given field defined by a bit-mask MASK of a 32-bit register at address
 * ADDR to a value VALUE.
 */
#define SET_REGISTER_FIELD(ADDR, MASK, VALUE)                                      \
{                                                                                  \
  u32 mask=(MASK); u32 value=(VALUE);                                              \
  u32 mem_reg = *(volatile u32*)(ADDR); /* Get current register value           */ \
  assert((MASK) != 0);                  /* Null masks are not supported         */ \
  while(0 == (mask & 0x01))             /* Shift the value to the left until    */ \
  {                                     /* it aligns with the bit field         */ \
    mask = mask >> 1; value = value << 1;                                          \
  }                                                                                \
  mem_reg &= ~(MASK);                   /* Clear previous register field value  */ \
  mem_reg |= value;                     /* Update register field with new value */ \
  *(volatile u32*)(ADDR) = mem_reg;     /* Update actual register               */ \
}

/* Test case */
#define BM_TEST_FIELD 0x000F0000
int main()
{
  u32 reg = 0x12345678;
  printf("Register before: 0x%.8X\n", reg);/* should be 0x12345678 */
  SET_REGISTER_FIELD(&reg, BM_TEST_FIELD, 0xA);
  printf("Register after: 0x%.8X\n", reg); /* should be 0x123A5678 */
  return 0;
}

Is there a simpler way to do it?

EDIT: in particular, I'm looking for a way to do reduce the run-time computing requirements. Is there a way to have the pre-processor compute the number of required left-shifts for the value?

+1  A: 

Why not just put both the mask and the value in the right place?

#define BM_TEST_FIELD (0xfUL << 16)
#define BM_TEST_VALUE (0xaUL << 16)
#define mmioMaskInsert(reg, mask, value) \
   (*(volatile u32 *)(reg) = (*(volatile u32 *)(reg) & ~(mask)) | value)

Then you can just use it like:

mmioMaskInsert(reg, BM_TEST_FIELD, BM_TEST_VALUE);

For sure what you have there is very dangerous. Register writing can often have side-effects, and these operations:

mem_reg &= ~(MASK);
mem_reg |= value;

are actually writing to the register twice, instead of once, like you probably intend to. Also, why isn't a mask of 0 supported? What if I want to write to the whole register (timer count match or something)? Do you have a different macro for that operation? If so, why not use it as part of this system?

Another note - it might be a good idea to apply the mask to the value before sticking it in the register, in case someone passes a value that has more bits than the mask does. Something like:

#define maskInsert(r, m, v) \
  (*(volatile u32 *)(r) = (*(volatile u32 *)r & ~(m)) | ((v) & ~(m)))
Carl Norum
Carl, the registers I need to write to are located in a peripheral module that is connected to the processor bus. mem_reg is just a variable that will typically be allocated on the stack and accessing it should have no side effects.
geschema
A mask of zero is not supported because a bit field must consist of at least one bit by definition. Writing to the whole register is done by setting the bit-mask to all ones.
geschema
+1  A: 

I would consider using bitfields to "format" bits for hardware, e.g.:

#include <stdio.h>
#include <inttypes.h>

struct myregister {
    unsigned upper_bits:12;
    unsigned myfield:4;
    unsigned lower_bits:16;
};

typedef union {
    struct myregister fields;
    uint32_t value;
} myregister_t;

int main (void) {
    myregister_t r;
    r.value = 0x12345678;
    (void) printf("Register before: 0x%.8" PRIX32 "\n", r.value);
    r.fields.myfield = 0xA;
    (void) printf("Register after: 0x%.8" PRIX32 "\n", r.value);
    return 0;
}

Edit: Note the follow-up discussion in the comments. There are valid arguments against using bitfields, but, in my opinion, also benefits (especially in syntax, which I value greatly). One should decide based on the circumstances the code will be used in.

Arkku
Please, NEVER NEVER EVER use bitfields for this kind of operation. They're badly supported in many compilers, the standard doesn't guarantee anything about their layout, and they make for huge portability problems. You are asking for a world of hurt if you follow this advice. The read/modify/write rules for accessing a given bitfield are particularly vague, and you'll end up with side-effects that are hard or impossible to debug, and code generation that will make you cry.
Carl Norum
@Carl Norum: Umm, modifying hardware registers is about as unportable as it gets; if bitfield happen to work in this inherently unportable scenario, why not use them? Of course, one should check that the generated code satisfies any performance requirements and run the above example to verify the correct result.
Arkku
C code is portable to different compilers on the same hardware. Just because they work with one compiler on that hardware doesn't mean they will work with another compiler on the same hardware. One thing I will *guarantee* you is that some day you will need to use a different compiler.
Carl Norum
@Carl Norum: I would be extremely surprised if a compiler didn't manage to layout a bitfield of the hardware's register size correctly, but it is certainly true that it's not strictly guaranteed. But like I said in the previous comment, one should check the generated code performance and correctness. If neither is an issue with the current compiler, I would use bitfields and change to manually shifting if it should become necessary. But that may be because I haven't been bitten by them with any of the compilers (for microcontrollers) that I've used them on to poke around hardware registers.
Arkku
Using the word "correctly" I think shows you have a lot of potentially dangerous assumptions. Any way where the data stored is retrieved unaltered is correct. There's no guarantee (even implicit) that the data is packed into memory in any particular fashion. It might not be packed at all!Just asking in your version of "correctly", is the first item declared in the highest bits of a word or the lowest? They'll pack just fine either way, so why is one way more correct than the other?
Southern Hospitality
@Southern Hospitality: First of all, the reason why C has bitfields is to allow packing data, and the standard does say: "An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit." I would argue that even though packing is not strictly guaranteed, worrying about some *possible* future compiler *for the same hardware* not packing bitfields in multiples of the hardware's word size is quite premature.
Arkku
@Southern Hospitality: As for the order of the storage units, I do know it's not guaranteed either—that's why I say above to verify the correct result. But again, I would argue that since formatting bits for a hardware register is inherently unportable, I don't think it's unreasonable to develop using bit fields if they work correctly *for now* and their performance is satisfactory.
Arkku
Arkku
(So, dangerous assumptions, yes, but I wouldn't say *unreasonably* dangerous compared to poking around a hardware register. But, one should decide based on the circumstances. Like I said before. The most dangerous assumption I'm making is probably that whoever uses a feature of C reads the standard to determine whether or not it's suitable *for their use* and tests with *their compiler*. =)
Arkku
+1  A: 

If you insist on this specific interface (the position of the field is defined by the mask), then probably the only thing that can be changed/improved in your implementation is the cycle where you shift the value to the proper position (to align it with the mask). Basically, what you have to do is to find the offset expressed in the number of bits, and the shift the value left that number of bits. You used a plain cycle to perform that operation, and instead of explicitly calculating the offset in bits you simply shift the value left 1 bit at each iteration. This will work. However, it might be seen as inefficient, especially for fields that reside in the upper portion of the register, since they will require more iterations of the shifting cycle.

In order to improve the efficiency you can also use any of the rather well-known, potentially more efficient methods to calculate the offset value, as the ones described on this page. I don't know whether this is worth the effort in your case though. It might make your code more efficient, but it might also make it less readable. Decide for yourself.

AndreyT
+2  A: 

EDIT: in particular, I'm looking for a way to do reduce the run-time computing requirements. Is there a way to have the pre-processor compute the number of required left-shifts for the value?

Yes:

value *= ((MASK) & ~((MASK) << 1))

This multiplies value by the lowest set bit in MASK. The multiplier is known to be a constant power of 2 at compile time, so this will be compiled as a simple left shift by any remotely sane compiler.

Matthew Slattery
AndreyT
That's a great trick!
geschema