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