How to Create Objects Larger than 256 Bytes with Microchip's MPLAB C18

One of the major design flaws with Microchip's MPLAB C18 compiler is that the programmer cannot create a single RAM object larger than 256 bytes without jumping through several hoops, including restrictions on how the object can be used in code. Simply defining such an object without jumping through the necessary hoops will lead to a can not fit the section linker error. If you are getting a can not fit the section error, and each of your objects is 256 bytes or smaller, the solution below is not applicable; go here instead.

These are the steps for creating large objects with C18:

  1. Put the large object(s) in dedicated sections:
    #pragma udata large_udata
    unsigned char big_buff1[300];
    unsigned char big_buff2[350];
    #pragma udata
    
    #pragma idata large_idata
    unsigned char big_table[400] = { 1, 2, 42 };
    #pragma idata
    
    In this example, there are several large objects, some uninitialized and thus placed in a udata section, and some initialized and thus placed in an idata section. udata and idata objects cannot be placed into the same section. It's not necessary to create both sections if you don't have both large idata and udata objects (most applications only have large udata objects). The section names can be any valid C identifier; the ones shown above do not have any special meaning. Be sure to include the section-closing #pragma after the declaration to prevent other objects from being placed into the large section. It isn't necessary to group all of the objects together in a single section, but doing so simplifies the required linker script modifications. The same section name can be used in different source files if different files contain large objects.

  2. Add a linker script to your project. With recent versions of the compiler, linker scripts are located in the compiler's bin/LKR directory. Be sure to get your linker script from bin/LKR rather than the lkr directory, since the scripts in the latter directory are obsolete, and should not be used. The lkr directory also does not include scripts for newer PICs.

  3. Determine the total size of the large sections, in bytes. In the example above, the large_udata section is 650 bytes, and the large_idata section is 400 bytes.

  4. Edit the linker script to create new memory regions to accommodate the large sections. In the linker script, you'll see a number of RAM memory regions declared like this:
    DATABANK   NAME=gpr1       START=0x100             END=0x1FF
    DATABANK   NAME=gpr2       START=0x200             END=0x2FF
    DATABANK   NAME=gpr3       START=0x300             END=0x3FF
    DATABANK   NAME=gpr4       START=0x400             END=0x4FF
    DATABANK   NAME=gpr5       START=0x500             END=0x5FF
    
    These 256-byte regions need to be combined into larger regions to fit the new sections:
    DATABANK  NAME=large_udata  START=0x100  END=0x389  PROTECTED
    DATABANK  NAME=large_idata  START=0x38A  END=0x519  PROTECTED
    DATABANK  NAME=gpr5         START=0x51A  END=0x5FF
    
    SECTION   NAME=large_udata  RAM=large_udata
    SECTION   NAME=large_idata  RAM=large_idata
    
    Note that we used all of gpr1-gpr4 and part of gpr5 for our large sections. The size of gpr5 is adjusted so that its unused area is available for normal allocation by the linker. The size of the large regions (END - START + 1) should match the size of the corresponding large sections.

    The PROTECTED keyword tells the linker not to place anything into the large regions of its own accord; only objects which have been explicitly placed into the associated sections with source code #pragmas can be placed there.

    The SECTION directive creates a logical section. A logical section links the section name used in the source code #pragma to the associated memory regions declared with the DATABANK directive.

    Ensure that each memory region's size is exactly the same as the section's size. If a memory region is too small, you'll get a can not fit the section linker error. If the region is too large, everything will work fine, but the additional RAM will be wasted since the linker is not permitted to allocate it due to the PROTECTED keyword.

  5. Ensure that any access of the large objects is through a pointer. C18 will not generate correct bank selection instructions for all direct accesses. In other words, you must do:
    unsigned char *ptr;
    ptr = big_buff1;
    ptr[5] = 42;
    
    Statements like
    big_buff1[5] = 42;
    big_buff1[index] = 42;
    
    will not work correctly in some circumstances. They will work correctly in some, or perhaps even most circumstances. But they will not work correctly in all circumstances.

Notes

The above instructions describe the most verbose and explicit method of creating a large object. The notes below describe some shortcuts that can be taken.

The PROTECTED keyword on the large memory regions isn't mandatory. The sections marked with #pragmas will take priority and get placed in those memory regions ahead of anything else. Using PROTECTED offers insurance against a typo: if you accidentally make your memory region too large, and the region isn't PROTECTED, the linker is free to put other (small) objects in the region after it has placed your large objects. You're not going to know those other objects are there, so you're not going to access them through a pointer, which can result in weird, hard-to-find misbehavior in the code.

It's also not necessary to use #pragma udata in your code, nor is it necessary to create the logical section in the linker. The linker assigns objects to memory regions in decreasing size order, so the large objects will be placed in the large memory regions without the linker needing to be told to do so explicitly. But providing these extra hints to the linker might prevent a can not fit the section error caused by a corner case in the linker's placement algorithm.


Copyright © 2011 John Temples (pic at xargs dot com)