Question

I'm programming C on an embedded system. The processor architecture is 32 bits (sizeof(int) is 32 bits, sizeof(short) is 16 bits). There is a 32-bit variable that is a memory-mapped control register (CTRL_REG) that is specified as only the bottom 16 bits being used, and they contain a signed 16 bit integer value (writing to the higher bits has no effect). Memory accesses must be 32-bit aligned, so I can't just bump the pointer over a couple bytes, and also I cannot assume an endianness. I'm concerned that automatic type promotion is going to mess with whatever I'm storing by extending the sign bit out to bit 31 instead of leaving it at bit 15 where I want it. What is the best way to store something in this location?

Here was my original code, which I am nearly certain is wrong:

#define CTRL_REG   *((volatile unsigned int *)0x4000D008u)
short calibrationValue;

CTRL_REG = -2 * calibrationValue;

Then I tried this, but I think it still might be subject to integer promotion at the point of the assignment:

CTRL_REG = (short)(-2 * calibrationValue);

Then finally I thought of this:

CTRL_REG = (unsigned short)(short)(-2 * calibrationValue);

I can't evaluate these options very well because they all work in my tests because calibrationValue happens to be negative (it's a calibration parameter specific to each device, and so could be positive on some devices), so after multiplying by -2, I end up storing a positive value and thus I don't actually run into the problem I'm expecting in my tests.

Your help is greatly appreciated, even if it's just to say "you're thinking too much".

Was it helpful?

Solution

Think about what a -16 ( for example ) looks like in 16 bit : '0xFFF0' , and in 33 bit: '0xFFFFFFF0'. Sign extending is exactly what you want to ensure you have a sign bit in the right place. Since the upper 16 are don't-care, it's fine to fill them with 1s. So create a signed 32 bit value, then cast it to an unsigned 32 to put in your register:

  Int32 calreg= -2L * calibrationValue;
   CTRl_REG = (Uint32)calreg;

If you would rather write 0s to the high bits, mask with 0xFFFF before the cast.

OTHER TIPS

Instead of an unsigned int, define a union of an unsigned int and 2 short signed ints.

That's how I handle the 'funny hardware control registers' on my ARM systems where, quite often, odd bits here and there are either 'don't care' or 'must not have 1 written to them'.

Rgds, Martin

If the values written to the most significant 16 bits is ignored I think you can safely rely on type conversion. All in all, you don't care about the higher bits.

#define CTRL_REG   *((volatile signed int *)0x4000D008u)
short calibrationValue;

CTRL_REG = -2 * calibrationValue;

Notice that the CTRL_REG is declared as a signed integer now.

#define SS2L(sh,sl) ( ((sh) <<16) | ((sl) & 0xffff) )

To combine two 16 bit shorts into one 32 bit long.

It is best for bitops to be done using unsigned types; promotion to int can grab you. Maybe add some extra casts to unsigned to the above macro, too.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top