How to view the stack allocation in keilc51

Publisher:Chunjie2022Latest update time:2015-07-14 Source: 51heiKeywords:keilc51 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
 Keil C is a very good C51 compiler, probably the best C51 compiler, providing various optimization modes, and doing a very good job of optimizing variables and arranging addresses. This is one of the advantages of writing code in C language. If you write in assembly language, you have to spend a lot of time to arrange the physical memory addresses for each variable, and you have to always remember which memory units have been allocated. Newly added variables cannot occupy those allocated units to avoid memory overlap conflicts and overflows. I have always trusted the compilation results of Keil C51. In my impression, its memory allocation is perfect. As long as the code does not report any warnings or errors when it is compiled, there will be no memory conflicts or overflows when the code is running.

But what happened today proved me wrong.

  I have a product code on hand, and the code volume is very large. The program does not run well, so I plan to optimize the code. The larger the code volume, the more areas that can be optimized. For 8051, the memory access speed of the data area (0~7FH) inside the chip is the fastest. Direct access can read and write with one instruction. Although the idata area (80H~FFH) is still a memory area, it can only be accessed indirectly because the address allocation overlaps with the special register SFR. It can only be read and written with two instructions, and the speed is slightly slower. The external memory xdata area (0~7FFFH) must be accessed using the DPTR pointer, and the speed is the slowest. Obviously, the principle of optimization is to try to prioritize the variables that are frequently read and written in the data area, then the idata area, and finally the xdata area.

   
 


 


  8051 has 4 physical memory spaces

   On-chip ROM and off-chip ROM. On-chip RAM and off-chip RAM.

   The address space of the off-chip program memory ROM is 64kB, and the off-chip data memory RAM also has a 64kB addressing area, which overlaps with the ROM in address.

   The 8051 microcontroller uses different signals to select ROM or RAM. When fetching instructions from the external ROM, the selection signal PSEN is used, and when reading and writing data from the external RAM, the selection signal PSEN is used.

   The read RD and write WR signals are used to select, so there will be no confusion due to address overlap. [page]

On-chip data memory RAM

     The on-chip RAM has 256 bytes, of which the address space from 00H to 7FH is the direct addressing area, and the address from 00H to 1FH in this area is the working register area.

There are 4 groups of working registers, each group is R0~R7. At a certain moment, the CPU can only use any one group of working registers, which is determined by the program status word.

Determined by the status of RS0 and RS1 in PSW.

The address units 20H to 2FH of the on-chip RAM are bit addressable areas, where each bit of each byte specifies a bit address.

In addition to section operations, bit operations can also be performed.

The address space 80H to FFH of the on-chip RAM is the special function register SFR area. For the 51 series, 21 special function registers are arranged in this area. For the 52 series,

26 special function registers are arranged in this area, and 128 bytes of indirect addressing on-chip RAM are expanded at the same time, and the address is also 80~FFH, overlapping with the SFR area address.

 

After I finished manually optimizing the variables, I set the compilation mode to SMALL. In this way, the C51 compiler will automatically prioritize the variables that I did not manually specify to be stored in the data area. If they exceed the valid address range, it will report an error, so I can rest assured. After pressing the rebuild all button, the compiler prompts:

Program Size: data=236.2 xdata=19321 code=43372

"ipphone_main" - 0 Error(s), 0Warning(s).

The data area prompted by the compiler includes idata. According to past experience, the data area has 256 bytes, and the program has only used 236.2 of them, leaving 19 bytes without overflow. The xdata area has 32k, and only 19k has been used, which is far from overflow. The compilation results are all normal.

After burning the code into the chip and running it, the result was unexpected. From the phenomenon, it restarted automatically about 1 second after power-on, and restarted again 1 second after the restart, which was a very regular restart.

I didn't suspect that it was the compiler. My first thought was that it was the watchdog. The code turned on the watchdog after power-on. Maybe some subroutine code took too long to execute and the watchdog was reset. So I inserted the watchdog feeding code in the suspected place, recompiled and tested it again, and it still restarted automatically. So I simply commented out the watchdog code and didn't use the watchdog, thinking that there would be no problem this time. Unexpectedly, it still restarted.

I thought about it carefully and there are not many reasons that can cause the 8051 to restart. One is the restart caused by the watchdog, which can be ruled out. Second, some 8051s support restart instructions. Although the one I use supports it, I have never used that instruction, so this can also be ruled out. Third, the 8051 is strongly interfered with, and the content of the instruction fetch register PC is changed to 0, so it restarts. This can also be ruled out, because if there is strong interference on site, it should restart before optimization.

