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.

TMS320F28377D: Question regarding integer overflow and literal typing in TI E2E thread #1644136

Part Number: TMS320F28377D
Other Parts Discussed in Thread: TEST2

Hi,

While browsing the community, I came across the thread linked below and had a few technical questions regarding the TI engineer's response.

Thread URL: https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1644136/tms320f28p650dk-the-program-for-tms320f28p650dk

In the original post, the author provided the following constant definition and code snippet:

#define CURRENT_SAMPLE_DEFAULT_OFFSET_MAX 39321 // 32768 * 1.2
volatile int32_t temp2;
temp2 = CURRENT_SAMPLE_DEFAULT_OFFSET_MAX; // temp2 = 2457

I read the TI engineer's explanation regarding this, but I am somewhat confused by it.

On a TMS320F28x MCU, even if we define the macro as a bare decimal literal and assign it to a 32-bit variable like above, the value 39321 is successfully and correctly stored in temp2. According to the C standard specifications, the type of an unsuffixed decimal integer literal is automatically determined as the first type in the sequence of int → long int → long long int that can fully represent its value. Since 39321 exceeds the maximum value of a signed 16-bit int (32767), it should inherently be treated as a 32-bit long int.

Even if the value 39321 were processed as a 16-bit type and truncation occurred, the result would still not be 2457. (Of course, if the value is assigned to a 12-bit register, 0x9999 would be truncated to 0x0999, which explains where the value 2457 comes from.)

If we perform compile-time arithmetic operations that overflow 16-bit limits without explicit casting, truncation due to overflow will occur as shown in the examples below. However, even in those cases, the TI compiler alerts the user during the build process with a specific warning message:

#define TEST1 (30000 * 30000)
#define TEST2 (0x4120 << 8)

volatile int32_t temp1, temp2;

temp1 = TEST1; // temp1 = -5888 (Truncated)
temp2 = TEST2; // temp2 = 8192  (Truncated)

[Compiler Warning]
"xxx.c", line nnn: warning #62-D: integer operation result is out of range

(While it is true that attaching suffixes like L or UL when defining constants is the correct method and good habit to prevent truncation phenomena like the example) Given this compiler behavior and the C standard rules for literal promotion, is the explanation provided in the original TI response entirely accurate? Or am I missing some C2000-specific compiler behavior here?

I would appreciate any further clarification on this matter.

Best regards,
Sang-il

  • Hi Sang-il

    Your understanding is correct. The assignment temp2 = 39321; (via macro expansion) will store the value correctly in an int32_t variable on the TMS320F28377D. The C standard's literal promotion rules apply as you described, and the TI engineer's explanation in the original thread appears to have conflated two different scenarios.

    The C28x int is 16 bits — but promotion rules still work

    On C2000 devices, the standard data type sizes are [1]:

    Type
    Size
    char
    16 bits
    int
    16 bits
    long
    32 bits
    long long
    64 bits

    Per the C standard, an unsuffixed decimal literal's type is determined as the first type in the sequence (int → long int → long long int) that can represent its value. Since 39321 exceeds the signed 16-bit int maximum of 32767, the compiler automatically promotes it to long (32 bits). The assignment to int32_t temp2 then proceeds without truncation. This behavior is confirmed by multiple TI engineer responses in analogous threads [2][3].

    Where the value 2457 actually comes from

    As you correctly identified, 39321 = 0x9999. If this value were written to a 12-bit register field, the upper nibble would be masked off: 0x9999 → 0x999 = 2457. This is a register bit-width truncation issue, not a C language integer literal typing issue. The original thread likely involved assignment to a peripheral register with a 12-bit field, not a general-purpose 32-bit variable.

    When truncation does occur: arithmetic expressions

    Your TEST1 and TEST2 examples correctly demonstrate the scenario where overflow happens. In (30000 * 30000), both operands are int (16-bit on C28x), so the multiplication is performed in 16-bit arithmetic before the result is assigned. The compiler correctly warns with #62-D: integer operation result is out of range [4]. This is fundamentally different from a standalone literal like 39321, which receives its type based on its value, not on arithmetic between smaller operands.

    Similarly, 1 << 31 on C28x is undefined behavior because the literal 1 is a 16-bit int, and shifting by more than the operand's bit-width is undefined [2][3].

    Summary of the distinction

    Scenario
    Behavior
    Truncation?
    temp2 = 39321;
    Literal promoted to long per C standard
    No
    temp2 = (30000 * 30000);
    Arithmetic performed in 16-bit int
    Yes (with warning #62-D)
    reg_12bit = 0x9999;
    Value written to 12-bit register field
    Yes (hardware truncation)

    Best practice remains: use suffixes

    While the compiler handles standalone literals correctly, explicitly using L or UL suffixes (e.g., 39321L) is still recommended practice [5] because it:

    • Makes intent explicit to code reviewers
    • Prevents issues if the literal is later used in arithmetic expressions
    • Aligns with MISRA-C guidelines for embedded development

    Conclusion

    You are not missing any C2000-specific compiler behavior. Our response in thread #1644136 addressed the specific context of a 12-bit register assignment (explaining the 2457 result), but may have been imprecise in attributing the truncation to the literal's type rather than the destination register's bit-width. Your understanding of C standard literal promotion rules is accurate and applies correctly to the C28x compiler.


    To help refine this answer, it would be helpful to know:

    • The exact context from thread #1644136 — specifically whether the original code was assigning to a peripheral register with a 12-bit field (which would fully explain the 2457 result)
    • The compiler version and ABI (COFF vs EABI)
    • Whether additional arithmetic operations were involved in the original code that aren't shown in the snippet

    1. TI Application Note - C28x Data Type Sizes (SPRAD88A)
    2. TMS320F28377D: Unable to access CAN 32-Bit register - E2E
    3. CCS/TMS320F28377D: Bit-shift Problem - E2E
    4. TMS320F28P65x Technical Reference Manual (SPRUIZ1B)
    5. C28x Compiler Type Definitions (SPNU629A)

    Best Regards,

    Zackary Fleenor

  • Hi Fleenor,

    Just to clarify, thread #1644136 was not originally posted by me, so I don't have the full background details on that specific case. I simply came across it while searching the community forum for relevant information and decided to ask a follow-up question because the previous response seemed a bit unclear.

    Regardless, your explanation has helped me better understand and clarify the matter.

    Thank you for your help.

    Best regards,
    Sang-il