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/TMS320F28075: C2000 Linker: Linker generated symbols & Section placement - How to get the code length into the code?

Part Number: TMS320F28075
Other Parts Discussed in Thread: CONTROLSUITE

Tool/software: TI C/C++ Compiler

Hi,

I wonder how the length of the code generated by the linker, i.e. the length of all sections that go to flash, can be "compiled" (i.e. computed by the linker) into the code generated. I tried several approaches:

Approach #1:

Linker Command File:

MEMORY {

...

    FLASHE00    : origin = 0x088000,
                  length = FIRMHEAD_SECTSIZE            /* firmware header  */
    FLASHE2J    : origin = 0x088000 + FIRMHEAD_SECTSIZE,
                  length = 0x030000 - FIRMHEAD_SECTSIZE /* firmware */

}

SECTIONS
{
    /* firmware sections in on-chip flash sectors */
    .firmware_header: LOAD_SIZE(_firmhead_size), fill = 0xffff
        {
        _firmware_start = .;
        firmware_header(.firmware_header)
        }
        > FLASHE00  PAGE = 0, ALIGN(4)

    /* group all other flash sections */
    .firmware GROUP LOAD_SIZE(_firmware_size) {
    codestart       : //PAGE = 0, ALIGN(4)
    .text           : //PAGE = 0, ALIGN(4)
    ramfuncs        : // LOAD = FLASHE2J,
                      RUN = RAMLS0 | RAMLS1 | RAMLS2 |RAMLS3,
                      LOAD_START(_RamfuncsLoadStart),
                      LOAD_SIZE(_RamfuncsLoadSize),
                      LOAD_END(_RamfuncsLoadEnd),
                      RUN_START(_RamfuncsRunStart),
                      RUN_SIZE(_RamfuncsRunSize),
                      RUN_END(_RamfuncsRunEnd)
                      //PAGE = 0, ALIGN(4)
    /* initalized sections go in flash */
    .cinit          : //PAGE = 0, ALIGN(4)
    .pinit          : //PAGE = 0, ALIGN(4)
    .econst         : //PAGE = 0, ALIGN(4)
    .switch         : //PAGE = 0, ALIGN(4)

    /* end of firmware sections in on-chip flash */
    .firmware_end   : {
        _firmware_end = .;
    }
    } > FLASHE2J    PAGE = 0, ALIGN(4),

}

C-file:

/* place firmware_header in section .firmware_header */
#pragma DATA_SECTION ( firmware_header , ".firmware_header")

/* symbols defined by the linker command file */
extern  uint32_t    firmware_start, firmware_size;
extern  uint32_t    firmware_end;

struct firmware_header firmware_header = {

(uint32_t)&firmware_size + FIRMHEAD_SECTSIZE,

...

};

This compiles and links successfully (with the right length compiled in), but a warning

warning #10082-D: placement ignored
   for "ramfuncs":  object is placed as part of "GROUP_1"

is generated and the ramfuncs RUN section is placed to flash (wrong!).

Approach #2:

Without a GROUP in the command file, I tried to put a section .firmware_end at the end of all (see above), and to use the difference of the symbols as length.

This causes a compile error

    //(uint32_t)(&firmware_end - &firmware_start), // error #28: expression must have a constant value

and it is not sure that .firmware_end is really placed at the end, since (SPRU513L):

8.5.5 The SECTIONS Directive

(Note that the memory placement order is not simply the sequence in which
sections occur in the SECTIONS directive.)

Other trials give other errors:

    //((uint32_t)&firmware_size + FIRMHEAD_SECTSIZE) * 2, // error #28: expression must have a constant value
    //(uint32_t)(&firmware_size + &firmhead_size), // error #31: expression must have integral type

Any hint

- How to get the length (sum of the lengths of several sections) compiled in?

- How to control the order of the sections (.firmhead as first, .firmware_end as last)? (HIGH is not the solution.)

Thanks,

