1. Basic composition of image files
The image file load-time domain includes RO and RW segments, and the runtime domain includes RO, RW and ZI segments. The contents of RO and RW segments are the same when loading and running, but the storage space may be different, while the ZI segment is created by the initialization function when running.
RO segment: Read-Only segment, including the CODE segment in the source program and the read-only data segment (including the initialization value of the variable - it can be any variable, the initial value of the global/local, static/dynamic variable; it also includes data constants - this constant can also be global or local. In other words, the compiler must allocate storage space for the variable - the variable is readable and writable and is not placed in the RO segment, and allocate storage space for the initial value of the variable, which are two different things).
RW segment: a readable and writable segment, mainly RW-DATA, and may also have RW-CODE. RW-DATA refers to initialized global variables.
ZI segment: Zero-Initialized segment, mainly including uninitialized global variables, the compiler initializes them with 0. The data in this segment is readable and writable because it is a variable, but when the image file is loaded, no storage space is allocated for the ZI segment, although the ADS compiler's Memory map file considers Total RW Size = (RW Data + ZI Data).
2. Location of code, data and variables in image files
The above briefly summarizes the composition of each segment of the image file. From the composition of the program, it can be divided into variables, data and code. Among them, variables are divided into global/local or static/dynamic. How are their storage spaces allocated?
Code: Generally read-only, the compiler allocates storage space and places it in the RO segment of the image file.
Data: The data referred to here are all constants (if mutable, they are variables), including pointer constants, which are also read-only data and are also allocated storage space by the compiler and placed in the RO segment of the image file.
Variables: Mainly classified according to lifetime, because lifetime is defined by the survival time in memory, while scope has nothing to do with storage space allocation.
1. Global variables and static variables: including static local variables and global/static pointer variables, the compiler allocates storage space, initialized ones are placed in the RW segment, otherwise they are placed in the ZI segment;
2. Dynamic variables: mainly refer to local variables, including local pointer variables, which occupy stack space.
3. Explanation of stack initialization during startup
Heap and stack: For ARM, the heap grows upwards and the stack grows downwards.
Local variables occupy stack space (but their initialization values are data, occupying RO space);
The memory space dynamically allocated in the program, such as that allocated by malloc() and new functions, occupies the heap space.
————×The following discussion does not use the semihosting mechanism×————
Therefore, before transferring to a C application, stack space must be prepared for the C program. According to the memory resources of the specific target platform, the stack initialization function __user_initial_stackheap() needs to be ported, mainly to correctly set the addresses of the heap and stack. It can be written in C or ARM assembly language, and at least returns the heap base address (stored in R0), and the stack base address (stored in R1) is optional. Therefore, a simple __user_initial_stackheap() function written in assembly language is as follows:
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, =0x20000 ;heap base
LDR R1, =0x40000 ;stack base, optional
MOV PC, R14
For the C language implementation of this function, see reference [6] page 158.
Note that if this function is not customized in the project, then by default, the compiler/linker will treat |ImageZI
Limit| is used as the base address of the heap (i.e., the heap and stack areas are placed above the ZI area, which is also considered the standard implementation [7]). However, if the scatter file is used to implement the scatter loading mechanism, the linker does not generate the symbol |ImageZI
Limit|, then you must re-implement the __user_initial_stackheap() function and set the heap base address and stack top, otherwise an error will be reported during linking.
The stacking zone is further divided into a single zone model and a dual zone model. In the dual zone model, a stacking limit must also be set [4,6,7].
There are a few things to note when redefining the __user_initial_stackheap() function: first, do not use a stack larger than 96 bytes; second, do not affect R12 (IP, used as a temporary register for inter-process calls); third, return parameter values according to the rules (R0: heap base; R1: stack base; R2: heap limit; R3: stack limit); and fourth, keep the heap area aligned to 8 bytes [6].
In the startup code, the stack pointer of each processor mode must be initialized. This problem is easily confused with the role of the __user_initial_stackheap() function mentioned above. It can be explained from the following points:
(1) In embedded applications, the startup code is divided into two parts: one is system initialization, including the establishment of the interrupt vector table, clock, storage system initialization, key I/O port initialization, stack pointer initialization in each processor mode, etc.; the other is application initialization (or C library function initialization), including the movement of the RW segment and the clearing of the ZI segment, the establishment of the C application stack area (__user_initial_stackheap() function initializes the stack pointer), etc.
In this sense, there is no direct relationship between the two.
(2) However, the two are not unrelated. Taking the stack area of the single-region model as an example, since the stack grows downward and the heap grows upward, the stack pointer of the system mode (the same as the user mode, sharing the same R13 register to describe) actually defines the upper limit of the stack area of the single-region model in user mode, and the heap base address specified in the __user_initial_stackheap() function becomes the lower limit of the stack area.
Therefore, if the system mode (user mode) stack pointer has been initialized before, there is no need to redefine the stack base when redefining the __user_initial_stackheap() function.
4. Discussion on the content and initialization order of startup code
As mentioned above, the startup code includes two parts: system initialization and application runtime environment initialization. After the initialization is completed, the user main program can be called. References [1], [3], and [5] all list very clear but simple steps for the content and process of the two parts, which is a bit abstract for beginners.
If you do not need to use MMU for address remapping, then it is relatively easy to understand by combining the sample boot code and analysis documents that can be collected online, plus porting and debugging by yourself. If you use the processor's own Remap control register to perform address remapping, there are also relevant codes on the Internet, such as the boot code of netizen twentyone [Implementation and Analysis of 4510 Bootloader (with Source Code)], which is very clear. In addition, there is also a detailed analysis of it in the "ARM Learning Report" series of articles.
Regarding the system initialization sequence that uses MMU for address remapping during the startup process, a reference step is given in the article "Notes on Using AXD to Debug MMU Address Mapping Program (II)" and some explanations are given. By further referring to authoritative materials, here are some small improvements and corrections to the system initialization sequence as follows:
① Disable all interrupts → ② Initialize the clock → ③ Initialize the memory → ④ Initialize the stack pointer in each mode → ⑤ Initialize GPIO → ⑥ Copy the image file to SDRAM → ⑦ Create an address remapping table → ⑧ Enable MMU → ⑨ Application initialization (RW&ZI area) → ⑩ Enable abnormal interrupts → ⑾ Call the main program (dummyOS).
The main adjustment is to the order of enabling abnormal interrupts and initializing the application, that is, the application is initialized first, and then the abnormal interrupt is enabled. Please refer to [3] and [10].
......
—————————————————————————————————————
【References】
[1] ARM Architecture and Programming, Du Chunlei, Tsinghua University Press, February 2003
[2] ARM Embedded System Development: Software Design and Optimization, translated by Shen Jianhua, May 2005
[3] “Key Points of Embedded System Program Development Based on ARM”, Fei Zeping, August 2003
[4] RealView Compiler Developer's Guide, ARM Limited, January 2003
[5] "ADS Developer Guide", ARM Limited, November 2001
[6] "ADS Compilers and Libraries Guide", ARM Limited, November 2001
[7] "ADS Linker and Utilities Guide", ARM Limited, November 2001
[8] Johnny Lee, “An Introduction to MAP Files”
[9] “The Difference Between Heap and Stack”, Unknown Netizen
[10] "Embedded Software Development Using ADS1.2", ARM Limited
Previous article:S3C6410 bare metal SD card driver (SDIO mode)
Next article:ARM11 (S3C6410) CP15 Registers
Recommended ReadingLatest update time:2024-11-16 13:39
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
- VICOR engineers invite you to chat: How to improve the throughput and running time of automatic test equipment?
- The price has increased by at least 200 times! Identification of popular chip material numbers from TI, ST, Infineon, and Qualcomm
- About MSP430-Timer WDT
- Does anyone know the pin map of the YC1021 chip?
- Why Japan failed to kill South Korea's semiconductor industry
- How to select all device bit numbers or nominal values in the SCH file for AD1904 version
- How to modify the initial position of Comment when editing Altium schematic library
- Protocols and services required for the WiFi authentication process
- Fundamentals of Circuits and Analog Electronics Technology
- Need a low current charging IC and a Bluetooth chip with integrated MCU