Use of Cortex-M startup code
The previous article talked about the principles of the startup code. Understanding the principles is meaningless if you don’t use them. Understanding the startup code is not the ultimate goal. Our goal is to deeply understand the underlying principles of the Cortex-M series MCUs and apply them to actual products to accelerate development and improve product stability. Now let’s take a look at the specific application of the startup code based on my actual experience.
The essence of the startup code is a section of code that initializes the vector table, completes scattered loading, and initializes the C language runtime environment before the program enters the user code (main function). It can be said that all the work you need to do before entering the user code (main function) can be done here, and some codes are put into the startup code to complete, which will be much better than completing them at the beginning of the user code and main function.
1. How to call functions in startup code? And what kind of functions cannot be called in startup code?
Because the main work of the default standard startup code is completed in Reset_Handler, the calling function is generally also here.
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR r0, =SystemInit
BLXr0
LDR r0, =__main
BX
ENDP
The above code shows how to import and call the SystemInit() function in Reset_Handler. First, import the label of the SystemInit() function.
IMPORT SystemInit
Then call the function, of course, this is done in assembly language
LDR r0, =SystemInit
BLXr0
Those who don't understand can take an assembly course by themselves. So the above two steps can be used to call C language functions in the assembly language of the startup code:
(1) Import function label;
(2) Call this function;
Of course, there are a few things to note here. Not all functions can be called in assembly language, because __main has not yet run, the C language runtime environment has not been fully built, and the stack has not been initialized, so please note:
(1) The number of parameters of a called C function cannot exceed 4. It is OK not to use it, but if you do use it, the number of parameters cannot exceed 4. The reason is that in the Cortex-M system MCU, the 1-4 parameters of the function will be pushed into the 4 general registers R0-R3 (Cortex-M series MCU, whether M0, M3 or M4, there are only 16 general registers. For the internal register structure, please refer to the ARM official white paper). If there is a fifth parameter, this parameter will be pushed onto the stack. However, because __main has not yet run and the stack has not been initialized, if the function has more than 4 parameters, the program will run away.
(2) Do not write the function you need to call after __main, because it is meaningless and the program will not run there;
2. What kind of functions or features are suitable to be started or executed in the startup code? Can't they be executed in the main() function? What is the difference?
The key to this problem is __main (not the main function but __main. The difference between the two is discussed in detail in the previous article). To put it bluntly, the biggest difference between the startup code and the main() function or user code is whether it is before or after __main.
Below I list several typical cases where it is better to execute in the startup code, i.e. before __main, than in the main() function, i.e. after __main:
(1) FPU (floating point co-processing unit) initialization:
To be precise, this is the code that must be executed in the startup code, that is, before __main. FPU is only available in Cortex-M4 & Cortex-M7 and the newly released Cortex-M33, but not in Cortex-M3 & Cortex-M0/M0+. The reason is that the floating-point library of floating-point C will be initialized in __main. If the floating-point hardware is not initialized before this, the program will directly hang and run away. Of course, you can also choose not to use FPU. There will be this option in the compiler options of the development environment, that is, do not enable FPU, so you don’t have to initialize FPU, and there will be no problem with the program. But as long as you use FPU, you must initialize FPU before __main. Taking LPC54608 as an example, the FPU initialization code is integrated in the SystemInit function. I have also used MCUs with separate functions to initialize FPU. The code is basically provided in the manufacturer's SDK, and you only need to call it;
(2) Off-chip SRAM/SDRAM initialization:
Many experienced developers immediately jumped up and said, "I used off-chip SDRAM before, and I initialized it in the main() function, and there was no problem. Don't talk nonsense here." The reason is this: it depends on the situation, whether you want to put part of the RW/ZI data segment into the off-chip SRAM/SDRAM for initialization, that is, whether you need to distribute the loading process to your off-chip SRAM/SDRAM. If you do, you need to call the initialization function of the off-chip SRAM/SDRAM before starting the code __main; if not, it doesn't matter, you can initialize the off-chip SRAM/SDRAM in the main function.
You may ask, under what circumstances do we need to scatter-load to the off-chip SRAM/SDRAM? (Scatter-loading will be discussed in several articles later, so let's go over it here first.) In fact, there are many situations, and we will discuss scatter-loading in detail later. Here is an example, still taking LPC54608 as an example. The situation is the same as other Cortex-M MCUs. LPC54608 has 200KB of SRAM on the chip, and the largest continuous SRAM is 128KB. The other 72KB is not continuous with the 128KB SRAM in terms of address. This is a detail (you should also pay attention to the continuity of the address when choosing an MCU). For example, I want to open a 150K BUFFER to cache the data in the middle of my program. It seems that the 200KB SRAM on the LPC54608 chip can achieve this. 150KB BUFFER, but in reality it is not. The compiler cannot allocate this BUFFER across addresses, that is to say, the actual maximum single BUFFER allocated is 128KB, but you want 150KB, so there is no way. You need to expand SDRAM externally, because this BUFFER is used by the compiler to allocate space and initialize, so it will inevitably be calculated as the RW/ZI data segment, so this data segment needs to be scattered loaded, which requires putting the initialization code of the external SDRAM before __main, so that __main will know that there is an SDRAM space outside the chip that needs to be initialized (this also requires the cooperation of the scattered loading script file, which will be discussed in detail in the article later). There are also other situations where SDRAM needs to be initialized in the startup code, so I personally recommend that the initialization of the external SRAM/SDRAM should be placed in the startup code, that is, before __main, because this is basically correct, and there will be many situations that cannot be covered if it is placed in the user code.
(3) Initialization of watchdog switch, low voltage detection, etc.:
Some manufacturers' MCUs will turn on the watchdog in the chip's BOOTROM, which means the watchdog is turned on as soon as the power is turned on. Some manufacturers do not do this, and the watchdog needs to be turned on and off according to the application. The core issue here is the running time of __main. Taking this factor into consideration, I strongly recommend putting the watchdog, low voltage detection and other functions in the startup code, that is, before __main to initialize them.
__main's running time? What? What the hell? Yes, this is a very important invisible factor. The running time of __main is determined by your user code. Remember the function of __main mentioned in the previous article?
Initialize the stack
Initializing the C Library
Scatter loading
Therefore, using MacroLib will be faster, using standard C Lib will be slower, and distributing loading content will be slower, and vice versa will be faster. Of course, it is also related to the main frequency of the MCU. The higher the main frequency, the faster the speed. The running time of __main is about 100-200 microseconds to tens of milliseconds. So if there are some bugs in the program during this period, such as you call several initialization functions before __main and there are bugs in them, or the voltage drops when __main is running, the MCU will respond to these emergencies, such as resetting, or protecting important data, etc., to increase system stability. Because if your program is more complicated, __main may run for dozens of milliseconds. For MCU, dozens of milliseconds is a very long time, especially when your product shipments are large, various small probability events will be magnified by usage into large quality problems, so the running safety of the MCU during this period also needs to be considered. So I suggest that the structure of the startup code is as follows, putting the watchdog & low voltage detection at the front:
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT LVDInit
IMPORT WDTInit
IMPORT OtherInit
IMPORT __main
LDR r0, =WDTInit ; Watchdog initialization
BLX r0,
LDR r0, =LVDInit ; Low voltage detection initialization
BLXr0
LDR r0, =SystemInit ; System initialization
BLXr0
LDR r0, =OtherInit ; Other initialization you need
BLXr0
LDR r0, =__main
Previous article:ARM Cortex-M Low-Level Technology (IV) Writing Your Own Startup Code
Next article:The difference between main() and _main() in Arm
Recommended ReadingLatest update time:2024-11-17 01:32
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!
- Rambus Launches Industry's First HBM 4 Controller IP: What Are the Technical Details Behind It?
- 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
- PSpice simulation integral circuit output problem
- What are the wireless transmission technologies for the Internet of Things?
- What's the logic behind this circuit?
- Live Review: TI Ultrasonic Gas Flow Measurement Innovation Solution + CC13X2/CC26X2
- YX009K-8D switch RGB colorful control example
- What a mess it is to have a 0.1uf capacitor with a problem {Complain about this here}
- Wuhan plague, the possibility of third, fourth, fifth and sixth generation infection cases
- [RISC-V MCU CH32V103 Evaluation] Design of Light Intensity Recorder (and Final Report)
- Fudan Micro FM33LC046N Review + RTC Clock
- PCB impedance control experience sharing