SoC
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
  • Arm Cortex M3
  • Contact
Reading: Cortex M0: How to Make the Default crt0.o Startup for GCC
SUBSCRIBE
SoCSoC
Font ResizerAa
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Search
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Have an existing account? Sign In
Follow US
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
© S-O-C.ORG, All Rights Reserved.
Arm

Cortex M0: How to Make the Default crt0.o Startup for GCC

Neil Salmon
Last updated: October 5, 2023 9:58 am
Neil Salmon 8 Min Read
Share
SHARE

The Cortex-M0 is one of ARM’s most basic and widespread microcontroller cores. As an ultra low power 32-bit chip optimized for cost-sensitive and power-constrained embedded applications, Cortex-M0 can be found in a vast range of IoT and wearable devices. To develop software for Cortex-M0, understanding how to build the correct startup code and initialization routines is essential.

Contents
Understanding the Role of crt0.oStack Pointer InitializationConfiguring the Interrupt Vector TableCalling the main() FunctionTarget Hardware InitializationBuilding the Default crt0.oCustomizing crt0.oLinker File ConfigurationDebugging Startup CodeConclusion

When using GCC as the toolchain, the crt0.o object file implements the standardized C runtime initialization and must be customized for each specific Cortex-M0 target. This involves configuring the vector table, stack pointer, hardware initialization, and calling the main() function. With the right startup code in place, you can then leverage the full capabilities of GCC to build efficient C/C++ applications for your Cortex-M0 chip.

Understanding the Role of crt0.o

The crt0.o object performs several key tasks during startup:

  • Sets up the initial stack pointer value
  • Initializes static C++ objects
  • Calls constructors for global C++ objects
  • Calls the main() function
  • Implements the default interrupt vector table
  • Performs any target-specific hardware initialization

Without a properly configured crt0.o, your Cortex-M0 application will fail to start correctly. The good news is that once you understand the basic initialization steps, creating a custom crt0.o is straightforward.

Stack Pointer Initialization

One of the first things crt0.o must do is set the initial stack pointer value on startup. On Cortex-M0, this is done by populating the value into the Main Stack Pointer (MSP) register which points to the master stack used by Thread mode code.

In GCC, the stack pointer location is indicated using the following syntax: .word _estack

Where _estack is a symbol that points to the end of the stack memory region reserved for your Cortex-M0 application.

Configuring the Interrupt Vector Table

Cortex-M0 supports Nested Vectored Interrupt Controller (NVIC) for managing hardware interrupts. This is enabled by populating a vector table with addresses pointing to the Interrupt Service Routines (ISRs).

The vector table should be placed in a dedicated .vector_table section. Each entry corresponds to a specific interrupt or exception and indicates the location of the associated ISR function.

For example: .section .vector_table .word _estack /* 0x0000 */ .word Reset_Handler /* 0x0004 */ .word NMI_Handler /* 0x0008 */ .word HardFault_Handler /* 0x000C */

The Reset_Handler label points to the startup initialization code within crt0.o that will call main(). Each subsequent entry points to interrupt handler functions defined elsewhere in your application code.

Calling the main() Function

After configuring the stack pointer and interrupt vector table, the next step is to call main(). This is done inside the Reset_Handler code: Reset_Handler: bl main

The main() function serves as the entry point for your C/C++ application code. Any target-specific hardware initialization steps should be performed before calling main().

Target Hardware Initialization

Depending on the specific Cortex-M0 chip and board being used, some amount of hardware initialization may be required before starting the main application. This includes things like:

  • Clock initialization
  • GPIO pin configuration
  • Watchdog timer setup
  • Chip-specific errata workarounds

These initialization steps can be implemented in a systemic_init() function called before main(): Reset_Handler: bl system_init bl main

The hardware initialization requirements will vary across Cortex-M0 targets and may require consulting the chip vendor’s documentation.

Building the Default crt0.o

