Electrical – Placing a key variable in internal flash Memory

armkeilstm32l

I am trying to place a variable named secret_key at location 0x08002000 in STM32Nucleo-board which has STM32L476RG on board with 1MB flash.

Question 1: Why am i unable to place code in custom ROM (SECRET_IROM1 )section?
Question 2: What is the mistake i am doing?

I am still newbie to Linker topic.

I am able to do it(still do not undestnad, why other RO section also follows my new address of secret_key variable) if i define the address which is in the default section for RO in the scatter file. If i create a new section, mapping is not happening. I can see the address assigned as expected but the value of the secret_key is not visible at that location.

below image shows what happens when i declare the address out of range from the default section.

Code section where variable is declared:

unsigned long long int array4[6]={0};
unsigned long long int array[6]={12};

unsigned long long int arra2[4] = {0x1111111111111111,0x2222222222222222,0x3333333333333333,0x4444444444444444};
const long secret_key[3] __attribute__((section(".ARM.__at_0x8001800"))) = {0xABABABAB,0xCDCDCDCD, 0xEFEFEFEF};
extern int addsum(int, int);

int main()
       {
    long long int array3[100] = {1,2,3,4,5,6,7};
    array[1] = arra2[1] - array[0]+array3[1]*2+array3[2]*2+addsum(array[1],array[2])+array4[2]+(int)secret_key[0];
    while(1);
}

Memory map below:
enter image description here

Scatter file contents below:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00002000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }

  SECRET_IROM1 0x08002000 0x00000030  {  ; load address = execution address
  *(.ARM.__at_0x8001800) ; secret key
  }

  RW_IRAM1 0x20000000 0x00000100  {  ; RW data
   project2.o (+RW)
   }

  RW_IRAM2 0x20000100 0x00000100  {  ; RW data
   project2.o (+ZI)
   }

  RW_IRAM3 0x20000200 0x00000300  {  ; RW data
   project.o (+ZI)   
  }

  RW_IRAM4 0x20000500 0x00000200  {  ; RW data
   project.o (+RW)   
  }

  RW_IRAM5 0x20000700 0x00001000  {  ; RW data
   .ANY (+RW,+ZI)   
  }
}

Second case: where it doesn't work

Code here:

unsigned long long int array4[6]={0};
unsigned long long int array[6]={12};

unsigned long long int arra2[4] = {0x1111111111111111,0x2222222222222222,0x3333333333333333,0x4444444444444444};
const long secret_key[3] __attribute__((section(".ARM.__at_0x8002100"))) = {0xABABABAB,0xCDCDCDCD, 0xEFEFEFEF};
extern int addsum(int, int);

int main()
       {
    long long int array3[100] = {1,2,3,4,5,6,7};
    array[1] = arra2[1] - array[0]+array3[1]*2+array3[2]*2+addsum(array[1],array[2])+array4[2]+(int)secret_key[0];
    while(1);
}

associated scatter file here:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00001800  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }

  SECRET_IROM1 0x08002100 0x00000030  {  ; load address = execution address
  *(.ARM.__at_0x8002100) ; secret key
  }

  RW_IRAM1 0x20000000 0x00000100  {  ; RW data
   project2.o (+RW)
   }

  RW_IRAM2 0x20000100 0x00000100  {  ; RW data
   project2.o (+ZI)
   }

  RW_IRAM3 0x20000200 0x00000300  {  ; RW data
   project.o (+ZI)   
  }

  RW_IRAM4 0x20000500 0x00000200  {  ; RW data
   project.o (+RW)   
  }

  RW_IRAM5 0x20000700 0x00001000  {  ; RW data
   .ANY (+RW,+ZI)   
  }
}

memory map below:
enter image description here

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Results of suggestion fron Jeroen3

enter image description here
enter image description here

I am expecting the values loaded into the variable secret_key to be visible in the flash memory region, but it is not.

Best Answer

secretkey.c:

const long secret_key[3] __attribute__((used)) = {0xABABABAB,0xCDCDCDCD, 0xEFEFEFEF};

__attribute__((used)) to prevent it from being removed if it isn't used anywhere.

secretkey.h:

extern const long secret_key[3];

scatterfile.sct:
Make a region and a C file, then add the RO (read only) part of that C file to the region. Nothing else.

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00002000 {    ; load region size_region
  ER_IROM1 0x08000000 0x00002000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }

  RW_IRAM1...
}

LR_SECRET_IROM1 0x08002000 0x00000030  {                         
  SECRET_IROM1 0x08002000 0x00000030  {
    secretkey.o (+RO)
  }
}

You now have two load regions (lr_), these blocks of memory (regions) are programmed into flash by the programmer. LR_IROM1 and LR_SECRET_IROM1.
Inside the load regions are execution regions. This is where the code references the objects from. ER_IROM1, RW_IRAM1 and SECRET_IROM1.

Most often the addresses of the load and execution region are identical.
But you can have a load region for flash, and an execution region for sram.
For example with pre-initialized variables. This means the linker then must insert the scatter loading algorithm. This copies the data from the flash location where the data is programmed in flash, to the intended usable location in sram. (i.e. where the code thinks the variables are)

This algorithm is called from the startup.s file, and it makes the jump to int main(void), for Keil this algorithm is started by calling __main in startup.s.

But when the execution region already equals the load region, no copying is required. Such as with the actual code, and your now with constant.

In my earlier version (and the part in your question), the constant was put inside an execution region, meaning it was programmed using .ANY (+RO) and then copied to the intended location. Except the code can't write to flash, thus nothing would be changed there.

See also, ARM® Compiler armlink User Guide: 8.2 Syntax of a scatter file
I'd also recommend looking at the memory .map file the linker outputs. You can use it to verify how your image is put together. I made a small tool for this.
The tickbox is in target settings -> listing -> linker listing.


Your RAM regions are a bit strange. I do not think there is an advantage in using multiple 256 sized regions.

Instead, you could use all SRAM regions:
- 96 kByte at 0x20000000
- 32 kByte at 0x10000000