How to evaluate the stack usage of TI C2000 series microcontroller programs
[Copy link]
Author: Sheldon He
Abstract: Real-time controllers often have very limited memory resources, especially on-chip random access memory (RAM) resources. Whether these resources can be used reasonably and efficiently not only affects the implementation cost and performance of the entire embedded system, but also whether fatal and difficult-to-find errors will occur during system operation. This article will briefly introduce the stack of the C2000 series microcontroller (also habitually referred to as the stack, please note the difference between the heap and the stack here), and propose four methods to track or evaluate the size of the stack space required for the application to run, so as to help developers optimize the use of memory resources during the development process (especially when using C/C++ high-level languages for development) and avoid possible risks in embedded programs.
In computers, a stack is a data structure that can store a series of members and add new data to the stack or remove data from the top of the stack through "push" and "pop" operations. From the category point of view, stacks can usually be divided into two categories: software stacks and hardware stacks. The former is often implemented in the program through arrays and linked lists, while the latter is related to the computer architecture and is used to implement memory allocation and access.
This article discusses the hardware stack in the C28x core of the C2000 series microcontroller. The typical characteristics of this stack are a fixed starting address, or register reset value, and a variable stack space size specified by the compiler. The stack pointer register SP of the C28x core is a 16-bit register, and the upper 16 bits remain at 0 when in use, so it can access a 64K memory space. When the chip is reset, the content of SP becomes 0x00000400, and when in use, the stack grows from low address to high address. The legal use space of the stack is usually set by the compiler command --stack_size= size, where size is a constant that specifies the size of the stack space (in 16-bit words). The stack space must not exceed the actual non-initialized physical memory area. The stack size must not exceed the 0xFFFF range, otherwise an overflow will occur.
An embedded system software often needs to use a stack for a variety of reasons, including: storing intermediate calculation results of mathematical expressions, storing the return address of each function call during function recursion, storing local variables within a function, storing parameters passed into a function, etc. As software processes become larger and more complex, it is very important to correctly evaluate the required stack space. Allocating too much stack space will "waste" memory, and stack overflow may cause stack information loss or modify data in adjacent memory areas and eventually cause system errors.
This article summarizes four stack usage evaluation methods applicable to TI C2000 series MCUs, and recommends that readers use multiple methods for cross-verification when conditions permit to make up for the limitations of a single method. These methods are generally applicable to some other embedded products of TI, and are also of certain reference value for stack testing and evaluation of other types of embedded systems.
1. Use the XML file processing script provided by TI to generate a function call graph and perform static analysis
The stack usage can be statically analyzed through the function call relationship. TI provides a Perl-based script tool that can be used to analyze the XML files generated during the project build process to provide information related to program space usage. Here, the author needs to use the call_graph.pl script in the toolkit to generate a function call graph (Call Graph).
First, you need to download and install the toolkit from the wiki page. You can search for the keyword "Code_Generation_Tools_XML_Processing_Scripts" in the search engine.
And find the corresponding ti.com page to download and install. For readers who are not familiar with command line operations, you can use the script by following the three steps below.
1. Create a new folder and name it in English, and copy odf2000.exe from the C2000 compiler directory corresponding to CCS to the new folder. (ofd2000.exe is in C:\ti\ccs901\ccs\tools\compiler\ti-cgt-c2000_18.12.1.LTS\bin, the path will vary depending on the CCS version, CCS installation path and compiler version) At the same time, you also need to copy call_graph.exe from the cgxml tool path C:\ti\cgxml\bin, and copy the compiled .out file from the project directory to this folder.
2. Open the command line tool (you can find it by searching "CMD" in the Windows Start menu), enter the following command in it and select the folder created in the previous step as the working directory
cd C:\ti\cgxml\utils
3. Run the following script in the command line to obtain the output results. The user needs to modify the file name of the .out file to make it the same as the .out file copied to the folder in the first step.
ofd2000 -xg gpio_toggle_cpu01.out | call_graph --stack_max
At this point, the user can see the worst-case stack occupancy in the command line output. Here, function c_int00 is the root of the function call graph, and its call will occupy 48 16-bit words of stack space in the maximum case. However, there are two limitations to this result, which will be pointed out at the end of this section.
查看详情
If you use the --stack_max parameter, you can get more detailed information. For specific data interpretation methods, please refer to the document "call_graph.pdf" in the installation directory.
This method is simple and easy to use, but for indirect calls and nested interrupt services, the tool cannot directly display them in the script output. In this case, the user needs to combine the detailed information output by call_graph with his understanding of the program flow to analyze and obtain the final stack evaluation result.
2. Use callback function to capture the maximum value of stack pointer (SP) at runtime
The newer versions of the C2000 compiler support inserting callback functions during the entry and exit of a function. Developers can use the --entry_hook option to insert a section of code that reads the stack pointer (SP) at the beginning of each function and extract and compare the maximum value of the stack pointer during a certain period of program execution to obtain statistical extreme stack usage.
Take TI v18.12.2LTS Coder generation tool as an example to do a test. First, right-click to open the project properties, and find the --entry_hook setting column in "Advanced Options". Enter the function name of the callback function in the space behind it (take the function named "entry_hook" as an example).
查看详情
Then you can define the function entry_hook in the c file, in which SP_current and SP_max are pre-declared int global variables.
void entry_hook(){
SP_current = getStackPointer();
SP_max = (SP_current > SP_max) ? SP_current : SP_max;
}
A small assembly function getStackPointer() is used in this function to obtain the value of the stack pointer (SP) register. The definition of this function is:
_getStackPointer:
.asmfunc
MOV AL, SP
SUB AL, #2
LRETR
.endasmfunc
Before testing, you also need to make the following function declaration in the header file:
extern int getStackPointer(void);
After completing the settings, rebuild the project and click the "Debug" button in CCS to enter the online debugging state, and then you can run at full speed. After running for a while, open "View", "Expressions" in CCS and click the green plus sign "Add new expression" to enter the variable name SP_max to observe the maximum stack occupancy.
Sampling and counting the stack pointer through the callback function may not necessarily capture the most extreme stack usage. The actual stack consumption will be slightly larger than that measured by this method. Therefore, the author also proposed a third test method.
3. Fill the stack space with identification data to detect the usage of the stack space
The idea of method three is to pre-write the landmark data (such as (0x5A)) in a specific memory area of the stack space. After the program is executed, the data in the used stack space will be overwritten by other data, and the landmark data from the end of the stack to the lower address will remain unchanged because its memory space is not used. Of course, the premise of all this is that the stack does not overflow. By finding the maximum address of the modified array, the maximum stack size actually used by the program during this test can be determined.
This method can be used to observe the stack occupancy while connected to the emulator, or it can be used to observe the results through the "Memory Browser" after the chip is disconnected from the emulator and then connected to the emulator (by setting the target chip not to be reset when connected). The accuracy of this method depends on the coverage of the software execution.
4. Stack Monitoring Using ERAD Peripheral Module
The ERAD (embedded real-time analysis and diagnostic) module is a new peripheral of the F28004x series MCU. It is independent of the C28x core and has 8 bus comparators and 4 detection counter sub-function modules. Since this module can be accessed by both the application and the simulation tool, it can greatly increase the flexibility and convenience of debugging.
For information on how to use the ERAD module for stack monitoring, you can directly refer to the example program that comes with the TI C2000ware software package. The reference location is: C:\ti\c2000\C2000Ware_2_00_00_03\driverlib\f28004x\examples\erad\stack_overflow
Its basic working mode is to monitor the address bus and compare the address bus content with the reference value in the HWBP_REF register in a specified way (greater than, greater than or equal to, less than, less than or equal to) according to the configuration of the HWBP_CNTL register, and finally trigger the CPU to stop or generate RTOSINTn interrupt when the comparison event occurs. Through multiple applications of this method, the actual demand for stack space can be locked in a range for reference.
Summary: This article combines the actual experience of engineering development and debugging to summarize four commonly used C2000 MCU program stack space evaluation methods. It is hoped that after reading, readers will be able to choose one or more methods based on actual conditions to determine the stack requirements of the application and make reasonable configurations in the project properties to achieve maximum optimization.
|