ARM Cortex-M underlying technology (III) Use of startup code

Publisher:Bby1978Latest update time:2020-01-16 Source: eefocusKeywords:ARM Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

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

[1] [2]
Keywords:ARM Reference address:ARM Cortex-M underlying technology (III) Use of startup code

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

Naming conventions for cross-compilation toolchains
Naming convention The naming convention for the cross-compilation toolchain is: arch Depending on whether the operating system is supported or not, ARM GCC can be divided into those that support the operating system and those that do not. arm-none-eabi: This does not have an operating system, so it cannot support
[Microcontroller]
Naming conventions for cross-compilation toolchains
ARM Flow Light C Language
#include"2440addr.h" //Defines the addresses and names of various special function registers. This header file must be called to use them./*=== If this header file is not called, the definition name and address statements to be used must be declared before the main program. #define rGPBCON (*(volatile unsigned *)0x560
[Microcontroller]
ARM Flow Light C Language
Research on DVB-S2 Set-Top Box Solution Based on ARM9
1 Introduction With the development of electronic technology, computer technology and information technology, the digitization of television systems is an inevitable trend. According to media reports, the United States will stop broadcasting analog television and fully implement digital television in 2010. Altho
[Microcontroller]
Research on DVB-S2 Set-Top Box Solution Based on ARM9
ARM commonly used pseudo instructions
1. AREA  The AREA directive is used to define a code segment or a data segment.        Syntax format:         AREA segment name attribute 1, attribute 2, ...         If the segment name starts with a number, the segment name must be enclosed in “|”, such as |1_test|.         The attribute field indicates the relevant
[Microcontroller]
ARM9_S3C2440 learning (V) norflash startup, nandflash startup, SDRAM summary
The first instruction read when S3C2440 starts is at 0x00, which is divided into starting on nand flash and nor flash.   NAND flash: suitable for large-capacity data storage, similar to hard disk; nor flash: suitable for storing small-capacity programs or data, similar to a small hard disk; sdram: Mainly used for prog
[Microcontroller]
ARM7-based city fire alarm intelligent monitoring network
1 Introduction The automatic fire alarm and fire linkage system (hereinafter referred to as: fire host) is now widely used in various buildings and structures, and fully demonstrates the characteristics of timely detection of fire and rapid extinguishing of initial fires. However, the communication protocols of var
[Microcontroller]
ARM7-based city fire alarm intelligent monitoring network
Embedded bar shearing production line CNC system based on ARM
  With the rapid development of my country's manufacturing industry, the requirements for machining accuracy and production efficiency of machined parts are getting higher and higher, and the degree of automation of the production process of enterprises is also very high. Bars are the main raw materials for the produc
[Microcontroller]
Embedded bar shearing production line CNC system based on ARM
ARM S3C2410 watchdog setting principle and source code
S3C2410 watchdog has only two functions 1. As a regular clock, it can generate interrupts 2. Used as a watchdog timer, when the clock decreases to 0 (timeout), it will generate a clock signal of 128 clocks (PLCK).   Watchdog settings: 1. The external clock source of the watchdog is provided by PLCK. PLCK generat
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号