Hello folks,
I wish to view the contents of flash memory on STM32L471 (1MB ROM) MCU using Keil ULINK2 debugger (uVision IDE v5.20.0.0), but when I do so the memory viewer (in Debug mode) displays all 0xFF where I expect to find data. I wonder if someone could suggest what I am doing wrong. Here is some background information.
In my Keil uVision project I have (among other files) a C file gsData.c containing a single initialized array, like this:
uint16_t gs[] = { 38, // 0x0026 217, // 0x00d9 72, // 0x0048 0, // 0x0000 4, // 0x0004 ... }
I use a modified scatter file to divide the flash ROM into a few regions so I can place gsData.o at a specific memory address. I do this because the array gs[] contains data which needs period updating without changing the main program. My scatter file looks like this:
LR_IROM1 0x08000000 0x00100000 { ; load region size_region ER_IROM1 0x08000000 0x00060000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } ER_IROM2 0x08060000 0x00004000 { gsData.o (+RO) ; array gs[] will be located at 0x08060000 } ...
I verified from the MAP file that gs[] is placed at the location I expect:
Global Symbols Symbol Name Value Ov Type Size Object(Section) gs 0x08060000 Data 1576 gsData.o(.constdata) ... Execution Region ER_IROM2 (Base: 0x08060000, Size: 0x00000628, Max: 0x00004000, ABSOLUTE) Base Addr Size Type Attr Idx E Section Name Object 0x08060000 0x00000628 Data RO 4583 .constdata gsData.o
Now I compile and download my program to the MCU and enter debug mode in uVision IDE. I press "F5" to run the program to make sure the debugger is connected to the target and in the "Memory 1" window I enter the address 0x08060000 (alternatively the name of the symbol "gs" also works). I find the memory window shows all uninitialized flash at the address I expect to find gs[].
0x08064000: FF FF FF FF FF ...
I don't understand why I am not seeing the contents of the initialized array gs[] as defined in the C file.
Can anyone see what I am doing wrong here?
Thank you!
Thanks Clive and Per for the prompt suggestions! Here is what I've got now:
const uint16_t gs[] = { 38, // 0x0026 217, // 0x00d9 72, // 0x0048 0, // 0x0000 4, // 0x0004 ... }
I cannot use 'static' qualifier because array gs[] is defined in gsData.c which I reference in other C files as:
extern const uint16_t gs[];
Using 'static' would limit the scope of gs[] to the current file which then conflicts with 'extern'.
Adding 'const' as above did not resolve my problem so I tried to update the scatter file to use two load regions as suggested by Clive. Here is the modified scatter file:
LR_IROM1 0x08000000 0x00060000 { ; load region size_region ER_IROM1 0x08000000 0x00060000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00018000 { ; RW data = SRAM1 = 96kB .ANY (+RW +ZI) } RW_IRAM2 0x10000000 0x00008000 { ; RW data = SRAM2 = 32kB .ANY (+RW +ZI) } } LR_IROM2 0x08060000 0x000A0000 { ; load region size_region ER_IROM2 0x08060000 0x00004000 { ; 16kB region to hold gs[] gsData.o (+RO) } ER_IROM3 0x08064000 0x0001C000 { ; 112kB region to hold nn[] nnData.o (+RO) } }
I'm unsuccessful with this modification. The linker complains with:
.\Objects\SensorModule.axf: Error: L6788E: Scatter-loading of execution region ER_IROM3 to execution address [0x08064000,0x0806b3e8) will cause the contents of execution region ER_IROM3 at load-address [0x08060628,0x08067a10) to be corrupted at run-time.
The STM32L471 I am using has 1MB of flash ROM from located from address 0x08000000 to 0x08100000. I tried to "divide" the ROM into two load regions: LR_IROM1: 0x08000000 to 0x0805FFFF containing the main program and LR_IROM2: 0x0806000 to 0x0x08100000 containing the two RO regions at 0x08060000 (16kB) and 0x08064000 (112kB). These two const data RO regions contain data used by the main program which needs to be periodically updated.
I do have two array's gs[] and nn[] (defined similarly in separate C files) of const data that I want to locate at specified addresses.
Obviously the changes I've made to the scatter file are wrong. This is my first time using scatter file so I'm very inexperienced in it.
Any comments are appreciated. In the meantime I'll follow up on the above error and read more of Keil documentation and try to understand it :)
You want the load address to be the same as the execution address.
So in your case give your chip three flash regions. So two flash regions for your two blocks of absolutely located data.
As soon as you have a block of data with a different load address and execution address, the linker will think it needs to store the data at one address and later copy it to another address. And copy to flash doesn't work well at run time.
And when you play like you do, the linker can also get in a situation where it tries to copy one block of data to an address range already used for the initial storage of a different block of data. Then the linker will complain that the scatter-load process will corrupt data.
With three separate flash regions, the linker will not create any configuration to try to copy any data on startup - the flash content will already be stored at the correct address it is expected to be used from.
This wouldn't have been an issue, if your structures had had fixed size so you could just store them directly after each other. But you want to add padding between the 2 data structures to allow later copies to grow.
If I replace '2' with 'two' in the previous sentence, then Keil doesn't accept the post claiming the use of an invalid word 0x57 0x6f 0x64 which isn't even a word...
Damn Keil spam protection that now and then manages really silly false matches... And best of all - when getting the text and screenshots of the error, Keil mails back that they can't reproduce, so the spam filter seems to be nondeterministic.
Thank you, Per! That worked perfectly! :)
From your description I'm understanding the meaning of "load address" and "execution address" in this context and it starts to make perfect sense: so clear now that if these addresses aren't the same the const data will be stored in one location and copied elsewhere before execution.
The scatter loading mechanism is powerful; thanks for describing a lot of what the linker is doing when processing this file. I hope to understand it well.
Also, you are correct that I wanted to define a maximum size for the regions to allow flexibility for the const data to grow. It's only a small amount of data now (< 2kB) but I wanted to reserve a 16kB region for future expansion.
Here is the final scatter file for anyone wondering the solution:
LR_IROM1 0x08000000 0x00060000 { ; load region size_region ER_IROM1 0x08000000 0x00060000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00018000 { ; RW data = SRAM1 = 96kB .ANY (+RW +ZI) } RW_IRAM2 0x10000000 0x00008000 { ; RW data = SRAM2 = 32kB .ANY (+RW +ZI) } } LR_IROM2 0x08060000 0x00004000 { ; load region size_region ER_IROM2 0x08060000 0x00004000 { ; load address = execution address, 16kB region to hold gs[] gsData.o (+RO) } } LR_IROM3 0x08064000 0x0001C000 { ; load region size_region ER_IROM3 0x08064000 0x0001C000 { ; load address = execution address, 112kB region to hold nn[] nnData.o (+RO) } }