|
|
View previous topic :: View next topic |
Author |
Message |
kender
Joined: 09 Aug 2004 Posts: 768 Location: Silicon Valley
|
looking for tips for reducing ROM footprint |
Posted: Wed Aug 25, 2010 5:32 pm |
|
|
Colleagues,
My project was evolving for years, and now I’m out of program memory. I’m using 18F4620, which has the most Flash in its line. Are there any CCS-specific tricks that can free-up some code memory?
I’m sure, this subject was discussed previously on the forum. But I couldn’t find those threads.
Any suggestion, insight, reference is appreciated!
- Nick _________________ Read the label, before opening a can of worms. |
|
|
collink
Joined: 08 Jan 2010 Posts: 137 Location: Michigan
|
Re: looking for tips for reducing ROM footprint |
Posted: Wed Aug 25, 2010 5:49 pm |
|
|
kender wrote: | Colleagues,
My project was evolving for years, and now I’m out of program memory. I’m using 18F4620, which has the most Flash in its line. Are there any CCS-specific tricks that can free-up some code memory?
I’m sure, this subject was discussed previously on the forum. But I couldn’t find those threads.
Any suggestion, insight, reference is appreciated!
- Nick |
Sure, what sort of things does your program do?
Something I do a lot of is factoring code so that as much code as possible is shared by as many routines as possible. That is, if two routines both do almost the same thing then factor the common stuff to another function and call it from both. This can save a lot of space in the long run. For instance, if you have a lot of menus in your code then make a function which draws and operates menus given a const string and use that instead of hand coding each and every menu. This way you have only one place that has to have code for menu drawing, selection, movement, etc. However, it also eats up your stack levels so you might have to watch how deeply you nest functions.
Also, try to make sure that #separate is in front of any functions that you suspect the compiler might otherwise inline in the code. This is especially important if the function is reasonably small and called in many different places. It adds up. I think that the compiler does a pretty good job of optimizing for space at opt level 9 though so this might not do a whole lot.
Try optimizing at the unsupported optimization levels 10 and 11. It's scary but sometimes it will actually produce code that is amazingly small and still works. You can try enabling the optimization at the function level and pick and choose functions to super optimize. Both optimization levels have a tendency to ruin your program so be careful. When I've tried opt level 11 I've seen code usage on a big project go from 98% down to like 85%. Granted the program puked after that and wouldn't run... I have actually had a program work at opt level 10.
If you have a lot of strings it might become advantageous to compress them in some way. Maybe huffman coding, maybe packing them to 6 bit chars (thus saving 25%) This is only worth it if you have a lot of strings. Say, if you have 30k worth of strings then saving 25% is a 7.5k savings. Keep in mind, though, that you'll have to code up a decompressing routine while will eat up some of the savings. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Aug 25, 2010 5:55 pm |
|
|
Look at the ASM code in the .LST file. Look for routines that use a lot of
ROM compared to what they do. The functions may have features that
you don't use and yet it's in the source code, so it gets compiled and it
wastes ROM. Example: Several different radixes may be supported
for a string to integer conversion routine (such as hex, decimal, octal, etc)
but you only need decimal. You could make your own version of the
routine and cut out all the unnecessary source code.
You may be using built-in functions that are replicated with inline code for
each call, such as write_eeprom(). This is very wasteful, but you can
fix this compiler behavior by adding a "wrapper" function to write to
an eeprom location, and then call the wrapper instead. Look for ASM
routines that are replicated over and over again in the .LST file.
You may be doing ROM tables inefficiently. For example, #rom has the
option of using an 'int8' qualifier. This allows you to pack 2-bytes per
ROM word, instead of just one.
Floating point math eats ROM. Try to avoid using functions in math.h.
Find some clever way to do use integer math instead. Quite often there
is one.
I think the most important thing is to review the .LST file and see where
the problems are. If you're using math.h routines, it will be obvious that
it uses 100's and 100's of ROM words, and something must be done about it. |
|
|
andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
|
Posted: Wed Aug 25, 2010 9:28 pm |
|
|
I've recently been tinkering with the CCS MMC/SD/FAT library code, and found that I could shave 1K off the code. What they are doing is passing structure pointers into functions, then dereferencing using the -> operator. That takes a relatively large amount of code. The fix is to copy the structure to a local copy at the start of the function, use the . operator instead, and copy the local copy back at the end of the function. One memorable function started out at 422 words and ended up 192 words. Watch out for the extra RAM the local copies take up, though. _________________ Andrew |
|
|
kender
Joined: 09 Aug 2004 Posts: 768 Location: Silicon Valley
|
|
Posted: Thu Aug 26, 2010 2:03 pm |
|
|
Gentlemen, thank you for the suggestions. I will try your approaches, and I will keep you posted.
I've noticed that seemingly simple* writing to structures takes a lot of instructions. I wonder if there's a way to improve that. For example:
Code: | .................... g_sChannels[iCh].sPID.iMax = 4095; // global struct
1D52: MOVF x86,W
1D54: MULLW 42
1D56: MOVF FF3,W
1D58: CLRF x88
1D5A: MOVWF x87
1D5C: MOVLW 0C
1D5E: ADDWF x87,W
1D60: MOVWF x89
1D62: MOVLW 00
1D64: ADDWFC x88,W
1D66: MOVWF x8A
1D68: MOVLW 14
1D6A: ADDWF x89,W
1D6C: MOVWF 01
1D6E: MOVLW 00
1D70: ADDWFC x8A,W
1D72: MOVWF 03
1D74: MOVF 01,W
1D76: ADDLW D3
1D78: MOVWF FE9
1D7A: MOVLW 02
1D7C: ADDWFC 03,W
1D7E: MOVWF FEA
1D80: MOVF FEE,F
1D82: MOVF FEE,F
1D84: CLRF FEC
1D86: MOVF FED,F
1D88: CLRF FEF
1D8A: MOVF FED,F
1D8C: MOVLW 0F
1D8E: MOVWF FEF
1D90: MOVF FED,F
1D92: MOVLW FF
1D94: MOVWF FEF |
* It's a global structure. In theory, address of each of it's member can be determined at compile time. _________________ Read the label, before opening a can of worms. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Aug 26, 2010 2:52 pm |
|
|
Quote: |
In theory, address of each of it's member can be determined at compile time.
g_sChannels[iCh].sPID.iMax = 4095;
|
It would if you got rid of the variable index and replaced it with a
number. The code would reduce down to almost nothing. |
|
|
EdWaugh
Joined: 07 Dec 2004 Posts: 127 Location: Southampton, UK
|
|
Posted: Mon Sep 20, 2010 5:46 am |
|
|
Hi all,
Interesting discussion here and some good suggestions. I am having a similar problem and by using PCM Programmer's encapsulating floating point math as functions I have managed to save some space. Does anyone have any tips for identifying functions that are appearing very often in the hex so these can also be encapsulated?
I also had another idea relating to kender's structure problem. I typically do this kind of access to lots of items in the same structure so be calculating the ptr first you save space by doing it only once (perhaps this is what PCM Programmer meant). You start with this:
Code: |
global_structure.structure_array[i].value = 1;
global_structure.structure_array[i].value2 = 2;
|
Then change to:
Code: |
structure_array_t *ptr = &(global_structure.structure_array[i]);
ptr->value = 1;
ptr->value2 = 2;
|
This cuts out a lot of address recalculation.
Also, what actually happens on #opt 10 and 11? Moving to 10 offers further savings but I am nervous about it, could it have broken my code in a way I can't easily notice? 11 just gives an 'internal error' during compile.
Cheers
Ed |
|
|
collink
Joined: 08 Jan 2010 Posts: 137 Location: Michigan
|
|
Posted: Mon Sep 20, 2010 6:13 am |
|
|
EdWaugh wrote: |
Also, what actually happens on #opt 10 and 11? Moving to 10 offers further savings but I am nervous about it, could it have broken my code in a way I can't easily notice? 11 just gives an 'internal error' during compile.
|
I can't answer with any certainty but from what I've seen in the list file when trying to compare code from optimization level 9 and 11 it seems like what it's doing is code sharing. It seems like it tries to find areas that are in common between multiple areas (or even functions) and factor them out so that there is only one instance. This saves code space but it seems like sometimes it gets a little confused and factors out areas of code that aren't compatible. Or maybe it's just that sometimes it factors out a code section that ends up calling itself or something and crashing.
At any rate, opt level 10 is scary to use and opt level 11 seems like suicide. They both stand a good chance of totally destroying your code. However, I have gotten programs to work at opt level 11 so it IS possible. If you can get your program to work at opt level 11 you can see space savings of upwards of 20%. I'm not kidding! The program space savings can be really large. This does you no good if your program crashes though.
Come to think of it, I think that the program I got to work at opt level 11 had no interrupt calls. It's possible that the optimization factors code in common between interrupt called functions and non interrupt functions and thus clobbers on interrupts. I should try a different program with interrupts and try setting opt level 9 around every function called from an interrupt and see what happens... |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|