Cortex-M4 Startup Example Code - ARM® DS-5™
This example shows the bare-metal startup code for the Cortex-M4 processor, including vector table, SysTick timer, interrupt handler, and MPU initialization, all written in C, and is illustrated by a simple semihosted "Hello World" and example sorting application.
Purpose and scope
This example shows the bare-metal startup code for the Cortex-M4 processor, including vector table, SysTick timer, interrupt handler, and MPU initialization, all written in C, and is illustrated by a simple semihosted "Hello World" and example sorting application.
This is a simple example for the purposes of illustrating features of the DS-5 Debugger. For real-world Cortex-M4 software development, the Cortex Microcontroller Software Interface Standard (CMSIS) is recommended to be used as a ready-made starting point instead.
Hardware and software requirements
A ARM/Keil Microcontroller Prototyping System, with its FPGA configured as a Cortex-M4 (MPS-M4) and a suitable power supply for it.
RVI or DSTREAM debug hardware and a suitable power supply for it, and a JTAG cable to connect it to MPS-M4
This example is intended to be built with the ARM Compiler. If you wish to modify and rebuild the example, you must use the ARM Compiler to rebuild it.
This example is intended for running on MPS-M4, but could be easily ported to other Cortex-M4 platforms.
A ready-made launch configuration startup-Cortex-M4-MPS-example.launch is provided.
The MPS_remap.ds script is executed during this debug connection, to remap memory on the MPS so that RAM appears at address 0x0.
Building the example
This example can be built with the ARM Compiler using the supplied Eclipse project, or directly on the command line with the supplied makefile.
The Eclipse project is a managed builder project, that creates a makefile in the /Debug directory.
The executable builds with a base address 0x0, and is intended for running in RAM on MPS-M4, but could be easily ported to other Cortex-M4 platforms simply by changing the code and data addresses in the scatter-file scatter.scat.
This example depends on semihosting support being provided by the debug system.
DS-5 Debugger enables semihosting automatically if either symbols __auto_semihosting or __semihosting_library_function are present in an image. The ARM Compiler 5.0 adds __semihosting_library_function automatically to an image at link time if that image uses any semihosting-using functions. If compiling with gcc or an earlier release of armcc, use an alias symbol definition such as void __auto_semihosting(void) __attribute__ ((alias("main")));
Building on the command-line
To build the example on the command-line with the supplied make utility:
Then navigate to the ...\startup_Cortex-M4 directory, and type:
make
The usual make rules: clean, all and rebuild are provided in the makefile.
Building from Eclipse
To build the supplied Eclipse projects:
In the Project Explorer view, select the project you want to build.
Select .
Running the example
Power-up the MPS-M4
Select Debug Configurations from the Run menu.
Select the startup-Cortex-M4-MPS-example from the list of DS-5 Debugger configurations.
In the Connections panel, enter the USB: or TCP: IP address or name of your RVI/DSTREAM unit in the Debug Hardware Address field,
or click on Browse to select one from a list, otherwise an error will be reported:
The connection has not been configured. Unable to connect to the system specified in configuration file.
Click on Debug to start debugging. The example image will be downloaded to the target and it will
run to the main() function.
Debugging requires the DS-5 Debug perspective. If the Confirm Perspective Switch dialog box opens, click on
Yes to switch perspective.
-
Run the executable (press F8). printf() output appears in the App Console. You should see:
Cortex-M4 bare-metal startup example
Calculating using the Cortex-M4's FPU
Float result should be 80.406250
Float result is 80.406250
Insertion sort took 21 clock ticks
Shell sort took 14 clock ticks
Quick sort took 14 clock ticks
SysTick interrupt 1
SysTick interrupt 2
SysTick interrupt 3
SysTick interrupt 4
SysTick interrupt 5
SysTick interrupt 6
Exploring the example
First disconnect any existing debug session, then power-cycle the target, and load the executable in the same way as before.
DS-5 Debugger will download the program's code and data sections to the target, and run to main().
-
The vector table and default exception handlers are coded in C in exceptions.c. The vector table is placed first at the start of the image at address 0x0 as specified in the scatter-file scatter.scat, by creating a named ELF section with #pragma arm section then placing that section +FIRST in the first (root) execution region in the scatter-file. The first two entries in the vector table are the Initial Stack Pointer and the Initial Program Counter. The Initial SP comes from the top address of the ARM_LIB_STACKHEAP region, as a linker-generated symbol Image$$ARM_LIB_STACKHEAP$$ZI$$Limit for the (default) One Region model. The Initial PC is the address of the Reset handler, which in this example is the C run-time entry point __main() that performs scatter-loading and all C run-time library initialization necessary before reaching main(). The SysTick handler is in timer.c. Default exception handlers to trap all exceptions (other than SysTick and Reset) in an endless loop are provided too. Note that unlike classic ARM processors, the Cortex-M family of processors do not have branch instructions in their vector tables, but have function pointers instead. A typedef for ExecFuncPtr for the function pointers in the exception table makes this explicit. These function pointers are all pointers to Thumb code, so will have their Least Significant Bits set to indicate Thumb. Open a Memory view at address 0x0 to check the addresses and that they all (except for the Initial SP) have their LSBs set.
-
The main() function in main.c first tells the processor the location of the vector table, obtained from the scatter-file, by writing its address to the Vector Table Offset Register. This needs to be done early in case some later actions trigger an exception. main() then displays a welcome message with a semihosted printf(), initializes the MPU, and performs a floating point calculation using either the processor's optional FPU or the software floating point library. main() then starts the SysTick timer (see later), initializes the Process Stack Pointer, changes Thread mode to Unprivileged and to use the Process Stack. Single-step from main() through this code, using the Variables view to watch the values in registers changing. main() then runs the main application (sorts), then sits in an endless loop, waiting for interrupts. The MPU initialization function SCS_init() in scs.c is a minimal example to show what can be done with the Cortex-M4's optional MPU. This is for illustration only - it can be removed without affecting the rest of the example.
-
The example sorting application in sorts.c is a simple example of data manipulation using standard C library functionality. clock() timing and printing of results is performed via semihosting. The value of N is limited to 200 to avoid excessive stack usage in compare_sorts due to the local arrays, because RAM is typically limited Cortex-M4 systems.
-
The SysTick Timer is initialized by SysTick_init() in timer.c. This is a minimal implementation of a count-down timer with an auto-reload value, that triggers a SysTick interrupt when it reaches zero. The tick period depends on the clock frequency and reload value. timer.c also contains the SysTick exception handler SysTickHandler() that is called from the vector table when a SysTick interrupt occurs. You can trap SysTick interrupts for debugging by placing a breakpoint on this function. Use the Registers view to see the SysTick registers and to watch the timer value changing. Use the Variables view to watch the values of ticks counting up.
Known issues and troubleshooting
Eclipse reports Unresolved inclusion for system headers such as stdio.h if it cannot locate the
header files for a project. You can resolve this by right-clicking on the project in the Project Explorer view and then selecting
Properties... from the context menu. In the dialog box, select:
and then add
the path to the ARM Compiler headers on your host workstation. For example, C:\Program Files\DS-5\include.
Select both Add to all configurations and Add to all languages, click on
Apply so that Eclipse can rebuild the index.
See also:
Copyright© 2012 ARM Limited. All Rights Reserved.