We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
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
1. Define 'slot' 2. Overlay is performed in a hierarchial fashion, so a tree view is the most logical way to look at it. Do you want to have space that just isn't overlayed, or to save a certain number of bytes at a specified point in the overlay tree?
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
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' ----
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
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); }
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
SEGMENT DATA_GROUP +--> CALLED SEGMENT START LENGTH ---------------------------------------------- ?PR?MAIN?MAIN 0008H 0004H +--> ?PR?FUNC_1?MAIN
SEGMENT DATA_GROUP +--> CALLED SEGMENT START LENGTH ---------------------------------------------- ?PR?FUNC_1?MAIN 000CH 0010H +--> ?PR?FUNC_2?MAIN
SEGMENT DATA_GROUP +--> CALLED SEGMENT START LENGTH ---------------------------------------------- ?PR?FUNC_2?MAIN 001CH 0014H
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