A brief analysis of keilc51 reentrant functions and simulated stack

Publisher:SereneDreamsLatest update time:2012-05-30 Source: 21ic Keywords:keilc51 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

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:

program

program

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

According to the memory mode used at compile time

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

Use the working register of 51 MCU to pass up to 3 parameters

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:

program

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).

program

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)

program [page]

program

program

Note: The simulation stack structure is as follows

Simulating stack structure

Next, we will explain two key sub-functions C_ADDXBP and C_XBPOFF

program

program

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

program

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.

Keywords:keilc51 Reference address:A brief analysis of keilc51 reentrant functions and simulated stack

Previous article:Design and development of LED lighting controller based on DMX512 protocol
Next article:Car audio voice control system based on UniSpeech-SDA80D51

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号