First of all, the system stack of 51 (also called system stack, or hardware stack) is the stack pointed to by SP. It is a full-increment stack (Note 1) located in the 128 bytes of on-chip RAM. What is the initial value of the system stack pointer SP after power-on? This needs to be analyzed from the startup file of 51. There is such assembly code in the startup file:
?STACK SEGMENT IDATA ; define an on-chip data segment, segment name: ?STACK
RSEG ?STACK; Select a previously defined relocatable segment ?STACK, the following assembly statements will be placed in this segment until the next segment location instruction is encountered, such as CSEG/RSEG.
DS 1 ; Reserve storage area command. The declaration first occupies one byte of space. During compilation, this reserved space will not be used by other variables. The meaning here is to allocate 1 byte to the hardware stack (actually this is problematic, more space should be reserved for the hardware stack)
besides:
MOV SP,#?STACK-1
As can be seen above, SP is initialized to #?STACK-1. At the #?STACK address, the DS instruction reserves N bytes of space, which is the space for the hardware stack.
However, in the code of the startup file, DS 1 is equivalent to reserving only 1 byte for the hardware stack, which will actually cause problems for the following reasons: there will be multiple data segments in the on-chip RAM. You only need to use the XX SEGMENT IDATA instruction to declare a data segment XX in the on-chip RAM. If multiple data segments are declared in the entire project program, the ?STACK data segment is just one of the many data segments in the on-chip RAM. If only 1 byte is reserved for the ?STACK segment, and there are other data segments behind the ?STACK data segment, then our hardware stack will only have 1 byte. Once an interrupt occurs, the CPU register is automatically pushed into the stack, which immediately causes a stack overflow. After the overflow, the memory of other variables is stepped on, and the program basically crashes. For this problem, keil handles it this way: keil always links the ?STACK data segment as the last data segment in the on-chip RAM during the linking stage. Even if we only reserve 1 byte for it, it doesn't matter. Anyway, there are no other variables occupying the segment, as long as SP does not exceed 0X7F (the upper limit of the on-chip RAM address). By observing .m51 (map file), we found that keil did put the ?STACK data segment at the end of the on-chip RAM. The following is an excerpt of a map file generated by a 51 project:
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0002H UNIT ?C?LIB_DATA
IDATA 000AH 000DH UNIT ?ID?UCOS_II
0017H 0009H *** GAP ***
BIT 0020H.0 0000H.1 UNIT ?BI?SERIAL
0020H.1 0000H.7 *** GAP ***
IDATA 0021H 0041H UNIT ?STACK ; Author's note: This is the line!
* * * * * * * X D A T A M E M O R Y * * * * * * *
XDATA 0000H 080EH UNIT ?XD?SERIAL
XDATA 080EH 0804H UNIT ?XD?MAIN
XDATA 1012H 0490H UNIT ?XD?UCOS_II
XDATA 14A2H 005CH UNIT _XDATA_GROUP_
To avoid insufficient system stack, a safer way is to use the assembly instruction DS to reserve more space for the ?STACK data segment. In the above 51 project, 40H bytes are reserved for the ?STACK data in another assembly file, so there are 41H bytes in total. The advantage of doing this is that stack errors can be checked during the compilation and linking stage. For example: Suppose there are many data segments in the on-chip RAM, so that, in addition to the ?STACK data segment, there are only 2 bytes left in the on-chip RAM, and the ?STACK data segment we only use the default configuration in the startup file to reserve one byte, so there is no problem with the compilation, keil compiles it, but during the operation, the system stack has only 2 bytes, and it is certain that the stack overflow will occur in a few minutes, and then crash; Suppose there are many data segments in the on-chip RAM, so that, in addition to the ?STACK data segment, there are only 2 bytes left in the on-chip RAM, and if we use the DS instruction to allocate 40H bytes to the ?STACK data segment, keil will find that the 51 chip RAM is insufficient and report an error when compiling, and it cannot be compiled, thus helping us find stack problems during the compilation and linking stage.
Continuing with the above question, what is the initial value of SP after reset? SP is equal to 0X07 after reset, but it is immediately changed by the startup file through the statement MOV SP, #?STACK-1. Therefore, when entering the main function, the value of SP is the value modified by the startup file, that is, #?STACK-1 (Note, it is easy to understand, here -1 is the characteristic of full stack increase), then what is the value of #?STACK? Look at the above assembly statement?STACK SEGMENT IDATA, this statement declares the?STACK segment as a relocatable segment, that is, the first address of the?STACK segment (#?STACK) can only be determined when the compiler links the program, that is, the value of #?STACK is automatically allocated by the compiler during linking, and is not allocated during the compilation stage. Still taking the above excerpted map file as an example, we find that the starting address of the?STACK segment is 0021H, that is, #?STACK is equal to 21H.
The simulation stack is used by Keil when it generates reentrant functions for 51 (the function can be made reentrant by qualifying it with the keyword REENTRANT). For STM32 , the default generated function (a function that does not contain global variables and static local variables) is reentrant, while the function generated by Keil for 51, even if the function does not contain global variables and static local variables, Keil will not assemble the function into a reentrant one by default. I think Keil mainly takes into account the lack of on-chip RAM of 51. Without external RAM, if the function is compiled as reentrant, the execution of the reentrant function needs to occupy a certain amount of stack space (especially the long call chain generated by the nested calls of reentrant functions, which requires more stack).
Reentrant functions need to use a stack during execution, so where is the stack used by 51's reentrant functions? Is it the system stack pointed to by SP? The answer is: No. Here is the explanation:
When we expand the 51 with a large external RAM, we don't have to worry about the problem of insufficient RAM. However, there is still a problem. The system stack pointer SP can only address 0~7FH, a total of 128 bytes of space. Reentrant functions are definitely not allowed to be compiled to use the system stack. Otherwise, even if the RAM is expanded, this external RAM cannot be used by the system stack, and the external RAM is meaningless. Therefore, Keil has created the concept of a simulation stack for 51. Keil declares a 1 or 2-byte variable as a stack pointer in the startup file. The name and size of this stack pointer vary depending on the compilation mode. Similar but different, take the large compilation mode (Note 2) as an example, in the large compilation mode, the programmer needs to manually set the XBPSTACK constant in the startup file to 1, so that the conditional compilation used in the startup file will reference a 2-byte simulation stack pointer? C_XBP, because Keil uses the simulation stack as a full minus stack, so this simulation stack pointer? C_XBP is initialized to the maximum value of the off-chip RAM address plus 1. If we connect a 64K off-chip RAM, the maximum address of the RAM is 0XFFFF, then the stack pointer? C_XBP is initialized to 0XFFFF 1 = overflow is 0x0000. Let's take another example of a small compilation mode. The small compilation mode is used for 51 without external RAM expansion. In this way, 51 can only use the 128 bytes of RAM from 0 to 127 on the chip (part of this 128 RAM is Rn, etc., leaving even less RAM for the program). In the small compilation mode, the simulation stack pointer generated by Keil for 51 is named ?C_IBP, and the programmer needs to manually set the IBPSTACK constant to 1. The initial value of the pointer ?C_IBP is initialized to the maximum address of the available RAM (127) plus 1, that is, 0x7f 1. For the differences in stack processing between small compilation mode, compact compilation mode, and large compilation mode, please refer to this article and click to open the link. If the link is down, you can search for it yourself: "Keil Mode Settings and Programming Matters".
Note 1: Full increment stack, full means SP always points to the address of the last byte pushed into the stack, increment means SP gets bigger each time something is pushed into the stack. Correspondingly, there are empty increment stack, empty decrement stack, and full decrement stack, empty means SP always points to the address of the next free position in the stack.
Note 2: How to select the large compilation mode: Take keil5 as an example, select -> Magic Wand -> Target tab, select Large:var... for Memory Model, and select Large for Code Rom Size....
Appendix: Here is an example of a possible trap that may occur when using a non-reentrant function. Suppose there are two functions as follows, the first one is reentrant and the second one is not reentrant.
int add5_re(char a1,char a2,char a3,char a4,char a5) REENTRANT
{
int sum;
sum=a1 a2 a3 a4 a5;
return sum;
}
int add5(char a1,char a2,char a3,char a4,char a5)
{
int sum;
sum=a1 a2 a3 a4 a5;
return sum;
}
We refer to the .m51 file for the parameters and local variable allocation of these two functions, which are as follows (the comments after the semicolon are added by the blogger himself):
[plain] view plain copy------- PROC _?ADD5_RE
x:0002H SYMBOL a1 ; Note that the address number is preceded by a small x, indicating that a1 is allocated to the simulation stack
x:0003H SYMBOL a2
x:0004H SYMBOL a3
x:0005H SYMBOL a4
x:0006H SYMBOL a5
------- DO
x:0000H SYMBOL sum
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
------- PROC _ADD5
D:0007H SYMBOL a1 ;R7
D:0005H SYMBOL a2 ;R5
D:0003H SYMBOL a3 ;R3
X:14ABH SYMBOL a4; Note that the address number is preceded by a large X, indicating external RAM
X:14ACH SYMBOL a5
------- DO
D:0006H SYMBOL sum ;R6
We found that the formal parameters and local variables a1/a2/a3/sum in add5 are allocated to Rn, and a4/A5 is allocated to the absolute address of external RAM xdata. If we call the add5 function in both the main call chain and the interrupt function, an error will occur. Suppose an interrupt occurs when add5 is executed in the main call chain, and the interrupt function is switched to execute add5. Then a1/a2/a3/sum in the main call chain is allocated to Rn, and the register BANK is switched when the interrupt is entered, so that a1/a2/a3/sum in the main call chain is not destroyed and is spared. However, because a4/a5 is allocated to the absolute address, after the interrupt executes add5, a4/a5 of add5 in the main chain will definitely be destroyed!!
For the reentrant add5_re function, the above-mentioned destruction situation will not occur even if it is called by the main call chain and the interrupt at the same time, because all the parameters and local variables of add5_re are defined in the simulation stack (see the above code comments). Using the add5_re function in the main call chain will apply for stack space, and add5_re will apply for new stack space when interrupted.
It should also be noted that because Keil uses overlay technology when compiling 51 programs (the parameters and local variables of different functions can share the same absolute memory unit in time-sharing), this may also cause traps. Suppose there is such a situation: the local variable b of a function func2() is allocated to the address 14ABH of absolute xdata after compilation, and shares memory with the a4 variable of add5 mentioned above. In this case, even if { func2() is only called in interrupts, and func2() is not called in the main call chain}, and { add5 is only called in the main call chain, and add5 is not called in interrupts}, there will be problems. The reason is obvious. If an interrupt occurs during the execution of add5, the variable a4 in add5 will be destroyed after the variable b is used in the interrupt. The reason is that the function generated by the shared address compilation method will not be destroyed as long as it is called in time-sharing, but the interruption causes the time-sharing mechanism to be destroyed, resulting in simultaneous calls.
Conclusion: The function used in the interrupt is either reentrant or all the local variables of the function are exclusive memory units.
Previous article:Summary of experience in using 51 single-chip microcomputer timer
Next article:Summary of 51 MCU Memory
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
- How to choose the right inductor for RF circuits
- msp430f169 bluetooth fan adjustable speed program
- Design of HDB3 Codec Based on FPGA
- EEWORLD University ---- Working Principle of Diode
- How do you know what power dissipation you need to choose for a diode in a circuit?
- Intel FPGA 2019 Engineer Application Video
- AD drawing PCB has many white lines after enlarging
- AD Ignore DRC with irregular slotting settings using pad stacking
- High frequency characteristics of resistors
- Problems with dynamic load line locus of transistor switches