Some products, when the system is reset (not power-on reset), may require to keep the data in RAM before reset, so as to quickly restore the site, or to avoid restarting the site equipment due to instant reset. However, by default, any form of reset in Keil MDK will clear the non-initialized variable data in RAM area. How to set non-initialized data variables not to be initialized by zero is what this article is going to discuss.
Before giving the method, let's first understand the storage rules and properties of code and data, and why the RAM where non-initialized variables are located is initialized to zero by default after reset.
What are initialized data variables and what are non-initialized data variables? (Because my text description may not be accurate, I like to give some examples to help understand the text.)
Define a variable: int nTimerCount = 20; variable nTimerCount is an initialized variable, which means it has an initial value;
If you define the variable: int nTimerCount; the variable nTimerCount is a non-assigned variable, and Keil MDK will put it in the input section with the attribute ZI by default.
So, what is "ZI" and what is "input section"? This requires understanding the composition of ARM image files. This part is a bit boring, but I think it is very necessary to master it.
The composition of ARM image files:
An image file consists of one or more regions.
Each domain contains one or more output sections.
Each output section contains one or more input sections
Each input section contains the code and data in the target file.
The input segment contains four types of content: code, initialized data, uninitialized storage area, and storage area with content initialized to zero. Each input segment has corresponding attributes: read-only (RO), readable and writable (RW), and initialized to zero (ZI).
An output segment contains a series of input segments with the same RO, RW, and ZI attributes. The output segment attributes are the same as the input segment attributes contained in it.
A domain contains one to three output segments, and the attributes of each output segment are different: RO attribute, RW attribute and ZI attribute
At this point we can know that, in general, the code will be placed in the input section of the RO attribute, the initialized variables will be assigned to the RW attribute input area, and the "ZI" attribute input section can be understood as a collection of variables initialized to zero.
Where will the initial value of the initialized variable be placed in the hardware? (For example, if we define int nTimerCount = 20, where will the initial value 20 be placed?) I think this is an interesting question. For example, after Keil is compiled, it will give information about the size of the compiled file, as shown below:
Total RO Size (Code + RO Data) 54520 ( 53.24kB)
Total RW Size (RW Data + ZI Data) 6088 ( 5.95kB)
Total ROM Size (Code + RO Data + RW Data) 54696 ( 53.41kB)
Many people don't know how this is calculated, nor do they know how much code is actually put into ROM/Flash. In fact, those initialized variables are put into the input section of the RW attribute, and the initial values of these variables are put into ROM/Flash. Sometimes these initial values are large, and Keil will compress them before putting them into ROM/Flash to save storage space. Who restores these initial values to RAM and when? Who initializes the RAM where the variables in the ZI attribute input section are located with zeros and when? To understand these things, you need to see what Keil does for you under the default settings, from system reset to executing the main function you wrote in C code.
After the hardware is reset, the first step is to execute the reset handler. The entry of this program is in the startup code (by default). Here is an excerpt of the reset processing entry code of cortex-m3:
1: Reset_Handler PROC; PROC is equivalent to FUNCTION, indicating the beginning of a function, as opposed to ENDP?
2:
3: EXPORT Reset_Handler [WEAK]
4: IMPORT SystemInit
5: IMPORT __main
6: LDR R0, =SystemInit
7: BLX R0
8: LDR R0, =__main
9: BX R0
10: ENDP
After initializing the stack pointer and executing the user-defined low-level initialization code (SystemInit function), the next code calls the __main function, which calls a series of C library functions to complete the copying, decompression and zero initialization of code and data. Data decompression and copying include copying the initial values of initialized variables stored in ROM/Flash to the corresponding RAM. For a variable, it may have three attributes. Variables modified with the const modifier are most likely to be placed in the RO attribute area, initialized variables will be placed in the RW attribute area, and the remaining variables will be placed in the ZI attribute area. By default, the zero initialization of ZI data will initialize all ZI data areas to zero. This is done by the compiler "on its own" before the program executes the main function of the C code after each reset. So if we want to set some variables in the C code not to be zero-initialized after reset, we must not let the compiler "do whatever it wants". We need to use some rules to constrain the compiler.
Scatter-loading files are crucial for linkers. In scatter-loading files, using UNINIT to modify an execution section can prevent __main from zero-initializing the ZI data of the section. This is the key to solving non-zero initialized variables. Therefore, we can define a data section modified by UNINIT, and then put the variables that we want to initialize to non-zero in this area. So, we have the first method:
1. Modify the scatter-loading file and add an execution section named MYRAM. The execution section starts at 0x1000A000 and is 0x2000 bytes (8KB) long. It is modified by UNINIT:
1: LR_IROM1 0x00000000 0x00080000 { ; load region size_region
2: ER_IROM1 0x00000000 0x00080000 { ; load address = execution address
3: *.o (RESET, +First)
4: *(InRoot$$Sections)
5: .ANY (+RO)
6: }
7: RW_IRAM1 0x10000000 0x0000A000 { ; RW data
8: .ANY (+RW +ZI)
9: }
10: MYRAM 0x1000A000 UNINIT 0x00002000 {
11: .ANY (NO_INIT)
12: }
13: }
So, if you have an array in your program that you don't want to be zero-initialized after reset, you can define the variable like this:
unsigned char plc_eu_backup[PLC_EU_BACKUP_BUF/8] __attribute__((at(0x1000A000)));
The variable attribute modifier __attribute__((at(adder))) is used to force the variable to be located at the address of adder. Since the 8KB area ZI variable starting at address 0x1000A000 will not be zero-initialized, the array plc_eu_backup in this area will not be zero-initialized either.
The disadvantage of this method is obvious: you have to allocate the address of the variable yourself. If there is a lot of non-zero initialized data, it will be an unimaginable large project (future maintenance, addition, modification of code, etc.). So you have to find a way to let the compiler automatically allocate the variables in this area.
2. Scatter loading files is the same as method 1. If you still define an array, you can use the following method:
unsigned char plc_eu_backup[PLC_EU_BACKUP_BUF/8] __attribute__((section("NO_INIT"),zero_init));
The variable attribute modifier __attribute__((section("name"), zero_init)) is used to force the variable to be defined in the name attribute data section. Zero_init means that the uninitialized variable is placed in the ZI data section. Because the "NO_INIT" explicitly named custom section has the UNINIT attribute. (The simplest method is strongly recommended)
3. How to initialize all non-initialized variables in a module to non-zero?
If the module name is test.c, modify the scatter-loading file as follows:
1: LR_IROM1 0x00000000 0x00080000 { ; load region size_region
2: ER_IROM1 0x00000000 0x00080000 { ; load address = execution address
3: *.o (RESET, +First)
4: *(InRoot$$Sections)
5: .ANY (+RO)
6: }
7: RW_IRAM1 0x10000000 0x0000A000 { ; RW data
8: .ANY (+RW +ZI)
9: }
10: RW_IRAM2 0x1000A000 UNINIT 0x00002000 {
11: test.o (+ZI)
12: }
13: }
The following method is used when defining:
int uTimerCount __attribute__((zero_init));
Here, the variable attribute modifier __attribute__((zero_init)) is used to place uninitialized variables in the ZI data section. In fact, by default in Keil, uninitialized variables are placed in the ZI data area.
Previous article:STM32 drives FM24CL16
Next article:STM32 study notes - serial port interrupt receiving indefinite data buff
Recommended ReadingLatest update time:2024-11-16 14:27
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- The problem of interference between ADC channels of STM32
- Where does the chip's power come from?
- EEWORLD University Hall ---- Zhou Gong Series Lectures —— CAXA Electronic Chart Example
- 8266 WiFi module obtains time and weather information through the network
- Performance Differences Between DC-DC Switching Power Supplies and DC-DC Integrated Modules
- Power Issues
- BlueNRG2 sends 247 bytes at a time
- AUTOSAR’s popularity in automotive electronics software development
- Understand the perfect combination of RPA and AI in one article
- X-nucleo-iks01A3 application--using Lis2DW12 accelerometer to calculate angle