1. About reentrant functions and simulated stacks
"A reentrant function can be called by more than one task without worrying about data corruption. A reentrant function can be interrupted at any time and can be run again after a period of time without losing the corresponding data." (Excerpt from the embedded real-time operating system uC/OS-II)
Before understanding the above concepts, we must first talk about the "overlay technology" of keilc51. (For the reason for using this technology, please see the explanation of a netizen in the appendix)
(1) Local variables are stored in the global RAM space (excluding the case of extended external memory);
(2) When compiling and linking, the local variables have been located;
(3) If there is no direct or indirect calling relationship between functions, their local variable spaces can be overwritten.
It is for the above reasons that in the Keil C51 environment, pure functions cannot be reentrant without processing (such as adding a simulation stack). For example:
In the above code, there is no direct or indirect calling relationship between TaskA and TaskB, so their local variables a and b can be overwritten, that is, they may both be located in the same RAM space. In this way, when TaskA runs for a period of time and changes a, TaskB may change b when it obtains CPU control and runs. Because a and b point to the same RAM space, when TaskA regains CPU control, the value of a has changed, causing the program to run incorrectly, and vice versa. On the other hand, func() has a direct calling relationship with TaskB, so its local variables b and c will not be overwritten by each other, but there is no guarantee that func's local variable c will not form an overwriting relationship with the local variables of TaskA or other tasks.
Based on the above analysis, we can easily determine that the two functions TaskA and TaskB are not reentrant (of course, func is also not reentrant). So how do we make a function reentrant? The C51 compiler uses an extended keyword reentrant as an option when defining a function. When you need to define a function as a reentrant function, just add the keyword reentrant after the function.
Different from the parameter passing and storage allocation method of local variables of non-reentrant functions, the C51 compiler generates a simulation stack (relative to the system stack or hardware stack) for reentrant functions, and completes parameter passing and local variable storage through this simulation stack. The simulation stack uses the global variables ?C_IBP, ?C_PBP and ?C_XBP as stack pointers (the system stack top pointer is SP). These variables are defined in the DATA address space and can be initialized in the file startup.a51. Depending on the memory mode used during compilation, the simulation stack area can be located in the internal (IDATA) or external (PDATA or XDATA) memory. As shown in Table 1:
Table 1
Note: The system stack (also called hardware stack or regular stack) of the 51 series microcontroller is always located in the internal data memory (SP is an 8-bit register and can only point to the inside), and is of the "upward growth" type (from low address to high address), while the analog stack is of the "downward growth" type.
1. Analysis of the parameter passing process of reentrant function
Before we go into the analysis, let's first briefly talk about how parameters are passed when calling a C51 function. In short, parameters are mainly passed through registers R1~R7. If no registers are available for parameters when calling or the compilation control instruction "NOREGPARMS" is used, the parameter passing will occur in a fixed memory area, which is called the parameter passing segment. Its address space depends on the memory mode selected during compilation. A maximum of 3 parameters can be passed using the working registers of the 51 microcontroller, as shown in Table 2.
Table 2
Here are two examples:
func1(int a): "a" is the first parameter, passed in R6, R7;
func2 (int b, int c, int *d): "b" is passed in R6 and R7, "c" is passed in R4 and R5, and "*d" is passed in R1, R2 and R3.
As for which registers or methods the function's return value is passed through, I will not go into details here. You can refer to the relevant documents or books on C51.
Okay, let's start analyzing a simple program. The code is as follows:
The program is very simple, without further ado, let's take a look at what the assembly language translated into by c51 looks like (large XDATA in large storage mode).
Note: The simulated stack pointer is initially initialized to 0xFFFF+1 in startup.a51; from the above assembly code, it can be seen that the parameters are scanned from right to left.
Next, let's look at the assembly code of fun: (It's very long, please be patient and read it, some of it can be skipped)
[page]
Note: The simulation stack structure is as follows
Next, we will explain two key sub-functions C_ADDXBP and C_XBPOFF
Finally, it’s the end, let me explain the key points~~~
The simulation stack grows downwards, and C_XBP is initially equal to 0xffff+1, so please see the following sentence
In fact, it is like this: adding 0xffff is equivalent to subtracting 1, adding 0xfffe is equivalent to subtracting 2, and adding 0xfffd is equivalent to subtracting 4. . . . . . There is no need to explain why :)
Conclusion:
After several days of research, I finally wrote a summary report, which can be regarded as a small achievement of my own. Mistakes are inevitable. I hope to discuss problems with you and make progress together.
References:
1. Xu Aijun, Peng Xiuhua, "Single-Chip Microcomputer Advanced Language C51Windows Environment Programming and Application", Electronic Industry Press, 2001
2. Peng Guanghong, construct a real-time operating system for 51 single-chip microcomputer.
appendix:
In other environments (such as PC, such as ARM), the problem of function reentrancy is generally not a problem that needs special attention. As long as you do not use static variables, or pointers to static variables, in general, functions are naturally reentrant.
But C51 is different. If you don't design your function specially, it is not reentrant.
The reason for this difference is that the local variables of a function in a general C compiler (or more precisely, in a C compiler based on a general processor) are stored on the stack, while those in C51 are stored in an overlayable (data) segment.
As for the reason why C51 does this, it is not to save memory as some people say. In fact, it does not save memory at all. The reasons are as follows:
1) If a function func1 calls another function func2, then the local variables of func1 and func2 cannot be in the same memory. C51 still has to allocate different RAM for them. This does not save memory compared to using the stack.
2) If func1 and func2 are not in the same call chain, C51 can use coverage analysis to make their local variables share the same memory address. However, this will not save memory more than using the stack. Because since they are in different call chains, when one function is running, the other function must not be in its lifetime, and the stack it occupies has been released and returned to the system.
The real reason (the reason why C51 uses the overlay segment as the storage place for local variables) is:
The 51 instruction set does not have an efficient relative addressing (indexed addressing) instruction, which makes using the stack as a variable too expensive.
The general practice of using the stack to store variables is:
When entering a function, a stack space is reserved as a storage space for variables, and a register that can be used as a base address is used to point to this space. By adding an offset, different variables can be accessed.
For example: MOV EAX, [EBP + 14]; X86 instruction
LDR R0, [R12, #14]; ARM instruction
All of these can solve this problem very well.
But 51 lacks such instructions.
*In fact, there are still two variable addressing instructions in 51, but they are not suitable for accessing local variables on the stack.
MOVC A, @A+DPTR
MOVC A, @A+PC
Therefore, C51 has a special keyword: reentrant to solve the problem of function reentry.
Previous article:Design and development of LED lighting controller based on DMX512 protocol
Next article:Car audio voice control system based on UniSpeech-SDA80D51
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
- Pointer variables as function parameters
- Transistor one-button switch circuit
- Analysis and application of key technologies of BLE low-power Bluetooth
- Power-saving Mode of ADI AD5933 Impedance Converter
- "New Concept Analog Circuit" - Signal Processing Circuit, Yang Jianguo's new book
- How to use the IP of Video On-Screen Display in zynq
- STM32F103 brushless DC motor control program
- [GD32E231 DIY Contest] Part 2: Dynamic QR Code Display
- EEWORLD University ----PI power chip: Learn about BridgeSwitch in one minute
- CCS compilation optimization and volatile