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.

Compiler/TMS320F2806: Bug in compiler 18.1.4.LTS on accessing global variable?

Part Number: TMS320F2806

Tool/software: TI C/C++ Compiler

Hi,

I might be experiencing a bug with the current LTS compiler for the c2000 series. In essence, I'm doing something like the following.

In a file globals.c:

int foo = 1;

In a file bar.c

void bar(void){

printf("Foo is: %d\n", foo);

}

In a file baz.c

void baz(void){

printf("Foo is: %d\n", foo);

}

In a file main.c

void main(int){

bar();

baz();

for(;;){};

}

The printed result is something like "Foo is: 1\n Foo is: 0".

However, if I modify the print statement to: printf("Foo is: %d at %d\n", foo, (int32_t) &foo);, then the printed result is something like "Foo is: 1 at 12345\n Foo is: 1 at 12345".

Seemingly adding a print statement with the location of foo gives me the expected behavior.

I've debugged the code (as well as I can with the large level of compiler optimization that I've enabled) and foo is seemingly placed in a reasonable location in memory for both cases. As the application is moderately large it's hard for me to generate a minimum viable example, but I might be able to send the current code base under some conditions.

If anyone has any ideas as to how to progress from here I'd very much appreciate it!

Cheers,

Jonathan Lock

  • After additional debugging I have still not resolved the issue. I have tested using a hardware watchpoint to monitor for any inadvertent writes to my global variable (foo in the above example), which I have not found. (I have verified that the watchpoint works by testing writing to the variable). I haven't really used a lot of the debugging features before, but as far as I can tell a hardware watchpoint should be trigged by any write?

    I've also verified that the location of the global variable is reasonable (0x137B4), which lies near the end of the L8 RAM block. (I am using the 28069M, so the instaspin variables are located from 0x13800, so my global variable should be in a reasonable position).

    Reading assembly isn't really my forte (and with all the size optimization flags it isn't made any easier), but to me it seems most plausible that incorrect assembly code is generated by the compiler. I read a variable in two locations, it's not written to inbetween, and I get different results. Any ideas?

    //JL
  • Be sure printf isn't somehow failing.  Make sure you #include <stdio.h> .  Check on whether you have overflowed the stack, or run out of heap.  These tips, and more like them, are in the article Tips for Using Printf.

    Thanks and regards,

    -George

  • Hi George,

    In my application I'm not actually using printf, but a rudimentary print_integer function. (The above example was just to illustrate the behavior). This application doesn't use the heap at all, and the worst-case stack usage is only around 25% of the total stack size (verified by painting the stack on initialization and inspecting changed values, and by activating a watchpoint slightly before _STACK_END). Furthermore, it's not just my print function that gives unexpected behavior: some locations that perform a compare action on my global variable seem to act as if it is zero, while other locations seem to act as if it was nonzero.

    Cheers,

    Jonathan

    EDIT: also, to clarify, I've identified that modifying the print statements to include the location of my global variable causes the linker to change the location the variable is placed in, which might be a cause for the change in behavior. One possible cause for problems could be that I'm placing data in invalid locations in memory. I've looked through my linker setup, and I'm pretty sure it should be OK, but here it is anyway:



    /* Define the memory block start/length for the F2806x
       PAGE 0 will be used to organize program sections
       PAGE 1 will be used to organize data sections

       Notes:
             Memory blocks on F28069 are uniform (ie same
             physical memory) in both PAGE 0 and PAGE 1.
             That is the same memory region should not be
             defined for both PAGE 0 and PAGE 1.
             Doing so will result in corruption of program
             and/or data.

             Contiguous SARAM memory blocks can be combined
             if required to create a larger memory block.
    */

    MEMORY
    {
    PAGE 0 : /* Program sections */

       BEGIN       : origin = 0x000000, length = 0x000002   /* BEGIN is used for the "boot to SARAM" bootloader mode   */
       RAML0_6       : origin = 0x008000, length = 0x008000    /* on-chip RAM block L0 to L6 */

       RESET       : origin = 0x3FFFC0, length = 0x000002
       FPUTABLES   : origin = 0x3FD590, length = 0x0006A0    /* FPU Tables in Boot ROM */
       IQTABLES    : origin = 0x3FDC30, length = 0x000B50   /* IQ Math Tables in Boot ROM */
       IQTABLES2   : origin = 0x3FE780, length = 0x00008C   /* IQ Math Tables in Boot ROM */
       IQTABLES3   : origin = 0x3FE80C, length = 0x0000AA    /* IQ Math Tables in Boot ROM */

       BOOTROM     : origin = 0x3FF3B0, length = 0x000C10

    PAGE 1 : /* Data sections */

       RAMM0_1     : origin = 0x000050, length = 0x0007B0    /* on-chip RAM block M0 to M1 */
       BOOT_RSVD   : origin = 0x000002, length = 0x00004E   /* Part of M0, BOOT rom will use this for stack */
       RAML7_8     : origin = 0x010000, length = 0x003800   /* on-chip RAM block L7 to L8, excluding sections used by instaspin (see SPRUHI9A) */
       USB_RAM     : origin = 0x040000, length = 0x000800   /* USB RAM */
       CPU_CLA     : origin = 0x001500, length = 0x000080    /* CPU-to-CLA ram */
    }

    SECTIONS
    {
       /* Setup for "boot to SARAM" mode:
          The codestart section (found in DSP28_CodeStartBranch.asm)
          re-directs execution to the start of user code.  */
       codestart        : > BEGIN,      PAGE = 0
       ramfuncs          : > RAML0_6,
                             LOAD_START(_RamfuncsLoadStart),
                             LOAD_END(_RamfuncsLoadEnd),
                             RUN_START(_RamfuncsRunStart),
                             LOAD_SIZE(_RamfuncsLoadSize),
                             PAGE = 0
       .text            : > RAML0_6,    PAGE = 0
       .cinit           : > RAML0_6,    PAGE = 0
       .pinit           : > RAML0_6,    PAGE = 0
       .switch          : > RAML0_6,    PAGE = 0
       .reset           : > RESET,      PAGE = 0, TYPE = DSECT /* not used by C28x devices */

       .stack           : > RAMM0_1, fill=0xFFFF,  PAGE = 1    //NOTE; The stack pointer may not exceed 64k, IE must be <= 0x00FFFF
       .econst            : >> CPU_CLA | USB_RAM | RAML7_8,    PAGE = 1
       .ebss            : > RAML7_8,    PAGE = 1
       .esysmem         : > CPU_CLA,    PAGE = 1    //Defined to some arbitrary small space, not used as we don't use any heap-related functions

       IQmath           : > RAML0_6,      PAGE = 0
       IQmathTables     : > IQTABLES,      PAGE = 0, TYPE = NOLOAD
       
       /* Allocate FPU math areas: */
       FPUmathTables    : > FPUTABLES,  PAGE = 0, TYPE = NOLOAD
       
      /* Uncomment the section below if calling the IQNexp() or IQexp()
          functions from the IQMath.lib library in order to utilize the
          relevant IQ Math table in Boot ROM (This saves space and Boot ROM
          is 1 wait-state). If this section is not uncommented, IQmathTables2
          will be loaded into other memory (SARAM, Flash, etc.) and will take
          up space, but 0 wait-state is possible.
       */
       /*
       IQmathTables2    : > IQTABLES2, PAGE = 0, TYPE = NOLOAD
       {

                  IQmath.lib<IQNexpTable.obj> (IQmathTablesRam)

       }
       */
       /* Uncomment the section below if calling the IQNasin() or IQasin()
          functions from the IQMath.lib library in order to utilize the
          relevant IQ Math table in Boot ROM (This saves space and Boot ROM
          is 1 wait-state). If this section is not uncommented, IQmathTables2
          will be loaded into other memory (SARAM, Flash, etc.) and will take
          up space, but 0 wait-state is possible.
       */
       /*
       IQmathTables3    : > IQTABLES3, PAGE = 0, TYPE = NOLOAD
       {

                  IQmath.lib<IQNasinTable.obj> (IQmathTablesRam)

       }
       */

    }

    /*
    //===========================================================================
    // End of file.
    //===========================================================================
    */

  • Does the problem global variable have the correct value when main starts?  If so, then this is unlikely to be a compiler problem.  I would then turn this thread over to the C2000 experts.

    Thanks and regards,

    -George

  • I am able to reproduce the error with the following code (very slightly paraphrased):

    g_sys_opmode declared in a file globals.c

    in main.c

    void main(void){

    DINT;

    g_sys_opmode = 1;

    volatile int foo;
    if(g_sys_opmode){
        foo = 12;
    }else{
        foo = 43;
    }
    some_function();

    // The rest of the application

    ...

    }

    in some_file.c:

    void some_function(void){

    volatile int foo;
    if(g_sys_opmode){
        foo = 12;
    }else{
        foo = 43;
    }

    // The rest of this function's code

    ...

    }

    If I execute this modified program and debug I can see that foo in main is set to 12, while foo in some_function is set to 43. This mirrors the behavior that I see during runtime, which sometimes acts as if g_sys_opmode is zero and sometimes as if it were nonzero.

    //JL

  • Part Number: TMS320F28069

    Tool/software: TI C/C++ Compiler

    Hi,

    This is a crosspost from my original post (e2e.ti.com/.../749712), I'm posting here because my issue may be a C200-related problem rather than a compiler error.

    To summarize my problem: I am experiencing the unexpected behavior where reading a global variable in one context acts as if the variable is nonzero, while reading it in another context acts as if it were zero. By debugging the program I can verify that the global variable is (for instance) set to a nonzero value and that is is unchanged. I have enabled a hardware watchpoint which is triggered on a write to the memory location, without triggering the watchpoint. I have verified that the stack does not overflow. I am not using the heap at all.

    I am able to reproduce the error with the following code (very slightly paraphrased):

    in globals.c
    int g_sys_opmode = 0;

    in main.c

    void main(void){

    DINT;

    g_sys_opmode = 1;

    volatile int foo;
    if(g_sys_opmode){
        foo = 12;
    }else{
        foo = 43;
    }
    some_function();

    // The rest of the application

    ...

    }

    in some_file.c:

    void some_function(void){

    volatile int foo;
    if(g_sys_opmode){
        foo = 12;
    }else{
        foo = 43;
    }

    // The rest of this function's code

    ...

    }

    If I execute this modified program and debug I can see that foo in main is set to 12, while foo in some_function is set to 43. This mirrors the behavior that I see during runtime, which sometimes acts as if g_sys_opmode is zero and sometimes as if it were nonzero.

    If anyone has any idea as to how to continue with this issue I would appreciate it greatly.

    Cheers,
    Jonathan Lock

  • Hi Jonathan,

    I'm looking into it. Thanks for your patience. How are you observing the value of the variables? Through the expressions window?

    For future reference, when posting code onto the forum, please use the code formatting tool marked by the </> button. This tool can be located by clicking the "Insert Code, Atatch Files, and more" link on the bottom right hand side of the Reply menu. The Code Formatting Tool allows the code to be easily read and can help you get support faster on the forums.

    Thanks
    Whitney
  • Hi Whitney,

    Thanks for confirming that you're looking at this issue. I'm observing the global variable (g_sys_opmode) using the memory browser and the expression window. I've observed the dummy variables in the expressions window. Would there be any benefit to statically allocating them (rather than just having them allocated on the stack) and viewing them in the memory browser as well? For what it's worth, I also notice this behavior through the actions the program takes as a whole, so it doesn't seem to me to be simply a matter of some compiler optimization that does something untoward with my dummy 'foo' variables.

    Ah, right, thanks, I'll keep that in mind.

    Cheers,

    Jonathan

  • I can't reproduce this just based on the couple lines of code you shared. Do you have a project that shows this behavior that you'd be willing to zip up an attach to a post?

    Do you have optimization turned on in the compiler? What level? Does making g_sys_opmode volatile make a difference?

    Whitney
  • Hi Whitney,

    That's definitely what I had expected. I do have several optimization flags turned on, primarily in an attempt to reduce the size of the final program. I can send my entire project, is there some way it could be done confidentially?

    The net compiler flags are:

    -v28 -ml -mt -O4 --opt_for_speed=0 --fp_reassoc=on --include_path="/home/hydra/ti/ti-cgt-c2000_18.1.4.LTS/include" --include_path="<SOME PATH>" --define=FAST_ROM_V1p6 -g --gcc --printf_support=minimal --diag_warning=225 --diag_wrap=off --display_error_number --auto_inline=10 -ms --fp_mode=relaxed

    and the net linker flags are:

    -v28 -ml -mt -O4 --opt_for_speed=0 --fp_reassoc=on --define=FAST_ROM_V1p6 -g --gcc --printf_support=minimal --diag_warning=225 --diag_wrap=off --display_error_number --auto_inline=10 -ms -z -m"drive.map" --stack_size=0x7B0 --warn_sections -i"/home/hydra/ti/ti-cgt-c2000_18.1.4.LTS/lib" -i"/home/hydra/ti/ti-cgt-c2000_18.1.4.LTS/include" -i"<SOME PATH>" --reread_libs --diag_wrap=off --display_error_number --xml_link_info="drive_linkInfo.xml" --rom_model

    I'll test making g_sys_opmode volatile soon and reply with the results.

    Thanks for taking your time to look at this.

    //Jonathan

  • Hi,

    This is just a friendly reminder to state that I haven't been able to resolve this issue and that I'd be happy to send my entire project, so long as it can be done reasonably securely (i.e. directly sending it to you/others at TI, and not making it publicly available).

    This issue really completely breaks my application, so I'd very much appreciate some help with this problem. If there's anything else I can do on my end just let me know.

    Cheers,

    Jonathan

  • Hi Jonathan,

    Did you have a chance to see if making g_sys_opmode volatile had any effect?

    Also, check your messages here on E2E. I've sent a request that should allow you to send me your code privately. Or you could try to create a test case that reproduces this issue but removes anything proprietary--this is preferred if possible.

    Whitney

  • Hi,

    I've set g_sys_opmode volatile, but still have the same behavior. I've also made the temporary variables static, and with the memory browser verified the values they are set to: I still have the same behavior. I've sent you a private message with the code base and specific details on how the execution in specific lines displays the indicated behavior.

    //Jonathan

    Edit: also, sorry for not noticing/replying earlier, I didn't get any notification from e2e about your request.

  • I was able to reproduce the issue. However, volatile g_sys_opmode DOES fix the issue for me. Can you make sure you've done a full clean rebuild after switching it and try again?

    I can see in the disassembly window when I look at the code for "some_function()" that when g_sys_opmode is not volatile, it's not being checked and foo is unconditionally being assigned 43. The value of g_sys_opmode doesn't appear to be changed to 0 at any point--it's just not being used. When it IS volatile I can see where it is being read into the accumulator and foo is being set accordingly as expected.

    Why this is happening I'm not certain. My guess is that some_file.c doesn't see where g_sys_opmode is assigned to anything other than its default value so it assumes your if-statement will always resolve to the else case. can probably give a more precise answer.

    Whitney
  • Okay, George and I did some debug this afternoon and found another possible solution you can try instead of making g_sys_opmode volatile. We found that if we went into the properties for "some_file.c" and set its optimization level to -o3 (just for the file with the weird behavior--the rest of program can stay -o4), the generated assembly was correct and g_sys_opmode was evaluated correctly.

    We'll do some investigating to find out if this is a bug with -o4.

    Whitney

  • I re-tested the volatile/non-volatile behavior and I now get the same behavior you got: making the variable volatile does resolve the issue.

    It's been a while since I've looked at the C reference manual, but IIRC this behavior must be a compiler/linker/etc bug, right, i.e. the behavior at O4 is not correct? Though the compilation unit in some_file.c never sees g_sys_opmode set to anything other than 0 it can not make that assumption, as some other section might have changed g_sys_opmode earlier. (Of course, during execution in some_file.c the compiler can assume no other source can modify g_sys_opmode).

    I also see the correct behavior (i.e. the same behavior you see) when setting some_file.c to have an optimization level of O3 rather than the project-wide settings of O4. Unfortunately, the size of the project is too large when the entire project is compiled with O4 =(

    One final note: I've been using this code base for quite some time, and this behavior is new. Is it possible this behavior is some type of toolchain regression? Looking at previous versions it looks like I've used version 6.2.11, seemingly without issues (I'm only speculating at this point, I haven't verified this hypothesis yet).

    Thanks for your help so far, could you keep me updated on what happens with this issue?

    Cheers,

    Jonathan

  • After further investigation, it appears this is a bug in the compiler. It seems to be the case the compiler, under --opt_level=4, incorrectly concludes g_sys_opmode contains 0, when it actually contains 1.  Based on that incorrect conclusion, it then optimizes away an if/else statement.  I filed the entry CODEGEN-5674 in the SDOWP system to have this investigated.  You are welcome to follow it with the SDOWP link below in my signature.

    Here is a workaround.  Start with the source file which contains the if/else statement that is incorrectly optimized away.  Use the File Specific Options feature of CCS to build that file with --opt_level=3 instead.  The rest of the project can still build with --opt_level=4.  It is not necessary to use volatile on g_sys_opmode.

    Thank you for your patience.  And especially thank you for sending in the test case.

    Thanks and regards,

    -George

  • Hi George,

    Happy to hear you could reproduce and isolate the issue. Unfortunately, for my project there are a lot of files that depend on the correct behavior of globals, and compiling them all with -O3 gives a binary that's too large. Do you happen to have a rough idea of how long you estimate it will take to resolve the issue? Weeks, months?

    Many thanks for your and Whitney's help,
    Jonathan
  • I understand you are anxious for a fix.  However, for the moment, I want to focus on workarounds that can get you usefully working again.

    Is g_sys_opmode the only global variable that experiences this problem?  If so, is temporarily making it volatile a practical workaround?

    I tried the test case you sent with compiler version 16.9.9.LTS.  I did not observe the problem.  Is downgrading to this version a practical workaround?

    Thanks and regards,

    -George

  • Hi,

    Well, g_sys_opmode is the only variable with this behavior that I've noticed so far, but my project uses several variables and it would be hard to verify the correctness entirely.

    That said, using an old compiler/toolchain should be a completely OK workaround. Do you know if there are any significant differences between the current version and 16.9.9 LTS?

    //JL

  • Jonathan Lock said:
    Do you know if there are any significant differences between the current version and 16.9.9 LTS?

    To see what features are introduced in compiler version 18.1.x.LTS, and are not available in version 16.9.x.LTS, please see the article MCU Compiler v17.  

    Thanks and regards,

    -George

  • Hi, thanks for the reference. It looks like 16.9.x will be completely OK for my application.

    Thanks for all your time and effort!

    //JL