IAR initializer function placement

maximus picture maximus · Dec 7, 2012 · Viewed 15k times · Source

Does anybody know how to deal with the following problem:
I have an IAR Embedded workbench. The project is using the SDRAM for running it's code and Flash ROM too. The code for SDRAM is loaded from SD Card. However, in SDRAM there are also some data stored, like global or static variables. Some of them have to be initialized. The initialization step, the iar_data_init3 function call, goes after the low_level_init function. So the problem is that for initialization of some of the variables in SDRAM, the initializer function is called from iar_data_init3, the code of which is inside of the SDRAM itself. Which is wrong because the loading of SDRAM code from SD Card is not yet done.

I have tried manual initialization as described in the C/C++ development guide, but this didn't help.

The function which is called is __sti__routine, which provides initialization of variables. All of these functions are generated by IAR. Is there any way to tell the linker to put the initializer functions to Flash ROM?

EDIT 1: Here is information from IAR manual for C/C++. It is an example of how to use manual initialization.

In the linker config file:

initialize manually { section MYSECTION };

Then IAR documentation says:

you can use this source code example to initialize the section:

#pragma section = "MYSECTION"
#pragma section = "MYSECTION_init"
void DoInit()
{
char * from = __section_begin("MYSECTION_init");
char * to = __section_begin("MYSECTION");
memcpy(to, from, __section_size("MYSECTION"));
}

I can't understand however, first of all, what is the difference between MYSECTION_init and MYSECTION. Aslo, if I have a global variable:

SomeClass myclass;

And it should be placed in SDRAM, then how does the initialization is done for it? I want to manually initialize the variable, and place that initializing functions to flash ROM. (the problem is that by placing variable to SDRAM it's initializing function also is placed to SDRAM).

Answer

embedded.kyle picture embedded.kyle · Dec 7, 2012

You can specify the location of variables and functions through the use of pragma preprocessor directives. You will need to use either one of the predefined sections or define your own.

You don't mention the specific flavor of IAR you're using. The following is from the Renesas IAR Compiler Reference Guide but you should check the proper reference guide to make sure that the syntax is exactly the same and to learn what the predefined sections are.

Use the @ operator or the #pragma location directive to place groups of functions or global and static variables in named segments, without having explicit control of each object. The variables must be declared either __no_init or const. The segments can, for example, be placed in specific areas of memory, or initialized or copied in controlled ways using the segment begin and end operators. This is also useful if you want an interface between separately linked units, for example an application project and a boot loader project. Use named segments when absolute control over the placement of individual variables is not needed, or not useful.

Examples of placing functions in named segments

void f(void) @ "FUNCTIONS";

void g(void) @ "FUNCTIONS"
{
}

#pragma location="FUNCTIONS"
void h(void);

To override the default segment allocation, you can explicitly specify a memory attribute other than the default:

__code32 void f(void) @ "FUNCTIONS";

Edit

Based on your comments you should have a linker file named generic_cortex.icf that defines your memory regions. In it should be instructions somewhat similar to the following:

/* Define the addressable memory */
define memory Mem with size = 4G;

/* Define a region named SDCARD with start address 0xA0000000 and to be 256 Mbytes large */
define region SDCARD = Mem:[from 0xA0000000 size 0xFFFFFFF ];

/* Define a region named SDRAM with start address 0xB0000000 and to be 256 Mbytes large */
define region SDRAM = Mem:[from 0xB0000000 size 0xFFFFFFF ];

/* Place sections named MyCardStuff in the SDCARD region */
place in SDCARD {section MyCardStuff };

/* Place sections named MyRAMStuff in the SDRAM region */
place in SDRAM {section MyRAMStuff };

/* Override default copy initialization for named section */
initialize manually { section MyRAMStuff };

The actual names, addresses and sizes will be different but should look similar. I'm just using the full size of the first two dynamic memory areas from the datasheet. What's happening here is you are assigning names to address space for the different types of memory (i.e. your SD Card and SDRAM) so that sections named during the compile will be placed in the correct locations by the linker.

So first you must define the address space with define memory:

The maximum size of possible addressable memories

The define memory directive defines a memory space with a given size, which is the maximum possible amount of addressable memory, not necessarily physically available.

Then tell it which chips go where with define region:

Available physical memory

The define region directive defines a region in the available memories in which specific sections of application code and sections of application data can be placed.

Next the linker needs to know in what region to place the named section with place in:

Placing sections in regions

The place at and place into directives place sets of sections with similar attributes into previously defined regions.

And tell the linker you want to override part of it's initialization with initialize manually:

Initializing the application

The directives initialize and do not initialize control how the application should be started. With these directives, the application can initialize global symbols at startup, and copy pieces of code.

Finally, in your C file, tell the compiler what goes into what sections and how to initialize sections declared manually.

SomeClass myClass @ "MyCardStuff";

#pragma section = "MyCardStuff"
#pragma section = "MySDRAMStuff"
void DoInit()
{
    /* Copy your code and variables from your SD Card into SDRAM */
    char * from = __section_begin("MyCardStuff");
    char * to = __section_begin("MySDRAMStuff");
    memcpy(to, from, __section_size("MySDRAMStuff"));

    /* Initialize your variables */
    myClass.init();
}

In order to customize startup initialization among multiple different memory devices, you will need to study the IAR Development Guide for ARM very carefully. Also try turning on the --log initialization option and studying the logs and the map files to make sure you are getting what you want.