With an understanding of the key startup steps, we can now look at building the default crt0.o object file for GCC. This can be done by creating a cortexm0.S assembly source file with the following contents: .section .stack .word _estack /* Set stack pointer */ .section .vector_table .word _estack /* Initial stack pointer value */ .word Reset_Handler /* Reset vector */ /* Other interrupt vectors… */ /* Reset handler */ Reset_Handler: bl system_init bl main system_init: /* Target hardware initialization */ /* Default interrupt handlers */ Default_Handler: Infinite_Loop: b Infinite_Loop

This implements stack and vector table initialization, calls target-specific init code, runs main(), and provides default interrupt handler stubs. The file is assembled into an object file: arm-none-eabi-as cortexm0.S -o cortexm0.o

And then passed to the linker when building your Cortex-M0 application: arm-none-eabi-gcc test.c cortexm0.o -o test.elf

With this approach, you now have a proper crt0.o default startup file to build atop for your GCC-based Cortex-M0 projects.

Customizing crt0.o

For most projects, additional customization of crt0.o will be needed beyond the bare basics shown above. Common extensions include:

  • Adding chip errata workarounds in system_init()
  • Implementing custom interrupt handlers
  • Setting up the C library newlib
  • Init code for peripheral libraries
  • Enabling semihosting for debug logging

Consult your Cortex-M0 reference manual and data sheet to determine what additional initialization and configuration must be performed for your specific chip implementation.

Linker File Configuration

To complement the crt0.o startup code, you will need to provide a linker script file that defines the memory regions and output sections for your chip. This ensures code and variables are placed into the correct Flash and RAM locations.

Key linker file settings needed for Cortex-M0 GCC projects include:

  • Flash and RAM memory region addresses and sizes
  • Placement of vector table section
  • Linker script symbols for stack and heap
  • .bss and .data section configuration

Consult the GCC linker documentation for details on how to properly configure memory regions, output sections, and other linker script directives.

Debugging Startup Code

Debugging issues with crt0.o and early startup code can be challenging. Some techniques to help include:

  • Enable semihosting in crt0.o for early debug printfs
  • Step through initialization code line-by-line in a debugger
  • Add temporary LED toggles in startup code to indicate progress
  • Print stack pointer address after initialization to verify location
  • Check linker map file to ensure sections are placed as expected

With careful use of debugging aids like these, you can identify and resolve bootup crashes, memory errors, stack overflows, and configuration problems caused by your Cortex-M0 crt0.o and startup code.

Conclusion

While creating a properly configured crt0.o requires some upfront effort, the benefits are substantial for building robust and efficient Cortex-M0 applications with GCC. Following the steps outlined here will give you a solid foundation to start your projects and customize further for your specific chip and use case needs.

With a customized crt0.o, linker script, and startup code tuned for your hardware, you can leverage the full capabilities of GCC to maximize the performance and capabilities of your Cortex-M0 microcontroller.

Newsletter Form (#3)

More ARM insights right in your inbox

 


Share This Article
Facebook Twitter Email Copy Link Print
Previous Article How to Run a Cycle Mode (DSM=yes) for CORTEX-M0 Processor?
Next Article How to Get Compiled Code into a Custom SoC through the SWD Interface for Cortex M0?
Leave a comment Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

2k Followers Like
3k Followers Follow
10.1k Followers Pin
- Sponsored-
Ad image

You Might Also Like

Dynamic Interrupt Priority Changes on Cortex-M3/M4

The Cortex-M3 and Cortex-M4 microcontrollers allow for dynamic changing of…

6 Min Read

Where is arm cortex-M0 used?

The ARM Cortex-M0 is a 32-bit microcontroller core from ARM…

6 Min Read

What is the difference between bootloader, startup code and bootstrap loader?

When an ARM-based system powers on, there are several key…

7 Min Read

Application Binary Interface Examples

An application binary interface (ABI) defines how application programs can…

8 Min Read
SoCSoC
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
Welcome Back!

Sign in to your account