View previous topic :: View next topic |
Author |
Message |
kgng97ccs
Joined: 02 Apr 2022 Posts: 97
|
Out of ROM, A segment or the program is too large |
Posted: Sat Dec 30, 2023 3:33 am |
|
|
I am using the PIC18LF46K22 MCU, MPLAB IDE v8.92, and CCS C compiler v5.115.
I am referencing this helpful reply from Ttelmah regarding optimizing code (https://ccsinfo.com/forum/viewtopic.php?t=59937; that post is locked): Quote: | Very basic things.
For example, if you are doing basically the same job in
two different places, making this into a separate routine, and then calling
this single routine (especially if you declare this as #SEPARATE, to avoid
the compiler by default 'inlining' small routines, will save space.
This is what the COMPRESS will switch. By default, small functions will
not be called as subroutines, instead the compiler will make a separate
copy each time these are called. Now when debugging, you step through
the code and can see that this is inside the main code where it is called.
However with COMPRESS selected the compiler instead makes this a
called subroutine. If you 'break' inside this, you can't tell which of the
calls you actually got there from. |
Questions:
1. For the following error message, how can I find out which functions the segments are pointing to?
*** Error 71 "F:\A\_Data1\Programming\SMN-828B_Code_v6.1\main_N828B_v6.1.c" Line 628(1,2): Out of ROM, A segment or the program is too large MAIN
Seg 00194-0FFFE, 04F2 left, need 00786
Seg 00000-00002, 0000 left, need 00786 Reserved
Seg 00004-00006, 0004 left, need 00786
Seg 00008-00192, 0000 left, need 00786 Reserved
I am trying to identify these functions so that I can break them into smaller functions.
2. I am already using the "#opt compress" directive. Does this directive make every function a "#separate" function? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Sat Dec 30, 2023 3:41 am |
|
|
No, opt compress will tend to combine things rather than separate them.
It is trying to make the total code size as small as possible.
However what you show suggests you are actually running out of total
size, not space in one segment. Splitting things up will actually make this
worse rather than better....
Segments as a problem are more a PIC16 problem, rather than PIC18.
If you look your whole available code space is the first segment.
You have simply run out of space. Second part of the message
'or the program is too large'..... |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9241 Location: Greensville,Ontario
|
|
Posted: Sat Dec 30, 2023 6:28 am |
|
|
WOW, ran out of ROM in a 46K22 !!!
I'm thinking you have HUGE 'data tables' in your program ??
If so, perhaps you could store that data(say it was a 'font table' ) in an external EEPROM ?
Any 'finished' program shouldn't use more than 90% of ROM, as we all know 'other code' HAS to be added a few days later.....
Just trying to think of options. |
|
|
kgng97ccs
Joined: 02 Apr 2022 Posts: 97
|
|
Posted: Sat Dec 30, 2023 7:20 am |
|
|
Thank you, Ttelmah and Temtronic.
My code can now compile, but with 99% ROM usage (I removed a function that is not called):
Memory usage: ROM=99% RAM=61% - 67%
I am still looking into how to reduce the ROM usage.
My code has many fprintf statements that print both text and variables to a software UART (and ultimately to the computer screen, for the user to see). When I commented out one group of fprintf statements in a function, the ROM usage reduced to 95%!
I will now try to shorten the text in all fprintf statements. Most static data and parameters are already stored in the built-in EEPROM. The PCB does not have any external EEPROM.
I will appreciate any other ideas you or any other contributors may have that can reduce ROM usage. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9241 Location: Greensville,Ontario
|
|
Posted: Sat Dec 30, 2023 8:24 am |
|
|
One possible solution...all depends on how the compiler works....
If all the 'fprints to PC screen' changed into calling a common function, that should ( may ?) reduce space.
Say you need to send 1-10 variables to the PC screen, have ONE function that sends ALL of them,even if some are not needed
I'm thinking each current 'printf to PC screen' code would be changed to a short 'call to 'printtoPCscreen' function instead of several lines of code.
Hopefully you can code a test of my idea,see if it does work or not.
Others may reply already knowing if this will work...at least it sounds good in my head..... |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Sat Dec 30, 2023 12:05 pm |
|
|
Several things.
The first is duplication. Any code that could be split off to be a subroutine?.
That includes as Jay says things like printouts etc.. Also things like messages.
If these are used in more than one place, then store as a constant value
instead of having these stored twice.
Then look at things that could be more efficient. So for example, arrays
are less efficient to access than separate variables. Large switch statements
etc. etc..
Jay also alludes to data. Remember if you initialise a variable, this involves
a ROM copy of the values being stored. Could these values come from
somewhere else like an external EEPROM instead?.
Then really if the code is this large, would it be better to look at a DsPIC?. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9241 Location: Greensville,Ontario
|
|
Posted: Sat Dec 30, 2023 1:45 pm |
|
|
re: Then really if the code is this large, would it be better to look at a DsPIC?.
No, he needs a SMALLER PIC !
Being told you have to use a smaller device and you're forced into becoming 'creative' on how to code and use BITS instead of having 'unlimited' WORDS to use.
As Mr. T points out several ways you can reduce ROM requirements.
I was thinking about this the other day while looking at BASIC code I'd cut in 1977. Had to 'pack' data into 256 byte sectors (85 x 3 was efficient ! ). Another chunk of 'fun' code was encoding 24hrs into a byte,well 7 bits. Each bit represented 15 minutes, and the 8th bit was a 'flag'.
Since memory got huge in size and cheap to buy, programmers don't have 'fun' anymore.. doing more with less. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Sun Dec 31, 2023 10:53 am |
|
|
Good point.
It is easy to write code very inefficiently, and modern programming is
more and more going that way. Using smaller variables also saves size
in the routines to handle them. Multiplying or adding two 8bit values
uses perhaps 1/10th the space of doing the same with 16bit values,
Going with float again perhaps 10* more space.
Using a bit of thought and scaling values to fit into integer types save
both ROM and RAM. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Sun Dec 31, 2023 11:48 am |
|
|
kgng97ccs wrote: | My code has many fprintf statements..... |
I'll offer this since no one else has yet. I've found that the compiler tends to 'inline' printf/fprintf calls instead of what I assumed was a traditional implementation of a generic function which is subsequently called when invoked.
I ran into a similar issue as you; what I did was break out the printfs based on the number of arguments.
It saved a literal TON of memory. |
|
|
kgng97ccs
Joined: 02 Apr 2022 Posts: 97
|
|
Posted: Mon Jan 01, 2024 3:51 am |
|
|
Thank you, everyone, for your ideas.
So far, by shortening the text in fprintf statements by using abbreviations for some words, I have managed to get the ROM usage down to 97% (not much, but at least the full code compiles). I am still looking into other options.
From Newguy: Quote: | what I did was break out the printfs based on the number of arguments. | Question:
I am not sure what you mean by “break out the printfs based on the number of arguments”. Can you explain more? One or more examples will help. Thank you. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Mon Jan 01, 2024 11:33 am |
|
|
Instead of using (for example)
Code: | printf(bputc, "Test message %u\r\n", count); |
Do this instead
Code: | #separate
void my_printf(unsigned char *text, unsigned int8 number) {
printf(bputc, "%s %u\r\n", text, number);
} |
Obviously quite a simplification and generalization, but I hope you get the point. I found that when I looked at the list file, the compiler was generating a completely unique instantiation of each printf I used in code. I then a) refactored my strings to follow the same format, i.e. [TEXT] [ascii number] [carriage return] [line feed], and b) made appropriate calls to my_printf() instead of printf(). The code shrank by a very large amount.
I also broke out different versions of my_printf() depending on how many arguments I'd need.
Clearer? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Mon Jan 01, 2024 11:38 am |
|
|
and remember you must have #device PASS_STRINGS=IN_RAM to use
constant strings passed like this. |
|
|
kgng97ccs
Joined: 02 Apr 2022 Posts: 97
|
|
Posted: Tue Jan 02, 2024 12:39 am |
|
|
Thank you, Newguy and Ttelmah. Yes, the example helps.
1. If I am passing a string constant declared in an array, I will not need to use the directive “#device PASS_STRINGS=IN_RAM”. Is this correct?
Example: Code: | char num_loops[] = “Number of loops: ”;
my_printf(num_loops, number); |
2. If I am passing a string constant written in double quotes in the calling statement, I will need to use the directive “#device PASS_STRINGS=IN_RAM”. Is this correct?
Example: Code: | my_printf(“Number of loops: ", number); |
Will this option (passing a string constant in double quotes) use less ROM than option 1 (using a declared string constant)? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Tue Jan 02, 2024 4:20 am |
|
|
Pass strings is useful in a lot of places and costs very little in code size.
Passing as a RAM string will work, but will cost (in this case) 18bytes of RAM.
Add this up for all the strings, and you'll probably run out.... |
|
|
kgng97ccs
Joined: 02 Apr 2022 Posts: 97
|
|
Posted: Wed Jan 03, 2024 5:39 am |
|
|
I have managed to get the ROM usage down to 95% (from 100%) by doing the following (I have “opt compress” in my code).
1. Abbreviating some text in fprintf() statements.
2. Consolidating sequential fprinf() statements into one fprintf() statement.
3. Using a wrapper function to wrap the built-in read_eeprom() function and doing the same for the write_eeprom() function. I gathered from the CCS forum that this can help to prevent the compiler from making these built-in functions inline. I placed “#separate” before the function prototype of each of these wrapper functions, but found that it actually does not affect the ROM usage (as shown in the .STA file).
I am still looking into how best to use one or more subroutines to do the fprintf(). This will need more planning, as my fprintf() statements are varied and scattered across many functions.
Ttelmah wrote: | Jay also alludes to data. Remember if you initialise a variable, this involves
a ROM copy of the values being stored. Could these values come from
somewhere else like an external EEPROM instead?.
Then really if the code is this large, would it be better to look at a DsPIC?. |
Is it possible to ask the compiler to save constants, including string constants, and initial values of variables in the RAM?
I am asking this because my code has some unused RAM space:
Memory usage: ROM=95% RAM=61% - 67%
It will be great if I can use the RAM to free up some ROM space. |
|
|
|