KEIL uVision: Tutorial 2: Storage of Variables, Constants/Literals and Code in Memory
-Aviral Mittal avimit att yhaoo dat camm.
Connect @ https://www.linkedin.com/in/avimit/

The First Tutorial, was about getting started with Keil uVision using a bare-minimum programing infrastructure.
This tutorial will demonstrate some other low-level aspects of embedded programing while using Keil uVision as the IDE (integrated development environment) and ARM processor as the target processor.
This tutorial will demonstrate how the Keil compiler uses memory to store variables, constants or code. This can be important for embedded software engineers, as memory is precious.
Consider the following C program:


typedef unsigned int uint32_t;
int main ()
{
    int ii; 
    uint32_t avar[10] = {0xaaaaaaaa,0xbbbbbbbb,0xcccccccc,0xdddddddd,0xeeeeeeee,0xffffffff,0xaabbaabb,0xccddccdd,0xeeffeeff,0xabcdefaa};
    uint32_t *ap; //pointer pointing to first element of vector avar.
   
    uint32_t bvar[10] = {1,2,3,4,5,6,7,8,9,0xa};
    uint32_t *bp; //pointer pointing to first element of vector bvar.
    ap = &avar[0]; //myp is pointing to the address of myp
    bp = &bvar[0];

    const uint32_t maxc = 0xabcdefaa;
   
    for(ii=0;ii<10;ii++)
    {
        asm("NOP");
        *((uint32_t *) (0x20000000)) = maxc;
        *bp = *ap;
        ap++;
        bp++;
        asm("NOP");
    }
    while(1);
}



It has variables namely ii, avar, bvar.
It has pointer variables *ap, *bp.
It has const called 'maxc'.
Note that the variables have been initialized to constant values.


The program does the following 2 things:
1. It writes the location 0x2000_0000 with a value of the constant 'maxc'. User is advised not to use these kind of hard coded addresses. Here it is only done for demonstration purposes only. This address might be reseved for other other things and therefore the execution of main program may get corrupted.
The program copies the variable ap into variable bp.


Compile it in Keil: To get an axf file, the user can use the same startup.s file as introduced in First Tutorial, and use the above C code. Then 'build' the target and use fromelf utility to convert the 'axf' file into its text version:
The following is the extract from the text version of the resulting axf file:



