This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

where is DATA/IDATA used

For the '51 compilers/linkers evaluate which data/idata slots can be overlaid. I have to save 3 slots to make room for a new feature. Is there a way to make the linker show the tree in a less cryptic way than by module (i.e. by function) ?

Have fun,

Erik

Parents
  • I want to find the "priority" of places in the source (it does no good to reduce where 27 functions share the same space) to look to reduce (I)DATA use by 3 bytes to get room for an additional register bank. E.g. in a block of (I)DATA there may be 43 functions sharing the space ("SLOTS") to reduce that block you must attack the function using the most.

    Erik

Reply
  • I want to find the "priority" of places in the source (it does no good to reduce where 27 functions share the same space) to look to reduce (I)DATA use by 3 bytes to get room for an additional register bank. E.g. in a block of (I)DATA there may be 43 functions sharing the space ("SLOTS") to reduce that block you must attack the function using the most.

    Erik

Children
  • You could try loading the memory map from the linker listing file into something like Excel, and then displaying it graphically - this would show you clearly how much memory each module is using, and how they are overlaid.

    It's not exactly trivial (and not helped by the fact that Keil's listings are not entirely regular), but worth the effort.

  • In the past we have gone to the trouble of formating the linker output and dropping it into Exel to give a graphical view of how the linker is overlaying memory. This has proven most relealing in the past, but we don't do it routinely because it is rather fiddly and time consuming to format the data.

    Straightening out the irregularities in the Overlay Map would be a help.

    A new linker feature that provided a semi-graphical representation of memory use or put out a file suitable for dropping straight into a utility such as Execl would be very useful.

  • If you need to save memory then there are a number of things you can do.

    The overlay map is a good place to start, this may help you identify the "worst" cases. In particular, look for places where you would expect the compiler to use registers for local variables - but has in fact allocated memory for them.

    Local registers are very good for minimising memory use and maximising speed. However, the compiler does not always do what you might expect. Turn on the Assembly Code options for your listings, compile and look in the .lst files. Look for indications that variables have been assigned to registers. e.g.:

    ;---- Variable 'character' assigned to Register 'R5' ----
    ;---- Variable 'port' assigned to Register 'R7' ----
    
    Note that the compiler assumes that a function call may use any of the registers. Presumably, the global register optimisation feature allows the compiler to know which registers are used by a called function. However, this does mean that 'leaf' functions - functions that do not call other functions - are much more likely to make good use of registers that 'branch' functions. You may be able to save memory by rearranging your function calls to make optimum use of register variables.

    The order in which parameters apear in a function declaration can make a significant difference to whether that function places the parameter in registers of in memory.

    If you have variables that do not need to use exactly 8, 16 or 32 bits then you can opt to define a structure that uses bit fields to cram several variables into the minimum possible amount of space.

  • Graham provides a marvelous discussion of how to optimize your program.

    I'll add a few more bits to what he said.

    The compiler uses registers for function arguments. This is always faster because the caller can store the argument values into registers rather than into memory.

    The C51 compiler passes at most 3 arguments in registers according to some rules that are defined in the Compiler User's Guide in Chapter 3. The register allocation scheme is fixed (there is nothing random about it) and is well-defined. Since there are only 8 1-byte registers, at most 8 bytes of arguments may be passed. The order of the argument is of paramount importance in register allocation, and, simply changing the order of the arguments (that have different types) may cause the compiler to place more or fewer arguments in registers. This is an exact science, so refer to the manual for details.

    As far as Global Register Optimization, the comment,

    this does mean that 'leaf' functions - functions that do not call other functions - are much more likely to make good use of registers that 'branch' functions

    is not exactly true. The assumption is that all functions destroy all register contents. So, a calling function must save and restore the contents of any registers it needs after a function call. Fortunately, in C, this does not cause a great deal of problems.

    Global register optimization maps out which registers are used in each function. This allows a calling function to avoid saving and restoring some registers. It doesn't do a lot in small programs, but in larger programs that have a lot of functions, this optimization helps.

    Jon

  • First, thanks for the info/advice.
    Second, I was looking for a function - short of perusing assembler output, which I am very capable of - to tell me not what module, but which function the maximum "load" happened in. One obstacle I'm up against is that I am trying to fix something in inherited code which is not organized in a way I would do it.

    Thanks again, and if you have a method of listing (I)DATA use by function, not mudule, please let me know.

    Erik

  • In the uVision2 Browser, could you check just DATA or IDTATA in 'Memory Spaces', and just 'Data' for 'Filter On'

    You can then click on the column heading (as in WinExplorer) to sort by, eg, 'Uses'.
    This would show you which variables in (I)DATA are used the most/least.

    But I can't see a simple, direct way to get to what you want. :-(
    Maybe a couple of passes with GREP or similar through the listing & map files, with all the symbol & cross-ref options enabled?

  • Below is an excerpt from a .M51 linker map file. Teh segment names listed in the left column are composed of three fields which indicate the segment time (PR for code in this case), function name, and module name. The column to the right indicates how much RAM that each segment (function) is using. Is this not what you're looking for?

    OVERLAY MAP OF MODULE:   test (MAIN)
    
    
    SEGMENT                          DATA_GROUP 
      +--> CALLED SEGMENT          START    LENGTH
    ----------------------------------------------
    ?C_C51STARTUP                  -----    -----
      +--> ?PR?MAIN?MAIN
      +--> ?C_INITSEG
    
    ?PR?MAIN?MAIN                  -----    -----
      +--> ?PR?FOO?MAIN
    
    ?PR?FOO?MAIN                   0008H    0004H
      +--> ?PR?BAR?MAIN
    

  • David, what you show is by MODULE (source file) not by single function. I want to know by function.
    Andy, I do not use uVision, I use Codewright as an editor and run the compiler from huge batchfiles. I have not seen a means of "batchfiles" in uVision.

    Thanx,

    Erik

  • what you show is by MODULE (source file) not by single function. I want to know by function

    No,

    What David shows IS by function. It is NOT shown by module.

    OVERLAY MAP OF MODULE: test (MAIN)

    SEGMENT                          DATA_GROUP 
      +--> CALLED SEGMENT          START    LENGTH
    ----------------------------------------------
    ?C_C51STARTUP                  -----    -----
      +--> ?PR?MAIN?MAIN
      +--> ?C_INITSEG
    
    ?PR?MAIN?MAIN                  -----    -----
      +--> ?PR?FOO?MAIN
    
    ?PR?FOO?MAIN                   0008H    0004H
      +--> ?PR?BAR?MAIN

    The FOO function uses 4 bytes of DATA memory.

    Maybe you are using the word function to mean something else?

    Jon

  • Ok, sorry BUT:

    (edited for space)

    ?PR?_SEN?UPC 0036H 0004H
    +--> ?PR?WAI?UPX
    +--> ?PR?_WRI?UPXA
    +--> ?PR?FIN?UPX
    +--> ?PR?DEC?UPCD
    +--> ?PR?_PAU?FQ1

    SEN uses 2 bytes of DATA, which of the rest is using 4 ? That, Horatio, is the question.

  • What are the arguments and local variables of this function and what are their data types?

    Jon

  • We are way past the subject: "WHICH of the functions sharing this (I)DATA space is the one that uses the full count". Of course I know what the local variables are, but when 47 functions share the same space it is quite tedious to look at all 47 to find out which one to analyze to see if you can find a workaround to reduce (I)DATA use.
    The abbreviated list in my posting 2 levels up represent a page of functions sharing the same (I)DATA space not just 4

    Erik

  • We are way past the subject: "WHICH of the functions sharing this (I)DATA space is the one that uses the full count". Of course I know what the local variables are, but when 47 functions share the same space it is quite tedious to look at all 47 to find out which one to analyze to see if you can find a workaround to reduce (I)DATA use.
    The abbreviated list in my posting 2 levels up represent a page of functions sharing the same (I)DATA space not just 4


    I you misunderstand the information that is presented in the call tree. The LENGTH is NOT the length of the DATA space used by the functions that are called. It is the size of the function indicated. Perhaps an example will help clarify this:

    #pragma NOREGPARMS  // Don't put arguments in registers
    
    long func_2 (long x)
    {
    long a, b, c, d;  /*** 16 + 4 bytes = 20 bytes ***/
    
    a = 1;
    b = 2;
    c = 3;
    d = 4;
    
    return (x + a + b + c + d);
    }
    
    long func_1 (long x)
    {
    long a, b, c;  /*** 12 + 4 bytes = 16 bytes ***/
    
    a = 11;
    b = 12;
    c = 13;
    
    return (x + a + b + c + func_2 (105));
    }
    
    void main (void)
    {
    long a;  /*** 4 bytes ***/
    
    a = func_1 (100);
    
    while (1);
    }

    Links with the following call tree.

    SEGMENT                          DATA_GROUP 
      +--> CALLED SEGMENT          START    LENGTH
    ----------------------------------------------
    ?C_C51STARTUP                  -----    -----
      +--> ?PR?MAIN?MAIN
    
    ?PR?MAIN?MAIN                  0008H    0004H
      +--> ?PR?FUNC_1?MAIN
    
    ?PR?FUNC_1?MAIN                000CH    0010H
      +--> ?PR?FUNC_2?MAIN
    
    ?PR?FUNC_2?MAIN                001CH    0014H

    In my example program, the main function only has 1 local variable (a long) that takes 4 bytes. The call tree listing is:

    SEGMENT                          DATA_GROUP 
      +--> CALLED SEGMENT          START    LENGTH
    ----------------------------------------------
    ?PR?MAIN?MAIN                  0008H    0004H
      +--> ?PR?FUNC_1?MAIN

    As you can see, the length of the DATA group for the MAIN function is 4 bytes. And, the main function calls func_1.

    func_1 only has 1 argument and 3 local variables (all long). That takes 4*4 or 16 bytes. The call tree listing is:

    SEGMENT                          DATA_GROUP 
      +--> CALLED SEGMENT          START    LENGTH
    ----------------------------------------------
    ?PR?FUNC_1?MAIN                000CH    0010H
      +--> ?PR?FUNC_2?MAIN

    The length of the DATA group for FUNC_1 is 10h bytes which is 16. And, func_1 calls func_2.

    func_2 only has 1 argument and 3 local variables (all long). That takes 5*4 or 20 bytes. The call tree listing is:

    SEGMENT                          DATA_GROUP 
      +--> CALLED SEGMENT          START    LENGTH
    ----------------------------------------------
    ?PR?FUNC_2?MAIN                001CH    0014H

    The length of the DATA group for FUNC_2 is 14h bytes which is 20. And, func_2 doesn't call any other functions.

    <hr>

    If I were looking for places to reduce the DATA space that's used, I'd start looking at which function used the most. I'd look in the call tree.

    SEGMENT                          DATA_GROUP 
      +--> CALLED SEGMENT          START    LENGTH
    ----------------------------------------------
    ?C_C51STARTUP                  -----    -----
    ?PR?MAIN?MAIN                  0008H    0004H
    ?PR?FUNC_1?MAIN                000CH    0010H
    ?PR?FUNC_2?MAIN                001CH    0014H

    And, immediately, I'd notice that FUNC_2 uses 20 bytes of DATA space. So, that's where I'd start.

    Jon


  • If only I could get the list you show. My results (with 6.01) is a long list of functions with ONLY the first having numbers associated with it. My perception is that these are the functions using the same space to store variables. HOWEVER, these functions use a varied number of variables and the length shown is for the one(s) using the most. What I show in the example some postings back is what I get.

  • OK - E-mail me the MAP file (jonw@keil.com).

    I'll take a look at it. However, I don't recall any problems with the 6.01 version of the tools generating this tree incorrectly.

    Jon