Since I couldn't figure out the reason, I started to tinker with it, restoring the optimized variables one by one to the unoptimized state, and retesting each time I restored them. Finally, when I restored a 16-byte array, I found that the program was normal. After a closer look, I found that the program was completely normal when the array was defined in the xdata area, but it was reset when it was defined in the idata area. Although it was strange that the compiler did not report a memory overflow when it was defined in the idata area. Tracing the assembly instructions did not find any abnormalities. Whether it was defined in idata or xdata, the addresses assigned by the compiler to the array proved to be valid addresses, and there was indeed no overflow. The compiler's arrangement was still correct.

Although I haven't found the root cause, since the problem is in the memory, I decided to check the memory allocation when the array is specified as idata type. Keil C51 will output an M51 file when compiling. This file contains a lot of memory allocation information, which is very detailed, including which variable is assigned to which memory address by the compiler, how many bytes it occupies, which variables are local variables, and which variables can be reused... This M51 file has a detailed list.

Looking down the variable allocation addresses in the list, everything is correct. I was amazed at how accurately the compiler arranged the variable allocation. But when I saw the arrangement of the last stack pointer, I finally found the problem. It is arranged like this:


  TYPE        BASE       LENGTH      RELOCATION      SEGMENT NAME
  ----------------------------------------------------------------------------------------------
  IDATA       0080H     0034H          UNIT                      _IDATA_GROUP_
  IDATA       00B4H     0022H          UNIT                      ?ID?IPPHONE_MAIN
  IDATA       00D6H     001FH          UNIT                      ?ID?DNS_NICRCV?IPPHONE_DNS
  IDATA       00F5H      0004H          UNIT                      ?ID?DISP
  IDATA       00F9H      0001H          UNIT                      ?STACK

The segment marked with STACK is the stack allocation. The data above shows that the SP stack pointer is arranged at the address F9H, and the stack space is 1 byte! On the surface, there is no overflow, but my program uses interrupt service. When entering the interrupt service, at least 8 bytes of stack space (to save R0~R7 registers) are required to protect the scene. 8051 uses an incremental stack design. The stack pointer is often arranged in the available part of the memory space at the back. Every time a byte is pushed, the SP pointer is increased by 1. When entering the interrupt service, at least 8 bytes are pushed, F9H+8, which exceeds FFH. The stack pointer cannot exceed FFH, which means the stack overflows! It turns out that this is the reason why the program keeps restarting, not the variable memory overflow, but the stack overflow!

When I specify the array as xdata type, since the array no longer occupies the idata area, IDATA suddenly has 16 bytes of available space. The recompiled M51 is arranged like this:

  IDATA      0080H      0024H           UNIT                      _IDATA_GROUP_
  IDATA      00A4H      0022H           UNIT                      ?ID?IPPHONE_MAIN
  IDATA      00C6H      001FH           UNIT                      ?ID?DNS_NICRCV?IPPHONE_DNS
  IDATA      00E5H      0004H           UNIT                      ?ID?DISP
  IDATA      00E9H      0001H           UNIT                      ?STACK

From this set of data, we can see that the SP pointer is arranged at the address E9H, and the stack space has FFH-E9H+1=23 bytes, which is enough for the program, so the program runs normally.

The compilation results of adjusting the variable type multiple times show that C51 does not calculate the required size of the stack space. Any code is allocated based on the requirement of only 1 byte of stack space (this is obviously nonsense in my opinion. It is impossible for a slightly complex subroutine call to complete the field protection with only 1 byte). Since the stack can only be allocated in the data area and idata area, when a program occupies too much data area for optimization, although the compiler can compile successfully, the SP stack pointer is often allocated at the end of the data area, which can easily cause insufficient stack space and overflow. To be on the safe side, it is best to ensure that the compiled SP value is arranged before F0H, so that there are at least 16 bytes of stack space to maximize the guarantee that the program will not run away.

It seems that we cannot trust Keil C51 too much. After compiling, we will have to check M51 to ensure the quality of the program. I don't know if this is a bug of Keil C51.

Keywords:keilc51 Reference address:How to view the stack allocation in keilc51

Previous article:51 MCU voice + temperature control source code
Next article:AT89S52 and DS18B20 digital temperature sensor testing completed

Recommended ReadingLatest update time:2024-11-16 14:59

s3c2440 stack allocation (fl2440 bare metal stack)
//2440INIT.S ;The location of stacks UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~ SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~ UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~ AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~ IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~
[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号