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.

TMS320F280041: Inline function not compiled properly

Part Number: TMS320F280041

Hi,

it seems that a simple inline function is not compiled properly. Here is the inline function that clears register bit:

static inline void
CAN_disableMailbox(uint32_t base, uint16_t mbx)
{
    (*((volatile uint32_t *)((uintptr_t)(base + 0U + 0x0U)))) &= ~(((uint32_t) 1U) << (mbx-1U));
}

Let's assume that initial value of register being modified is 0. When the above function is implemented as "inline", the register after function is 0xFFFF0000, and when it is implemented as regular static function, the register is 0x00000000.

I am still trying to figure out from asm what is the root cause for this problem.

Is there anything you could see as being problematic for the above implementation?

It works as expected if I expand the function line as:

uintptr_t ptr = (uintptr_t) (priv->base + 0U + 0x0U);
volatile uint32_t *pptr = (volatile uint32_t *) ptr;
uint32_t reg = *pptr & ~(((uint32_t) 1U) << (mbx - 1U));
*pptr = reg;

However, it does not work in the abbreviated form:

*pptr &= ~(((uint32_t) 1U) << (mbx - 1U));

  • For a source file that contains a call to this function where this happens ...

    When the above function is implemented as "inline", the register after function is 0xFFFF0000

    ... please follow the directions in the article How to Submit a Compiler Test Case.  If there is more than one call to CAN_disableMailbox in this source file, please indicate which call has this problem.

    Thanks and regards,

    -George

  • It seems that the problem is not inline function per-se, but expressions with pointers of "volatile uint32_t *" type:

    // None of the patterns below work correctly when 'ptr' is of
    // "volatile uint32_t *" type and 'val' is of "uint32_t" type
    *ptr = *ptr | val;
    *ptr |= val;
    *ptr = *ptr & ~val;
    *ptr &= ~val;
    
    // This works correctly
    reg = *ptr;
    *ptr = reg | val;
    *ptr = reg & ~val;

    P.S. I sent you the source (preprocessed file) in private message.

  • I received the test case.  Thank you.  I apologize for not getting to it today.  I expect to look at it first thing tomorrow.

    Thanks and regards,

    -George

  • No worries, I have a workaround, but would still like to understand what happened here.

    I noticed that in hw_types.h in driverlib, there are two macros:

    #define HWREG(x)                                                              \
            (*((volatile uint32_t *)((uintptr_t)(x))))
    #define HWREG_BP(x)                                                           \
            __byte_peripheral_32((uint32_t *)(x))

    What is the difference between the two? Could this be the root cause of issues I'm having?

    Just to give some background, I am trying to modify 32-bit CAN registers with HWREG macro, as follows:

    HWREG(addr) |= val;

  • I'm not certain, but I suspect you are affected by the previously reported problem EXT_EP-10160.  You currently use compiler version 18.12.1.LTS.  The problem is fixed in version 20.2.4.LTS.  The article Updating the Compiler shows how to update the compiler.  Is it practical for you to try version 20.2.4.LTS to see if it fixes the problem?

    Thanks and regards,

    -George

  • I tried with 20.12.0.STS (latest version), and it did not solve the problem. Should I try with 20.2.4.LTS?

    I sent you more files in the private message.

    Can you please review question in my last message. It seems that if I use HWREG(x) to modify 32-bit peripheral register it does not work, but if I use HWREG_BP(x) it seems to work. Could this be the root cause, what is the difference between the two?

  • It seems that if I use HWREG(x) to modify 32-bit peripheral register it does not work, but if I use HWREG_BP(x) it seems to work. Could this be the root cause

    Maybe.  I lack the expertise to answer.  I have notified the C2000 experts who can help.

    Thanks and regards,

    -George

  • It seems that SPRAA85E, Section 7.1 "eCAN Control Registers" (p. 23) might provide an answer what happened here.

    Access to eCAN control 32-bit registers must not be broken to two 16-bit accesses, which actually happens in my case. Here is the disassembly of the "problematic" code. Notice two 16-bit accesses to XAR4, where *+XAR4[0] and *+XAR4[1] are low and high 16 bits of a single eCAN control register.

    000082cd   a9a9   MOVL         ACC, P
    000082ce   8ae3   MOVL         XAR4, *+XAR3[4]
    000082cf   ff55   NOT          ACC
    000082d0   1e46   MOVL         *-SP[6], ACC
    000082d1   9246   MOV          AL, *-SP[6]
    000082d2   c0c4   AND          *+XAR4[0], AL
    000082d3   9245   MOV          AL, *-SP[5]
    000082d4   c0cc   AND          *+XAR4[1], AL
    000082d5   92d1   MOV          AL, *+XAR1[2]
    000082d6   56c1   BF           210, EQ

    On the other hand, if I use __byte_peripheral_32() compiler intrinsic, the access is not broken. Here is the disassembly of working code. Notice single 32-bit access to *+XAR4[0] which is eCAN control register.

    000082cd   8ae3   MOVL         XAR4, *+XAR3[4]
    000082ce   06a6   MOVL         ACC, XAR6
    000082cf   ff55   NOT          ACC
    000082d0   ff5a   MOVL         P, ACC
    000082d1   06c4   MOVL         ACC, *+XAR4[0]
    000082d2   ceab   AND          AL, PL
    000082d3   cfaa   AND          AH, PH
    000082d4   1ec4   MOVL         *+XAR4[0], ACC
    000082d5   92d1   MOV          AL, *+XAR1[2]
    000082d6   56c1   BF           205, EQ

    Can you please confirm that this indeed might be the root cause of issues I am having?

    Follow-up question: Should I always use __byte_peripheral_32() compiler intrinsic when accessing 32-bit peripheral registers? Is there any downside of using this intrinsic other than compiler not being able to (maybe) do some optimization?

  • Hi Marko,

    CAN peripheral is designed for a 8 bit byte addressable architecture and C2000 devices are a 16 bit byte machine, an address/data bus bridge was added to translate the accesses so the C28x could interface with CAN.

    At higher optimizations the compiler can convert a 32-bit write to a CAN register into two 16-bit accesses which can cause an issue using the CAN bus bridge which uses the byte addressing

    the "__byte_peripheral_32 " intrinsic is the optimal way to access such peripherals. The instrintric essentially prevents the code from translating 32-bits into two 16-bit accesses which messes up with the byte bridge.  refer the following related post 

    https://e2e.ti.com/support/microcontrollers/c2000/f/c2000-microcontrollers-forum/442233/controlsuite-can-change-information/1586639#1586639

    There is no other side effect of using this intrinsic.

    Best Regards

    Siddharth