Frank

  • Later edit ... The suggestion given in this post ultimately failed to solve the problem.  Read the whole thread before using it.

    fmdhr said:
    I wonder how the length of the code generated by the linker, i.e. the length of all sections that go to flash, can be "compiled" (i.e. computed by the linker) into the code generated.

    This is the first time I am recommending this particular technique.  While I am very confident it will solve your problem, I have to admit I may have overlooked something.

    One central concept to this solution .. It has no knowledge of where the sections are placed in memory.  It only adds up the sizes of the sections you indicate.  

    There is a list of caveats and limitations below.  Please do not ignore them.

    In your linker command file, capture the size of all the sections of interest ...

    SECTIONS
    {
       .text   > FLASH, SIZE(text_size)
       .cinit  > FLASH, SIZE(cinit_size)
       .pinit  > FLASH, SIZE(pinit_size)
       /* ... and so on */
    }

    In one statement after the SECTIONS directive, add up all those sizes and assign to another symbol ...

    _total_size = text_size + cinit_size + pinit_size;

    In your C or C++ code, you can refer to the the symbol total_size (no leading underscore).  For details on doing that, please see the section titled Using Linker Symbols in C/C++ Applications in the C2000 assembly tools manual.

    CAVEATS AND LIMITATIONS

    This does not work when section splitting (signaled by the operator >>) is used.  For instance ...

       .text >> FLASH1 | FLASH2, SIZE(text_size)  // SILENT FAILURE!!!

    The linker does not compute the section size in this case.  It silently assigns the value 0xffffffff to the symbol text_size, and adds that in to _total_size.

    If there are any gaps between sections, the size of those gaps is ignored.  For this reason, this technique is not the same as computing size = end_address - start_address.

    The C2000 tools use the older COFF ABI, and not the newer EABI.  Under COFF ABI, global symbols in C or C++ are written with a leading underscore in assembly.  So, total_size in C becomes _total_size in assembly.  In this case, the direction is the reverse.  _total_size is created in the linker command file, and used as total_size in C.  If you use this technique under EABI, either with upcoming C2000 tools that support EABI, or with other TI toolsets that support EABI, write the symbol as simply total_size throughout.  Forget that leading underscore.

    Thanks and regards,

    -George

  • Hi George,

    thank you for your profund reply. I did as you recommended and I get the code size computed by the linker and compiled in. Great.

    But, I am very astonished - it's wrong. Also the .map file is wrong. In my example I get a size of 0xecf + 0x20 = 0xeef (end address 0x88eef), but there is code flashed at 0x88ef0. See the snippets from the .map file and the code dump below.

    I also can reproduce the strange results with a standard example from the controlSUITE, also see below.

    It makes me wonder...

    Regards,

    Frank

    *.cmd

        _firmware_size = FIRMHEAD_SECTSIZE + codestart_size + text_size +
        ramfuncs_size + cinit_size + pinit_size + econst_size + switch_size;

    *.map
             name            origin    length      used     unused   attr    fill
    ----------------------  --------  ---------  --------  --------  ----  --------
      FLASHE00              00088000   00000020  00000014  0000000c  RWIX
      FLASHE2J              00088020   0002ffe0  00000ecf  0002f111  RWIX

    ...

    .cinit     0    00088ed4    0000001a     
                      00088ed4    0000000e     rts2800_fpu32.lib : exit.obj (.cinit)
                      00088ee2    00000005                       : _lock.obj (.cinit:__lock)
                      00088ee7    00000005                       : _lock.obj (.cinit:__unlock)
                      00088eec    00000002     --HOLE-- [fill = 0]

    codestart
    *          0    00088ef0    00000002     
                      00088ef0    00000002     F2807x_CodeStartBranch.obj (codestart)

    ...
    0     00088ccc  ___etext__                            
    0     00088ccc  etext                                 
    0     00088e8c  _big_data                             
    0     00088e9c  _RamfuncsLoadStart                    
    0     00088ed3  _RamfuncsLoadEnd                      
    0     00088ed4  ___cinit__                            
    0     00088ed4  cinit                                 
    0     00088ef0  code_start                            
    ...
    abs   00000000  pinit_size                            
    abs   00000000  switch_size                           
    abs   00000002  codestart_size                        
    abs   0000001a  cinit_size                            
    abs   00000037  _RamfuncsLoadSize                     
    abs   00000037  _RamfuncsRunSize                      
    abs   00000037  ramfuncs_size                         
    abs   00000100  __STACK_SIZE                          
    abs   000001d0  econst_size                           
    abs   00000cac  text_size                             
    abs   00000eef  _firmware_size                        


    dump of flashed code:

    0x00088EE0    0000    0000    FFFE    A81E    0000    8CC0    0008    FFFE    A820    0000    8CC0    0008    0000    0000    FFFF    FFFF
    0x00088EF0    0048    8CC1    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF
    0x00088F00    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF


    C:\ti\controlSUITE\device_support\F2807x\v190\F2807x_examples_Cpu1\cpu_timers\cpu01

    CPU1_FLASH configuration .map and memory dump:

      FLASHA                00080002   00001ffe  00000000  00001ffe  RWIX
      FLASHB                00082000   00002000  00000c94  0000136c  RWIX

                      00082c8b    00000008     F2807x_CodeStartBranch.obj (.text)
                      00082c93    00000002     rts2800_fpu32.lib : pre_init.obj (.text)
                      00082c95    00000001                       : startup.obj (.text)

    0     00082c8a  __nop                                 
    0     00082c93  __system_pre_init                     
    0     00082c95  __system_post_cinit                   

    0x00082C70    FFFF    0FAB    6004    BE00    D400    6F09    28AB    FFFF    28AA    FFFF    A9A4    88C4    0902    8AA9    92A6    7648
    0x00082C80    2ABF    0006    761F    02A0    A820    0006    761F    02A0    A81E    0006    0006    561F    7622    B9C0    2829    0068
    0x00082C90    761A    0048    2B20    9A01    0006    0006    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF
    0x00082CA0    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF    FFFF


  • It seems you are saying the SIZE operator gets the wrong answer.  I'm not clear on how you know that.  In the "dump of flashed code", should I presume the address of the last value that is not FFFF is where the section ends?  How do you know the flash utility (or whatever programs the flash) did it right?

    Thanks and regards,

    -George

  • Hi George,

    I think I found the reason for the wrong result: SIZE is correct, sum of sizes is correct, .map is correct, flash utility is correct. However, the holes resulting from the ALIGN operator are not taken into account (should they?). I replaced

        .text           : > FLASHE2J        PAGE = 0, ALIGN(4), SIZE(text_size)

    with

        .text           : > FLASHE2J        PAGE = 0, ALIGN(1), SIZE(text_size)

    and get the right result. However, ALIGN(4) is, I think, good for faster code fechting from flash (I remember to have read something some time ago ...), so it better should be retained.

    May be I should have asked my question more precisely: How to get the address of the last word used (and the difference firmware_end - firmware_start!)? This leads back to the 2nd question in my first posting (section ordering, see above): How to get a section placed at the end?

    (Background: The final goal is to generate from the .out (via the hex tool) a C source of the firmware code which is linked to the firmware of another processor which then can do the firmware update of the C2000.)

    Thanks,

    Frank

  • My first post talks about how that solution ignores gaps between sections. But I can see how that is easily overlooked.  I recommend that first solution be abandoned.

    Please try another suggestion.  This is an adaptation of what appears in this post.

    Create a #define symbol to establish the base address of the flash memory range.  Use that name when defining the flash memory range.  Something like the following ...

    #define FLASH_BASE 0x3000 /* change to correct address */
    ...
    MEMORY 
    {
       FLASH : origin = FLASH_BASE, length = 0x2000
       ...
    }

    Pick one output section that goes in the flash memory range.  Just for now I will use the .cinit section.  Change it so it looks similar to this ...

       .cinit > FLASH(HIGH), END(end_of_flash)

    Then create the symbol you refer to in the C code ...

    _flash_size = end_of_flash - FLASH_BASE;

    Handle this symbol just like I recommend the symbol _total_size be handled in my first post.

    I'm not aware of any reason to prefer any output section in flash be last.  But if there is, that is the one to use for this technique.

    Be sure you use HIGH exactly one time in the flash memory range.

    Thanks and regards,

    -George

  • fmdhr said:
    (Background: The final goal is to generate from the .out (via the hex tool) a C source of the firmware code which is linked to the firmware of another processor which then can do the firmware update of the C2000.)

    The script bootimage from the cg_xml package does something very similar.

    Thanks and regards,

    -George

  • Hi George,

    George Mock said:
    My first post talks about how that solution ignores gaps between sections. But I can see how that is easily overlooked.

    I am proud to serve as another negative example for the well known fact that some minutes of careful reading can save dozens of hours of research in the laboratory...

    Unfortunately, your suggestion

    George Mock said:
       .cinit > FLASH(HIGH), END(end_of_flash)

    is also not what I want, since this computes the size of the memory section, not the size of my code (I mentioned in my first post.).

    Thanks also for the link regarding in your other post, I'll forward it to my colleague who does this part of work, may be he can use it. Meanwhile we have a solution that takes the Hex-Tool output and we search from the end to the first non-0xfff word to detect the size. Not very elegant and faulty, if the code ends with 0xffff and it goes not to flash that initially is 0xffff. So I asked for another, better & easy solution...

    Thanks & regards

    Frank