i.main
    main
        0x00000114:    b094        ..      SUB      sp,sp,#0x50
        0x00000116:    2228        ("      MOVS     r2,#0x28
        0x00000118:    490e        .I      LDR      r1,[pc,#56] ; [0x154] = 0x15c
        0x0000011a:    a80a        ..      ADD      r0,sp,#0x28
        0x0000011c:    f7ffffb8    ....    BL       __aeabi_memcpy4 ; 0x90
        0x00000120:    2228        ("      MOVS     r2,#0x28
        0x00000122:    490c        .I      LDR      r1,[pc,#48] ; [0x154] = 0x15c
        0x00000124:    3128        (1      ADDS     r1,r1,#0x28
        0x00000126:    4668        hF      MOV      r0,sp
        0x00000128:    f7ffffb2    ....    BL       __aeabi_memcpy4 ; 0x90
        0x0000012c:    ae0a        ..      ADD      r6,sp,#0x28
        0x0000012e:    466d        mF      MOV      r5,sp
        0x00000130:    bf00        ..      NOP     
        0x00000132:    2400        .$      MOVS     r4,#0
        0x00000134:    e00a        ..      B        0x14c ; main + 56
        0x00000136:    bf00        ..      NOP     
        0x00000138:    4807        .H      LDR      r0,[pc,#28] ; [0x158] = 0xabcdefaa
        0x0000013a:    f04f5100    O..Q    MOV      r1,#0x20000000
        0x0000013e:    6008        .`      STR      r0,[r1,#0]
        0x00000140:    6830        0h      LDR      r0,[r6,#0]
        0x00000142:    6028        (`      STR      r0,[r5,#0]
        0x00000144:    1d36        6.      ADDS     r6,r6,#4
        0x00000146:    1d2d        -.      ADDS     r5,r5,#4
        0x00000148:    bf00        ..      NOP     
        0x0000014a:    1c64        d.      ADDS     r4,r4,#1
        0x0000014c:    2c0a        .,      CMP      r4,#0xa
        0x0000014e:    dbf2        ..      BLT      0x136 ; main + 34
        0x00000150:    bf00        ..      NOP     
        0x00000152:    e7fe        ..      B        0x152 ; main + 62
    $d
        0x00000154:    0000015c    \...    DCD    348
        0x00000158:    abcdefaa    ....    DCD    2882400170
    $d.realdata
    .constdata
        0x0000015c:    aaaaaaaa    ....    DCD    2863311530
        0x00000160:    bbbbbbbb    ....    DCD    3149642683
        0x00000164:    cccccccc    ....    DCD    3435973836
        0x00000168:    dddddddd    ....    DCD    3722304989
        0x0000016c:    eeeeeeee    ....    DCD    4008636142
        0x00000170:    ffffffff    ....    DCD    4294967295
        0x00000174:    aabbaabb    ....    DCD    2864425659
        0x00000178:    ccddccdd    ....    DCD    3437087965
        0x0000017c:    eeffeeff    ....    DCD    4009750271
        0x00000180:    abcdefaa    ....    DCD    2882400170
        0x00000184:    00000001    ....    DCD    1
        0x00000188:    00000002    ....    DCD    2
        0x0000018c:    00000003    ....    DCD    3
        0x00000190:    00000004    ....    DCD    4
        0x00000194:    00000005    ....    DCD    5
        0x00000198:    00000006    ....    DCD    6
        0x0000019c:    00000007    ....    DCD    7
        0x000001a0:    00000008    ....    DCD    8
        0x000001a4:    00000009    ....    DCD    9
        0x000001a8:    0000000a    ....    DCD    10
    Region$$Table$$Base
        0x000001ac:    000001bc    ....    DCD    444
        0x000001b0:    20000000    ...     DCD    536870912
        0x000001b4:    00000400    ....    DCD    1024
        0x000001b8:    00000044    D...    DCD    68
    Region$$Table$$Limit




Things to Notice:
1. All the initialization constant values for avar and bvar are stored in a section named '.constdata' which is placed next to 'main' code section.
2. Not so obvious is the storage of variables? These are all local variables, and are stored on the stack, as it will be evident from a debug of the above code.
BTW the default location of stack in the memory will be from 0x2000_0000 to 0x2000_0400, when the 'Stack_Size' is made 'EQU' to 0x00000400, as done in the startup.s assembly code.
This will make the __initial_sp to point to 0x2000_0400, and the lower limit of stack be 0x2000_0000.
To see the variables storage, start debugging your program as instructed in First Tutorial:
Put a break point just inside the for loop, and click on 'Run' icon or press F5
Now mouse-over on the pointer variables 'ap' or 'bp'. Remember 'ab' and 'bp' are both pointers to variables 'avar' and 'bvar' respectively, so they should be storing the addresses of  'avar' and 'bvar' respectively.
Mouse over 'avar' reveals '0x2000_03DB'
Mouse over 'bvar' reveals '0x2000_03B0'
To confirm Click on 'Memory 1' tab near to bottom right of your Keil window, and type in the address 0x20000300 to see the contents of memory.
You will see the following:
3. The variable ii does not occupy any memory. Its in the register r4 as shown below:
        0x0000014a:    1c64        d.      ADDS     r4,r4,#1
        0x0000014c:    2c0a        .,      CMP      r4,#0xa

It can be observed from the above code that the variable ii is in register r4, and its compared with an immediate constant 0xa (decimal 10).






The constant values of '0xAAAAAAAA', '0xBBBBBBBB' , '0xCCCCCCCC' are clearly visible at the memory location pointed by the 'ap' pointer.
The user may keep on stepping through the program code, to observe the memory contents pointed to by the pointer 'bp' changing, as it is copied from 'avar'.

What happens when the user declares a global variable, i.e. a variable which is not local to 'main'?
For example edit the C-code shown above and add a variable 'jj' just before the 'main()' function, and use 'jj', instead of 'ii' in the for loop

typedef unsigned int uint32_t;
int jj;
int main ()
{
    int ii; 
    uint32_t avar[10] = ........


These variables are not stored in the stack region, but by default they are stored at the start of R/W region, which is 0x2000_0000 by default for Cortex M targets in Keil.
So the jj will be stored at the location 0x2000_0000.
This will also push the stack lower limit to 0x2000_0004, instead of 0x2000_0000.



Key Learnings:

Following is a view where certain things from c-code are mapped to assembly-code.






This is the end of this tutorial exercise:
However an interesting question arises:
The constants were stored in memory location just following the 'main' program code. i.e at locations 0x0000_015c to 0x0000_01a8,
But when the 'main' program runs, it can be observed that the constant values were already copied to the memory locations pointed by the pointers 'ap' and 'bp', that is to say that the memory locations pointed to by the pointers 'ap' and 'bp' were already 'initialized'.
So how do the constant values from the Read-Only region of memory gets copied into the 'Read-Write' region of memory?

<= PREV                          NEXT =>