F29H85X-SOM-EVM: F29-SDK: sysctl_delay inccorect with optimization

Part Number: F29H85X-SOM-EVM

Hello,

I am currently working on the F29H85x platform and I observed an unexpected behavior related to the DEVICE_DELAY_US() macro when optimizations are enabled.

I started from the official example: led_ex1_blinky. The code runs as expected with lower optimization levels, but when enabling -O1 -flto the LED blinking behavior becomes incorrect (timing is inconsistent or completely wrong).

The issue seems to come from the implementation of the delay:

void SysCtl_delay(uint32_t count)
{
    __asm volatile("    MV A0, D0           \n" \
                   "    DECB A0, #1, 0x0    \n");
}

However, if I move SysCtl_delay() into a separate .c file (instead of being inlined), the behavior becomes correct again, even with LTO optimizations enabled.

It looks like the problem is related to the Link Time Optimization (LTO)

In the SDK documentation, it is stated in the "What is not supported" section:
"Link Time Optimization (LTO) compiler option for all the libraries and examples."

Does this mean that LTO should be disabled for all DriverLib files when building an application?

I would also like to get guidance regarding the use of Link Time Optimization (LTO) on F29H85x.

While LTO appears to be very effective in improving performance, I am concerned about its impact on legacy code when migrating from F28x to F29x.

In particular, I observed that even a simple delay function (SysCtl_delay), which is widely used in the examples, can behave incorrectly when LTO is enabled.

From a migration perspective, this raises an important question:
in which cases is it recommended to avoid using LTO, especially when dealing with legacy embedded code?

Thanks
  • Hi,

    We have identified the issue that you have mentioned and below is the fix:

    • The SysCtl_delay() is moved to a separate file
    • The definition is modified as below:
    void SysCtl_delay(uint32_t count)
    {
        // volatile because this block does not "write" anything, and would otherwise
        // be removed by the compiler during optimization.
        __asm volatile
        (
                "  MV       A14,    %[count]\n"
                "  SDECB    A14,    1\n"    /* No need for label if using the special SDECB */
                :                           /* No output registers written */
                : [count] "D" (count)       /* Generate asm symbol 'count', tied to C symbol
                                            'count', and tie it to the D register class */
                : "a14"                     /* A14 is written to, a.k.a. clobbered */
        );
    }
    We plan to enable LTO for the libs and examples in the upcoming LTS release in August
    regards,
    Anand
  • Thank you, this change resolves the issue.

    I still get a diagnostic message "unknown register name 'a14' C/C++(1118)", however the project compiles successfully and the code behaves correctly with LTO enabled.

    I also encountered another issue in "spi.c" when using optimization level -O3, which results in the following error: "error: ran out of registers".

    It is mentioned that this issue will be fixed in compiler version 3.0.0.0 LTS. Could you confirm when this version will be available? Is it also in August?

  • Hi,

    I don't observe "unknown register name 'a14' C/C++(1118)", what are your compile options?

    The "error: ran out of registers" will be fixed in compiler which will be part of LTS release.

    regards,

    Anand

  • Hi

    This warning seems to come from the "ms-vscode.cpptools" extension.

     If I disable this extension, the message disappears. I think this extension is not aware of TI-specific register names (such as "A14"), and therefore reports a false diagnostic.

     So this seems to be a limitation of the IntelliSense engine of this extension rather than a compiler issue.

  • Do you think you can consider another change for the new version of LTS release on ssu.h? 
    The following structure contains "DISABLE"
    struct
    {
    uint32_t LOCK : 1;
    uint32_t rsvd1 : 11;
    uint32_t START : 20;
    uint32_t COMMIT : 1;
    uint32_t rsvd2 : 11;
    uint32_t END : 20;
    uint32_t ACCESS;
    uint16_t rsvd3;
    uint32_t LINKID : 4;
    uint32_t rsvd4 : 2;
    uint32_t DISABLE : 1;
    uint32_t XE : 1;
    uint32_t APILINK : 4;
    uint32_t rsvd5 : 2;
    uint32_t APILINKE : 1;
    uint32_t rsvd6 : 1;
    } AP_REGS[96]; // 0x000 - 0x5F8

    However, DISABLE is a very common macro name that is often already defined in projects (e.g., for ENABLE/DISABLE values). This can lead to compilation issues when integrating the library, as the macro may conflict with this structure field.

    In the reference manual, this field is defined as "APD". Would it be possible to rename this field to "APD" in a future release to improve compatibility with existing projects that already use the DISABLE macro?

  • Hi,

    Sure, we will consider this feedback

    regards,

    Anand

  • Hi,

    I made the change requested; it will be available as part of the next release

    regards,

    Anand

  • Hi,

    The newer version of the compiler (2.2.1.LTS) is released with the fix for the below issue that you were facing:
    The "error: ran out of registers" will be fixed in compiler which will be part of LTS release.

    Pl. install from https://www.ti.com/tool/download/C29-CGT/2.2.1.LTS and verify

    regards,

    Anand

  • Hi Anand,

    I tested the new C29 compiler v2.2.1.LTS and I can confirm that the previous "ran out of registers" error in spi.c is no longer present. Thank you for the fix.

    I also compared the runtime performance against v2.0.0.STS and observed a small regression in one of our functions:

    • v2.0.0.STS: 9.66 µs
    • v2.2.1.LTS: 9.85 µs

    The difference is relatively small (~2%), but it's just to inform. The test is with the same optimization for the project O3 + RAM function + flto + ffast-math + finline-functions.

    In addition, I noticed a new error with v2.2.1.LTS that was not reported with v2.0.0.STS. It occurs when passing a uint32_t* pointer (which resolves to unsigned int*) to a parameter defined as const DT_U32* (internal data types) (which resolves to const unsigned long *):

    "incompatible pointer types passing 'uint32_t *' (aka 'unsigned int *') to parameter of type 'const DT_U32 *' (aka 'const unsigned long *') [-Wincompatible-pointer-types]"

    Both types are 32-bit on this platform, and this diagnostic was not generated with compiler v2.0.0.STS.

    Is this error expected due to stricter type checking in v2.2.1.LTS, or could this be a regression compared to the previous compiler version?

    Regards,

    Nicolas