Ads1.2 embedded software development (zt)

Source: Internet
Author: User
Overview
Embedded applications are usually debugged and developed in a prototype environment, which is not exactly the same as the final product. Therefore, it is very important to consider the running status of applications in the final target hardware during system debugging.
This article aims to discuss how to transfer an embedded application in a development/debugging environment to a target system that ultimately runs independently, it also mentions some features of the ARM ADS1.2 Development Kit and their roles in this process.
When using ADS to develop embedded programs, you need to consider the following questions:
1. Use of hardware-related C language library functions;
2. Some C language library functions use resources in the debugging environment and redirect these resources to the hardware in the target system;
3. Memory ing of executable image files must be cropped Based on the memory distribution of the target hardware;
4. Before the main program is executed, the embedded application must initialize the system. A complete initialization includes the user's startup and Execution Code and the initialization process of the C library function in ADS.
Figure 1 Semihosting implementation example
Figure 2 C Language Library Function Structure
Figure 3 default memory ing
Figure 4 connector layout rules Default Project Settings
At the beginning of an embedded application software development, ADS users may not fully understand some parameter indicators of the target hardware. For example, some details about peripherals, memory address distribution, and even the processor type may not be finalized yet. To develop software before all these details are ready, the ADS tool has a set of default settings for program building and debugging. Understanding the default project setting method is very helpful for grasping the final porting steps.
ADS1.2C function library
Semihosting
In the C language function library of ADS, some ANSIC functions are provided by the debugging environment of the host. This mechanism is called Semihosting. Semihosting is implemented through a set of software interrupt (SWI) commands. 1. When a Semihosting Soft Interrupt is executed, the debugging system first identifies the SWI request, then suspends the running program, calls the Semihosting service, and resumes the original program execution. Therefore, the tasks executed by the host are transparent to the program.
C Language Library Function Structure
In terms of concept, C language library functions can be divided into two parts: one is part of the ANSIC language specification itself, and the other is only supported by a specific ANSIC level, as shown in 2.
Some ANSIC functions are completed by calling driver-level functions in the host debugging environment. For example, the library function printf () of ADS outputs the output information to the console window of the debugger. This function is implemented by calling _ sys_write (), __sys_write () A Semihosting Soft Interrupt service program that outputs the string to the host console is executed.
Default memory ing
If the user does not specify the memory ing distribution of the image during program compilation, ADS will allocate a default memory ing map for the generated target code and data, as shown in figure 3.
The target impression is connected to the address 0x8000, and the storage and execution areas are all located in the space starting from this address. The RO (read-only) part is placed in front, followed by the RW (read/write) part, and the ZI (zero initialization) part.
HEAP is followed on the ZI part, so the exact HEAP address can be determined only during connection.
The base address of the STACK is provided by a Semihosting operation when the application starts. The address value returned by this Semihosting operation depends on the debugging environment:
ARMulator returns the Setting Value in the configuration file peripherals. ami. The default value is 0x08000000.
Multi-ICE returns the value of $ top_of_memory In the debugger. The default value is 0x00080000.
Connector layout rules
The connector allocates code and data in the memory system according to a set of rules, as shown in figure 4.
The image is first arranged in the order of RO-RW-ZI by attributes, and the code in the same attribute is prior to the data. The connector then sorts the input segments in alphabetical order of their names. The names of the input segments are the same as the block names in the assembly code (use the AREA keyword in the assembly program ). In the input segment, the order of code and data placement from different objects is the same as the order of object Files specified in the connector command line.
We recommend that you do not simply rely on these rules when you need to flexibly allocate code and data placement. The following describes how to control the code and Data Layout in Scatterloading.
Figure 5 default ADS initialization process

