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!
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) } }