10.6.3 Using an Application-Defined Memory Region

To use this feature, work through the following sections.

Define a New Memory Space

The XC32 tool suite requires information about each memory region. In order for the XC32 linker to be able to properly assign memory, you must specify information about the size of the memory region available and the origin of the memory region.

Define an application-defined memory region, with the origin and the size, using the 'region' pragma as shown below.

#pragma region name=name origin=address size=bytes

where name is a quoted string containing the name of the region, address is the starting address of the region, and bytes is the size in bytes of the region.

Defining a New Memory Space

#pragma region name="ext_mem" origin=0xC0000000 size=0x1000

In this example, we define an application-defined memory region to be used for external memory. We name the region "ext_mem" and specify that the starting address is 0xC0000000 and that it has a size of 0x1000 bytes. Consult your PIC32 device data sheet for information about the external-memory interface options and the memory mappings available on your device.

Define Variables within a Region

When you have defined a new memory region, you can then assign a variable to that region. Use the region attribute on a variable to specify that it should be allocated to the specified region. This requires the memory region definition to be present. Given the definition in the previous subsection, you can make the following variable definition:

int ext_array[256] __attribute__((region("ext_mem")));

ext_array will be allocated in the previously declared region "ext_mem".

Once the variable has been defined with the region attribute, it may be accessed using normal C syntax.

When called with the --report-mem linker command-line option, the linker prints a summary of memory usage to stdout. If the -Map linker command-line option was specified, the memory summary will also be printed in the map file. When the application-defined memory region is used, the length of each section allocated to a region and the total memory used for each region is displayed.

Define Functions within a Region

You can also use the region attribute to assign individual functions to the region. This requires the memory region definition to be present. Given the definition in the previous subsection, you can make the following function definition:

int  __attribute__((far, region("ext_mem"))) foo()
{
   ext_array[2] = ext_array[0] + ext_array[1] ;
   return 0;
}  

Use the region attribute with the far attribute to allocate the function in our example "ext_mem" region. In this case, we need the far attribute because the address of our "ext_mem" region is located outside of the 256 MB segment of our default program-memory region, kseg0_program_mem, as defined in our default linker script. Using the far attribute tells the compiler to generate a long call.

Initializing Memory Interfaces

When your application-defined memory region corresponds to an external-memory interface such as the Serial Quad Interface (SQI) or External Bus Interface (EBI), you will likely need to configure the interface module. For instance, the EBI module must be configured to understand such things as the type, size, and bus width of each attached device. See the device data sheet and the family reference manual for your target device.

The default XC32 runtime start-up code uses a linker-generated data-initialization template placed in a section named .dinit (see 18.2.9 Initialize Objects and RAM Functions). For variables or functions placed in an application-defined memory region, the application must execute any memory-interface configuration code before the runtime start-up code attempts to initialize these variables or functions.

The default runtime start-up code provides an _on_reset() weak hook. This routine is called after initializing a minimum ‘C’ context, but before data initialization. You can provide your memory-interface configuration code in this hook. See 18.2.5 The “On Reset” Routine for more information on this important hook.

Hardware Init Before Data Init

/* The _on_reset() function will be called by the default
   runtime start-up code prior to data initialization. */
void _on_reset (void)
{
  /*  Call a function that configures the EBI control
      registers for the target board. */
  configure_ebi_sram();
}

On some target devices, your application may also need to enable the Memory Management Unit (MMU) and initialize the Translation Lookaside Buffer (TLB). On many devices, the XC32 toolchain provides a default mapping suitable for the SQI and EBI interfaces. See your target device data sheet for information on default memory mapping that is specific to your target device. For devices where a default SQI and EBI mapping is provided, you can override the default mapping by providing your own __pic32_tlb_init_ebi_sqi() function.

The source code for this is found in the pic32m-libs.zip file located at:

<install-directory>/pic32-libs/

Once the file is unzipped, the source code can be found at:

pic32m-libs/libpic32/stubs/pic32_init_tlb_ebi.S.

The following sections provide example cases using the application-defined memory region feature.

Case 1

Variables can be placed in external memory by using the region attribute.

#pragma region name="ext_mem" origin=0x0xC0000000 size=0x1000

signed int   ea1  __attribute__((region("ext_mem")));
unsigned int ea2  __attribute__((region("ext_mem")));

signed int   eb1[10]  __attribute__((region("ext_mem"))) = {10,20};
signed long  eb2[10]  __attribute__((region("ext_mem"))) = 
                                         {0x987654321, 0x12345678};

Case 2

Functions can be placed in external memory by using the region attribute. Since functions default to space(prog), the function is assumed to be programmed into the region and will not be initialized by the runtime start-up code.

#pragma region name="ext_flash" origin=0xC0000000 size=0x1000

int ea1 __attribute__((region("ext_flash"))) ;
int eb1 __attribute__((region("ext_flash"))) = 0x1000 ;
int ec1 __attribute__((region("ext_flash"))) = 0x2000 ;

void __attribute__((region("ext_flash"))) foo()
{
    ea1 = eb1 + ec1 ;
}

void main()
{
    foo();
}

Apply the far attribute to foo(), since it is out of range of the default kseg0_program_mem region. Alternatively, use -mlong-calls option to compile the above example.

Case 3

Combine the region attribute with the space (data) attribute to indicate that the function code should be initialized by the runtime start-up code's data initialization template. In this case, the code for the function is contained in the data-initialization template and copied to the memory region at startup.

#pragma region name="myebi_sram"  origin=0xC0004000  size=0x100
void __attribute__((far,space(data),region("myebi_sram"))) fn_in_sram()
{ /* Code here */ }
 

Case 4

Combine the region attribute with the address() attribute to place a variable at an absolute address within the region.


#pragma region name="myebi_2"  origin=0xC0001000  size=0x10
unsigned long __attribute__((region("myebi_2"),address(0xC0001000))) paws = 0xAAAABBBB;