Figure 6 function redirection in library C
Figure 7 scatter File Syntax
Figure 8 simple example of Distributed Loading Start the application
Most embedded systems have an initialization process before entering the application main program, which completes the system startup and initialization functions. The default ADS initialization process is shown in step 5.
In general, the initialization process can be divided into two parts:
_ Main is responsible for setting the running image memory ing;
_ Rt_entry initializes the library function.
_ Main completes code and data copying and clears the ZI data zone. This step makes sense only when the code and data areas are in different storage locations during storage and runtime. Next, _ main jumps into _ rt_entry to initialize the STACK and HEAP. Finally, _ rt_entry jumps into the main () Entry of the application (). When the application is executed, _ rt_entry returns the control to the debugger.
Function main () has extraordinary significance in ADS. When a program project contains main (), the connector connects the initialization code in _ main and _ rt_entry. If the main () function is not used, the initialization process will not be connected, as a result, some standard C library functions are invalid.
Cut C-database functions according to the target environment
By default, the C library function uses the Semihotsting mechanism to implement the device-driven function. However, a real embedded system uses peripherals or hardware to run independently of the host environment.
C library function redirection
You can define your own C-language library functions. These new functions are automatically used by the connector during connection. This process is called the function of redirecting the C language library, as shown in figure 6.
For example, a user has an I/O device (such as UART ). The original library function fputc () Outputs characters to the debugger control window, but the user changes the output device to the UART port, so that all () the printf () Series Function outputs of the function are redirected to the UART port.
The following is an example of implementing fputc () redirection:
Externvoidsendchar (char * Ch );
Intfputc (intch, file * F)
{/* E. g. writeacharactertoanuart */
Chartempch = CH;
Sendchar (& tempch );
Returnch;
}
In this example, the input character is simply redirected to another function sendchar (). sendchar () is assumed to be another defined serial output function. In this place, fputc () seems to be an abstraction layer between the target hardware and Standard C library functions.
Disable semihosting in C language library functions
In an independent embedded application, the semihostingswi operation should not exist. Therefore, you must ensure that semihosting is not used in all the called library functions. To ensure this, you can introduce a symbolic keyword _ use_no_semihosting in the program:
In the C code, use # prgrama # pragmaimport <_ use_no_semihosting_swi> 〉
In the assembler, use import
Import_use_no_semihosting_swi
In this way, when a library function using the SWI mechanism is connected, the connector reports an error:
Error: symbol_semihosting_swi_guardmultiplydefined
To determine which function is used, enable the-verbose option during connection. In this way, the library function will have a tag of _ I _use_semihosting_swi when the result information is output.
Loadingmembersys_wxit.ofromc_a_un.1.
Definition: _ sys_exit
Reference: _ I _use_semihosting_swi
You must define these functions as your own execution content.
Note that the connector can only report the semihosting called in the library function, and no error is reported for the semihosting used in the User-Defined Function. Memory ing customized based on target hardware
Scatlerloading)
In the actual embedded system, the default memory ing provided by ADS cannot meet the requirements. Your target hardware usually has multiple memory devices located in different locations, and these memory devices may have different configurations during program loading and running.
Scattertoading can specify different locations of a piece of code or data in the memory during loading and running through a text file. The scatterfile is specified by the-scatter switch in the command line. For example:
Armlink_scatterscat.scffilel.ofile2.0
In scatterfile, you can specify different storage region addresses for each code or data zone during loading and execution. The storage block of scatlertoading can be divided into two types:
Load area: The Storage Area of the application when the system starts or loads.
Execution zone: the memory zone for application execution and data access after the system starts. The system can have one or more execution blocks during real-time operation.
All the code and data in the image have a loading address and an operational address (the two may be the same or different, depending on the situation ). When the system starts, the _ main initialization code in the C function library performs the necessary copying and resetting operations to transfer the corresponding code and Data Segment of the application from the loading status to the execution status.
1. scatter File Syntax
The scatter file is a simple text file that contains some simple syntax.
My_Region0x00000x1000
{
Thecontextofregion
}
Each block is defined by a header title. The header contains at least the block name and start address, and the maximum length and other attribute options. The content of the block definition is included in a pair of curly braces, depending on the specific system situation.
A loading block must contain at least one execution block. In practice, multiple execution blocks are usually used.
An execution block must contain at least one code or data segment. These are usually from target files such as source files or library functions; the wildcard Number * matches the remaining part of the specified attribute item that is not defined in the file.
2. Simple decentralized loading example
In the example shown in figure 8, there is only one loading block, which contains all the code and data. The starting address is 0. The loaded block corresponds to two execution blocks. One contains all the RO code and data, and the execution address is the same as the loading address. The other starting address is 0x10000, which contains all RW and ZI data. In this way, when the system starts to start, it starts to run from the first execution block (the execution address is equal to the loading address). During the execution, some initialization code transfers part of the code in the loading block to another execution block.
The following is the scatter description file, which describes the above memory ing method.
LOAD_ROM0x4000
{
EXE_ROM0x00000x4000; Rootregion
{
* <+ RO>; Allcodeandconstantdata
}
RAM0x100000x8000
{
* <+ RW, + ZI>; Allnon-constantdata
}
}
3. place objects in scattered files
In most applications, all attributes are not put together in a simple way like metadata. users need to control the location of specific code and data segments. This can be achieved by defining a single target file in the scatter file, rather than simply relying on wildcards.
To override the standard connector layout rules, we can use the + FIRST and + LAST Distributed Loading commands. A typical example is to place the interrupt vector table at the beginning of the execution block:
LOAD_ROM0x00000x4000
{
EXEC_ROM0x00000x4000
{
Vectors. o <Vect, + FIRST> 〉
* <+ RO> 〉
}
; Moreexecregions...
}
In this scatter file, the Vect field in vextors. o is saved to address 0x0000.
4. RootRegion (root zone)
The root zone is an execution block, and its loading address is consistent with the execution address. Each scatter file must have at least one root zone. Distributed Loading has one restriction: the code and data (that is, the code and data that have been copied and cleared) of the execution block cannot be copied to another location. Therefore, the root zone must contain the following parts:
_ Main. o, including the code for copying code/data;
Connector output variables $ Table and ZISection $ Table, including the address of the copied code/data.
Because the attributes of the preceding two parts are read-only, they are matched by the * <+ RO> wildcard syntax. If * <+ RO> is used in a non-root zone, you must explicitly specify another RO region in the root zone.
The following is an example:
LOAD_ROM0x00000x4000
{
Exe_rom0x00000x4000; rootregion
{
_ Main. O <+ RO>; copyingcode
* <Region $ tabl0e>; RO/rwaddressestocopy
* <Zisection $ table>; ziaddressestozero
}
Ram0x100000x8000
{
* <+ RO>; allotherrosections
* <+ RW, + Zi>; allrwandzisections
}
}
Stack and heap placement
The scatterloading Mechanism provides a method to specify the code and static data layout. The following describes how to place the application stack and heap.
* _ User_initial_stackheap redirection
The Application Stack and heap are established during the C library function initialization process. You can rewrite the corresponding subroutine to change the location of the stack and heap. In the library function of ads, that is, the _ user_initial_stackheap () function.
_ User_initial_stackheap () can be implemented using C or assembly. It must return the following parameters:
R0: heap base address;
R1: Stack base address;
R2: Maximum heap length (if needed );
R3: Stack length limit value.
When using the distributed load function, you must re-call _ user_initial_stackheap (). Otherwise, the connector reports an error:
Error: l6218e: Undefined symbol image $ Zi $ limit (Referred From sys_stackheap.o)
* Memory Model
ADS provides two real-time storage models. The default value is one-region. The application stack and heap are located in the same storage block and grow in the opposite direction. When allocating a memory space in the heap area, you need to check the stack pointer. Another scenario is that the stack and heap use two separate storage areas. For RAM with extremely fast speed, you can choose to use it only for stacks. To use this two-region model, you need to import the use_two_region_memory symbol. To use heap, you need to check the length limit of heap.
By default, the growth of stacks is not checked for both models. You can use the-apcs/swst compiler option for software stack check during program compilation. If the two-region model is used, a stack limit value must be specified when _ user_initial_stackheap is executed.
Figure 9 redirection _ user_initial_stackheap ()
Figure 10 basic initialization process
Figure 11 ROM/RAM redirection and ing
Table 1 System reset and initialization
In the current situation, it is generally assumed that the program is executed from the initialization entry _ main of the C library function. In fact, all embedded programs must perform system-level initialization during startup. This topic is discussed here.
Initialization Process
Figure 10 shows the basic initialization process of an ARM-based embedded system. We can see that a reset processing module reset handler is added before _ main, Which is started immediately when the system is powered on and reset. The new code block marked as $ sub $ main is executed before the main program.
The reset processing module reset handler is usually a small piece of assembly code that is executed during system reset. It completes at least the stack initialization of all processor modes used in the application. For a kernel containing a local memory system (such as an ARM Kernel containing a cache), the configuration must also be completed during the initialization process. After the system initialization is completed, the program usually jumps to _ main to start the initialization process of the C library function.
The system initialization process generally includes other content, such as interrupt enabling and so on, which are mostly executed after the initialization of the C library function is complete. $ Sub $ main () to complete this function.
Vector table)
All ARM systems have an interrupt vector table. When an exception occurs, the vector table must be called. Generally, the vector table must be at 0.
Table 2
Table 3
Table 4
Table 5
Table 6
Table 7
Table 8
Table 9
Table 10
Memory Configuration
* ROM/Ram redirection
When the system is started, non-volatile memory is required to ensure that the correct startup code exists at address 0.
A simple method is to allocate an address starting with 0 x to ROM. The disadvantage is that the ROM Access speed is much slower than that of RAM. When the execution interrupt response needs to jump from the interrupt to the table, the system performance will be compromised. At the same time, the vector table content in the ROM cannot be dynamically modified by the user program.
Another feasible solution 11 is shown. ROM is located at the beginning of address 0x1000, but is mapped to address 0x0000 by the memory controller when the system is reset. In this way, after the system starts, the ROM is displayed at address 0x0000. The system executes the boot code in this ROM and the boot code jumps to the real ROM address, and let the memory controller remove the address ing to ROM. At this time, the memory at the address 0x0000 is restored to RAM. The code in _ main copies the vector table to RAM at 0x0000, so that the table can be correctly responded to exceptions.
Table 1 provides an example of ROM/RAM redirection and ing in ARM assembly. It is based on ARM's Integrator platform and applies to all platforms similar to ROM/RAM redirection methods. The first command redirects from the ROM ing address (0x00000) to the real address. The instrUCt_2 address is the actual ROM address (0x180004 ). Then, the ROM address ing is removed by setting the corresponding control registers on the Integrator platform. The code is executed once the system is started. All operations on address redirection/ ing must be completed before the C-library function initialization.
* Local Memory Configuration
Many ARM processors have on-chip memory systems, such as cache and tightly coupled memory (TCM), memory Governance Unit (MMU), or memory Protection Unit (MPU ). These devices must be correctly configured during system initialization and have special requirements.
As we can see from the previous article, the C library function initialization code in _ main is responsible for the memory system settings when the program is running. Therefore, the entire memory system must complete initialization before _ main. For example, MMU or MPU must complete configuration in reset handler.
The initialization of tightly coupled memory (TCM) must also be completed before _ main (usually before MMU/MPU), because the general program needs to scatter the code and data into TCM. It is worth noting that when TCM is enabled, it no longer accesses the memory blocked by TCM.
For cache consistency, if the cache is enabled before _ main, when _ main is used to copy code and data from the loading area to the execution area (because commands and data are essentially treated as data processing during the copy process ), the command appears in the data buffer. To avoid this problem, you can enable the cache after the C library function Initialization is complete.
* Scatter loading and memory configuration
Whether it is through ROM/RAM redirection or MMU configuration, assuming the storage distribution is inconsistent during startup and runtime, the definition in the scatterloading file must be based on the memory distribution after system redirection.
The preceding ROM/RAM redirection is used as an example:
LOAD_ROM 0x10000 0x8000
{
EXE_ROM 0x10000 0x8000
{
Reset_handler.o (+ RO, + FIRST)
...
}
RAM 0x0000 0x4000
{
Vectors. o (+ RO, + FIRST)
...
}
}
The LOAD_ROM in the load area is placed at 0x10000, representing the address for Loading Code and data after redirection. Stack Initialization
A stack pointer must be defined for all the processor modes that may be used in the program.
In Table 2, the stack is located in the address identified by stack_base. This symbol can be a direct address in the memory system, or it can be defined in another assembly file, which is defined by the scatter file. Table 2 code allocates a 256-byte stack for each FIQ and IRQ modes. You can allocate a stack for other modes in the same way. The simplest way is to enter the corresponding mode and specify the corresponding value for the SP register. To use the software stack check, you must specify a stack length limit value.
The value of the stack pointer and stack restriction will be automatically passed to the initialization code _ user_initial_stackheap of the c library function as a parameter. These values should not be modified in _ user_initial_stackheap.
Hardware initialization $ sub $ main ()
In general, all system initialization code should be separated from the main application, but there are several exceptions, such as cache and interrupt enabling, which must be executed after the C library function initialization.
Table 3 code shows how to use $ sub and $ supper. The connector replaces the Call main () function with call $ sub $ main (), completes cache and interrupt enabling, and finally jumps to main ().
Execution Mode considerations
It is very important to select a processor Execution Mode for the main application, depending on the system initialization code.
Many functions used during the startup process, such as MMU/MPU configuration and interrupt enabling can only be performed in privileged mode. If you need to run your own applications in privileged mode, you only need to change to the corresponding mode before exiting the initialization process. There is no other problem.
If the user mode is used, you must ensure that all functions executed in privileged mode are completed before entering the user mode. Because the system mode and user mode use the same register group, reset handler should exit from system mode, and _ user_initial_stackheap completes Application Stack initialization in system mode. In this way, after the processor enters the user mode, all the stack spaces have been correctly set. Further consideration of memory Layout
Allocate hardware addresses in the scatter File
Although code and data layout can be described in a scatter file, the peripheral registers, stacks, and heap configurations in the target hardware are still directly set in the program source code using the hardware address. Assuming that all information related to the memory address is defined in the scatter file to avoid referencing absolute hardware address in the source file, it is of great benefit to the Engineering Management of the program.
* Define the target peripheral address in the scatter File
Generally, the address of the peripheral register is defined in the program file or header file. You can also declare a structure type pointing to the peripheral register. The structure address is located in the scatter file.
For example, the target timer has two 32-bit registers, which can be mapped in Table 4. To place the structure on the specified memory address, create a new execution zone (see table 5 ). The scatter file locates the timer_regs structure in the address 0x40000000.

