This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430 compiler, multiplication failure involving bit fields

Other Parts Discussed in Thread: CC1101, MSP430F5528

Since the MSP430 compiler upgrade to 4.3.x I have found a multiplication problem that did not exist in the 4.2.x version.

The code example below will be using the specific compiler versions 4.3.3 and 4.2.1 to illustrate the problem.

Background: We are using the CC1101 with the MSP430. The registers accessors use bit fields to get/set the registers. It makes for readable code.

Here is the function for calculating symbol rate:

/**
 * @details
 * Rdata = [ (256 + DRATE_M) * 2 ^ DRATE_E / 2^18 ] * FxOsc
 *
 * @example
 *  DRATE_E = MDMCFG4.DRATE_E       = 0x0B;  11
 *  DRATE_M = MDMCFG3.DRATE_M       = 0x83; 131
 *
 *  Rdata = [ (256 + 131) * 2 ^ 11 / 2 ^ 28 ] * 26MHz = 76.767 K baud
 */
uint32_t cc1101SymbolRate(void)
{
    const uint8_t mdmCfg4Val = cc1101Read(CC1101_REGISTER_MDMCFG4);
    const uint8_t mdmCfg3Val = cc1101Read(CC1101_REGISTER_MDMCFG3);

    const struct CC1101_REG_MDMCFG4 mdmCfg4 = * (const struct CC1101_REG_MDMCFG4 *) &mdmCfg4Val;
    const struct CC1101_REG_MDMCFG3 mdmCfg3 = * (const struct CC1101_REG_MDMCFG3 *) &mdmCfg3Val;

    uint64_t Rdata = (256 + mdmCfg3.DRATE_M);
    Rdata *= CC1101_FxOSC;
    Rdata /= iexp2(28 - mdmCfg4.DRATE_E);

    return (uint32_t) Rdata;
}

Here are some actual register values:

CC1101 register 0x10: 0x7b 'MDMCFG4'
CC1101 register 0x11: 0x83 'MDMCFG3'
CC1101 register 0x12: 0x13 'MDMCFG2'
CC1101 register 0x13: 0x22 'MDMCFG1'
CC1101 register 0x14: 0xf8 'MDMCFG0'


Here are the bit field data structs and other symbols, functions:

/// The CC1101 Crystal oscillator nominal frequency in Hz.
#define CC1101_FxOSC                    ((uint32_t) 26UL * 1000UL * 1000UL)

// Calculate 2^power, where power is an unsigned integer.
inline uint32_t iexp2(uint8_t power)
{
    uint32_t value = 0x01;
    value <<= power;
    return value;
}

struct __attribute__(__packed__) CC1101_REG_MDMCFG4 ///< Reg: 0x10
{
    unsigned int DRATE_E            : 4;            ///< [3:0]
    unsigned int CHANBW_M           : 2;            ///< [5:4]
    unsigned int CHANBW_E           : 2;            ///< [7:6]
};

struct __attribute__(__packed__) CC1101_REG_MDMCFG3 ///< Reg: 0x11
{
    unsigned int DRATE_M            : 8;            ///< [7:0]
};

Under version 4.2.1 Compiler, the result (as expected) for our Symbol Rate is

Symbol Rate           :      76766 baud


Under the 4.3.3 compiler the unexpected (surprise!) symbol rate is:
Symbol Rate           :      75180 baud

Other calculations are also off.

 

I have tried using intermediate variables that are not bit fields -- but come up with the same results.

 

Additionally, I tried straight 32 bit to 64 bit multiplies and divides - however they work out properly.

The CPU is MSP430F5528.

Thanks,

 

Nat

 

  • I suggest posting the disassembly for the cc1101SymbolRate function, both using bitfields and when the bitfield values are replaced with numeric literals taken from the register values you posted.

  • I will attempt to do so today.

    Thanks ...

  • Nat Ersoz said:
    Under the 4.3.3 compiler the unexpected (surprise!) symbol rate is:
    Symbol Rate           :      75180 baud

    I created the attached project in CCS6 to try and repeat the problem, based upon the code shown in your post. 1777.MSP430_bitfields.zip

    With this project compiled using the 4.3.3 compiler for a MSP430F5528 the answer reported is the "correct" value of 76766. i.e. I have been unable to re-create the problem.

    What compiler options are you using?

    [I tried different compiler optimizations but the code always produced the correct result]

  • Thank you for checking! I will try to get an assembler list file to you. Right now we are on deadline and its hard to take time for this in the coming week. Certainly after this week.

    I have used the 4.3., 4.3.2 and 4.3.3 compiler - all with incorrect results. Compiler optimizations are typically set for smallest code size. F5 options for multiplier.

    With the 4.2.1, it works as expected.

    Thanks for your help. This can wait for a week on our end...

  • “unsigned int DRATE_E            : 4;            ///< [3:0]”

    [3:0] is is an assumption. AFAIK, the C standard never promises any ‘position’ of the reserved bits.  It just promises that all elements are put into the same container unit (an unsigned int), as long as they fit, before the next container unit is allocated.

    It is undefined whether the bits are packed MSB first or LSB first. It’s even undefined whether an element that doesn’t fit into the current allocation unit anymore will flow over to the next or completely go into the next.

    A bitfield containing
    Int a:1;
    Int b:16;
    int c:1;
    Might be four or six bytes long. B might be stored partly in the first and partly in the second int, or have an int on its own, while a and c get their own int before and after b.

    In theory, if the packed attribute is defined, the compiler would be allowed to reorder the three, putting a and c in one int and b in a second. Bu tI don’t know of any implementation that does it. Usually, in packed case,, b would be split, while without packed attribute, b would go into its own int.

    The reason for this behavior is clear: a C program, by the standard, runs in a self-contained system. It is unimportant, which bits are used for storing DRATE_E, as long as they always the same.
    Likely, the order in which bits are allocated for bitfields has changed, breaking code that was only coincidentally working.

    I remember a notation where you can directly give the bit range that belongs to an element. However, this was Espire, an assembler derivate that supported local variables, objects and inheritance, used for programming for the GEOS OS.

  • I finally had a chance to revisit this issue. The 4.2.x and later compilers allocate 16 bits of space for bitfields that are only 8 bits long. Using them to perform 8-bit register access is a fools errand. Oh well. I'm the fool.

**Attention** This is a public forum