CCS News

Dealing With Out of ROM Errors

Thursday 18 March, 2021

Often times the "Out of ROM, a segment or the program is too large" error pops up near the end of a project and users are now forced to deal with this new problem. This article will help to understand the causes and some simple solutions to get back to the project.

About Segment Sizes
The first thing to realize is that users may get this error and have plenty of ROM left. The reason is the CCS C Compiler does not split a function across a ROM page boundary. This error message always produces some informational lines in the .err file to help understand what is going on. Here is an example output for the case where a function is too large for a segment.

C:PICmain.c:1231:1: Error#71 Out of ROM, A segment or the program is too large
process_data
Seg 00000-00002, 0000 left, need 009C7 Reserved
Seg 00003-00003, 0001 left, need 009C7
Seg 00004-00055, 0000 left, need 009C7 Reserved
Seg 00056-007FF, 003D left, need 009C7
Seg 00800-00FFF, 042E left, need 009C7
Seg 01000-017FF, 0800 left, need 009C7
Seg 01800-01FFF, 0800 left, need 009C7
Seg 02000-027FF, 0800 left, need 009C7
Seg 02800-02FFF, 0800 left, need 009C7
Seg 03000-037FF, 0800 left, need 009C7
Seg 03800-03FFF, 0800 left, need 009C7

The first thing to notice is the "process_data." This is the name of a function in the users program that caused the linker to stop. Then look at the number after "need", this is how many words would be needed for that function (process_data). Sometimes it is different in different areas of memory because extra instructions are needed to jump to other pages.

The ROM in the chip is split into segments. Each segment is shown in this list. The first and third are marked as reserved, this is the reset jump and interrupt handler. These segments cannot be relocated. Of the remaining segments you will notice there is never more than 800 (2K) available (left). This is because on this chip the page size is 0x800.

There is plenty of ROM available on this chip but the function takes 9C7 and that does not fit into the 800 segment size. The simple solution is to split the function into two.

We most commonly see this error in technical support with main() as the function and there are no other functions in the program. It is always good to adapt a coding style where there are some limits on function size to aid in readability, maintainability and to keep sanity. It is recommended that a function should fit on a screen or at least a page.

It is possible to get an error like the above when users have a well structured program with lots of small functions. Consider the following example:

void func_b(void) {
...
}
void func_c(void) {
...
}
void func_d(void) {
...
}
void func_a(void) {
funct_b();
funct_c();
funct_d();
}

If funct_b(), funct_c() and funct_d() are each only called once then the compiler to save valuable stack space will copy the code from those functions to funct_a(). This makes funct_a() way bigger that it appears.

To solve the problem users can tell the compiler to never inline the function like this:
#separate
void func_b(void) {
...
}

Note that when prototyping the function there must also have the #separate there.

Here are the page (max segment) sizes for each family:



Fragmentation
Another problem maybe the way the segments are being used. Users can see something like this:

Seg 00000-00002, 0000 left, need 00261 Reserved
Seg 00003-00003, 0001 left, need 00261
Seg 00004-00055, 0000 left, need 00261 Reserved
Seg 00056-007FF, 003D left, need 00261
Seg 00800-00FFF, 022E left, need 00261
Seg 01000-017FF, 00F7 left, need 00261
Seg 01800-01FFF, 0005 left, need 00261
Seg 02000-027FF, 0193 left, need 00261
Seg 02800-02FFF, 0073 left, need 00261
Seg 03000-037FF, 011B left, need 00261
Seg 03800-03FFF, 0175 left, need 00261

In total you have enough RAM for your function of size 261 but not all in the same segment. The linker does try to shift functions around and make room, but if this is not possible you will get the error. The best way to solve this is to again split some larger functions into smaller functions. If there are more smaller functions the linker will be able to shuffle things around and make more room in one of the segments for your 261 function.

To find out how much ROM each functions used in the IDE use COMPILE > STATISTICS. There needs to be an error free compile to see this however.

Code Optimization
If users are really out of ROM then they need to consider optimizing your code. The general rule is to find groups of lines that are the same or similar and move them to a function. The compiler does some optimization between statements, but most of the optimization is done on a single statement. Skimming through the .LST file to identify C lines that generate a lot of assembly can help to identify areas that users may want to check for the possibility to move to a function. Here are some examples:

1. Even though this appears to be a simple initialization you will save ROM by moving the four lines to a function:
next_in=0;
next_out=0;
count=0;
total=0.0;
read_from_history();
...
next_in=0;
next_out=0;
count=0;
total=0.0;
read_from_device();

2. Unless using #opt compress (see below) the compiler does not optimize the subscript expression used here:
weight = weight_lookup[width*height+cal_offset];
time = time_lookup[width*height+cal_offset];

When possible do this:
volume = width*height+cal_offset;
weight = weight_lookup[volume];
time = time_lookup[volume];

3. When reviewing the LST file users will see printf's can take a lot of space. For code like this:
printf("Max position = %lu:%lurn", max_x, max_y);
printf("Ave position = %lu:%lurn", ave_x, ave_y);
printf("Min position = %lu:%lurn", min_x, min_y);

Consider:
void printxy( rom char * label, int16 x, int16 y) {
printf("%s position = %lu:%lurn", label, x, y);
}
...
printxy("Max", max_x, max_y);
printxy("Ave", ave_x, ave_y);
printxy("Min", min_x, min_y);

Compiler Optimization
By default the compiler uses optimization level 9. This provides a good level of optimization that has been well tested. If the optimization is set down then now is a good time to get it back up to 9. If there are any optimization problems do report then to support so we can fix them.

Some chips have a more extreme level of optimization available. To invoke it use:
#opt compress

This optimization will save ROM but will take longer to run. What the compiler does is to go through the whole program and any sequence of instructions that is repeated are made into a function that is called each time it is needed. This will make debugging and even reading the .LST file more difficult. The savings can be substantial and may prevent users from moving to a larger chip.


Like us on Facebook. Follow us on Twitter.

About CCS:

CCS is a leading worldwide supplier of embedded software development tools that enable companies to develop premium products based on Microchip PIC® MCU and dsPIC® DSC devices. Complete proven tool chains from CCS include a code optimizing C compiler, application specific hardware platforms and software development kits. CCS' products accelerate development of energy saving industrial automation, wireless and wired communication, automotive, medical device and consumer product applications. Established in 1992, CCS is a Microchip Premier 3rd Party Partner. For more information, please visit https://www.ccsinfo.com.

PIC® MCU, MPLAB® IDE, MPLAB® ICD2, MPLAB® ICD3 and dsPIC® are registered trademarks of Microchip Technology Inc. in the U.S. and other countries.