Note: During the startup process, the content of these registers does not need to be cleared. Changing the register content may affect the system status. Adding the uninit attribute to the execution area can prevent the Zi data from being cleared during initialization.
Allocate stacks and heap in the scatter File
In many cases, using scatter files to define the stack and heap addresses brings some benefits, mainly including: all memory allocation information is concentrated in one file; you only need to reconnect the stack address and heap address without re-compiling.
* Explicitly place symbols
In the ads1.2 environment, this is the simplest method. Two symbols stack_base and heap_base are referenced in the previous article. These two symbols are created in the assembly module and located in the respective execution areas of the scatter file (see table 6 ).
In table 7, the heap base address is located on 0x20000, And the stack base address is located on 0x40000. Now the location of heap and stack can be easily edited.
* Use the symbols produced by the connector
In this method, you must specify the heap and stack length in the target file. This weakens the two advantages described at the beginning of this section.
First, define the heap and stack length in the Assembly source program. The keyword SPACE is used to retain a piece of memory SPACE, and NOINT can prevent the zeroing operation (see table 8 ). Note that the address label is not required in the source file of this location.
Then these parts can be located in the corresponding execution zone of the scatter file (see table 9 ). The symbols generated by the connector point to the base address and length limit of each execution zone. These symbols can be used by the redirection code called by _ user_initial_stackheap. Using DCD in Code to define more meaningful names for these values can enhance code readability (see table 10 ).
The file locates the heap base address in 0x15000, And the stack address in 0x4000. The Heap and stack locations can be easily changed by editing the address of the corresponding execution zone.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.