Arm Community
Arm Community
  • Site
  • User
  • Site
  • Search
  • User
Arm Community blogs
Arm Community blogs
Tools, Software and IDEs blog Using CMSIS with Arm Compiler 6, without an IDE
  • Blogs
  • Mentions
  • Sub-Groups
  • Tags
  • Jump...
  • Cancel
More blogs in Arm Community blogs
  • AI blog

  • Announcements

  • Architectures and Processors blog

  • Automotive blog

  • Embedded and Microcontrollers blog

  • Internet of Things (IoT) blog

  • Laptops and Desktops blog

  • Mobile, Graphics, and Gaming blog

  • Operating Systems blog

  • Servers and Cloud Computing blog

  • SoC Design and Simulation blog

  • Tools, Software and IDEs blog

Tags
  • Arm Compiler 6
  • Arm Compiler
  • Keil
  • CMSIS
  • Software Development
Actions
  • RSS
  • More
  • Cancel
Related blog posts
Related forum threads

Using CMSIS with Arm Compiler 6, without an IDE

Salman Arif
Salman Arif
November 7, 2019
9 minute read time.

What is CMSIS?

The Common Microcontroller Software Interface Standard (CMSIS) is a vendor-independent abstraction layer for microcontrollers that are based on Arm Cortex processors. It defines generic tool interfaces and enables consistent device support. To learn more about about how CMSIS has evolved and is used today, read Christopher Seidl's blog. Reference documentation is available online and you can download CMSIS releases from GitHub.

CMSIS is easiest to use with an IDE such as Keil MDK or Arm Development Studio. However, if you are working on a project on Linux, or would like to create a command-line only project that does not rely on any IDEs, it is useful to know how to use it standalone. A future release of CMSIS will include support for a command-line driven Make system, "CMSIS-Build", to make this process even easier.

This technical blog explains how to start working with CMSIS with Arm Compiler 6 and uses an example to show how interrupts can be configured on a Cortex-M4 processor. Specifically, this blog focuses on the CMSIS-Core component for Cortex-M. CMSIS-Core implements the basic run-time system for a Cortex-M device and gives users a standardised way to access processor core and device peripherals. An operational knowledge of using Arm Compiler 6 is assumed. CMSIS also supports other toolchains such as IAR and the GNU Arm Embedded Toolchain.

Using CMSIS

In general, you need the following steps to use CMSIS-Core:

  1. Download and extract the CMSIS *.pack file for the release you would like to use. Despite the file extension, this is just a ZIP file.
  2. Include the required header files for the device you are building for in your C/C++ language source code.
  3. Use the startup file for the device you are building for, and specify the Reset_Handler symbol within it as the entry point for your image.
  4. Use the system configuration file for the device you are building for, and specify the selected device using a preprocessor macro.
  5. Link with a scatter-file that places the RESET section of the startup file at the start of the image, to ensure that the vector table is located at the default address of the Vector Table Offset Register (VTOR).

If you're working for a specific device from a Arm partner instead of a generic Cortex-M device, you may want to download software packs for your device from the Keil website: https://www.keil.com/dd2/

Minimum required CMSIS files

Startup file

CMSIS-Core(M) device startup files compatible with Arm Compiler 6 are typically available in C or legacy armasm syntax assembly language source code. Documentation for these files is available on GitHub. A startup file includes a vector table, a reset handler, default exception handlers, and sets up the Main Stack Pointer. Linking with this file is required to use CMSIS functions in C/C++ code.

Startup files are located in the following directory within a CMSIS release:

/Device/<vendor>/<device>/Source/

where <device> is a device string as documented in the “Device Examples” table in the CMSIS documentation. For a generic Cortex-M4 target, the device would be ARMCM4[1] which can be seen in the startup file names:

/Device/ARM/ARMCM4/Source/startup_ARMCM4.c
/Device/ARM/ARMCM4/Source/ARM/startup_ARMCM4.s

With Arm Compiler 6, you can assemble files with the legacy assembler, armasm, with a minimum set of command-line options:

armasm --cpu=<name> /path/to/Device/ARM/<device>/Source/ARM/startup_<device>.s -o startup_<device>.o

where <name> is the target architecture or CPU you are building for. For a Cortex-M4 target, <name> is Cortex-M4, and <device> is as described above. To see a list of available options for the --cpu=<name> option, use --cpu=list and refer to the armasm Legacy Assembler Reference section of the Arm Compiler Reference Guide.

CMSIS-Core Processor file

A CMSIS-Core Processor file defines CMSIS functions for a given device, and must therefore must be included in your project.

You do not need to #include this file for your target device in your code, but it should be available on the include paths for the compiler. CMSIS-Core Processor files have a name with the format core_<device>.h, and the list of names for various devices is shown in a table under the “CMSIS-Core Processor Files” heading in the CMSIS documentation. For example, for Cortex-M4, this would be core_cm4.h.

These files are located in the following directory within a CMSIS release:

/CMSIS/Core/Include

To ensure that the compiler can find these files, compile with the -I option and specify a path to this directory. For example:

-I /path/to/CMSIS/Core/Include

Device header file

These files contain macro definitions and enumerations for device-specific information. For example, interrupt numbers, and whether configurable features such as the Floating-Point Unit (FPU) and Memory Protection Unit (MPU) are available. You can configure some of these macros to support your exact target configuration if it varies from the default. The list of parameters you can configure are available in the CMSIS documentation.

You must #include this file for your target device in your code. These files have a name with the format <device>.h, where <device> is as defined previously. For example, for a generic Cortex-M4 target with the Floating-Point Extension, <device> is ARMCM4_CM4 and the device header file is called ARMCM4_FP.h[1].

These files are located in the following directory within a CMSIS release:

/Device/ARM/<device>/Include

where <device> is as defined previously. To ensure that the compiler can find these files, compile with the -I option and specify a path to this directory. For example:

-I /path/to/Device/ARM/<device>/Include

System configuration file

These files provide a device-specific implementation of a system initialization function, SystemInit() that configures the system, and is the first function to be called after Reset from within Reset_Handler() function in a CMSIS startup file. More information about these files is available in the CMSIS documentation.

For example, for a generic Cortex-M4 target with the Floating-Point Extension, this function enables the Floating-Point Unit (FPU) if present on the device:

/*----------------------------------------------------------------------------
  System initialization function
 *----------------------------------------------------------------------------*/
void SystemInit (void)
{

#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
  SCB->VTOR = (uint32_t) &__VECTOR_TABLE;
#endif

#if defined (__FPU_USED) && (__FPU_USED == 1U)
  SCB->CPACR |= ((3U << 10U*2U) |           /* enable CP10 Full Access */
                 (3U << 11U*2U)  );         /* enable CP11 Full Access */
#endif

#ifdef UNALIGNED_SUPPORT_DISABLE
  SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;
#endif

  SystemCoreClock = SYSTEM_CLOCK;
}

You need to build this file using the compiler, and link the output object file into your image. You will also need to specify a preprocessor macro that matches the device string for your device, as documented in the “Device Examples” table in the CMSIS documentation.

If you do not specify a device string, you may see the compiler report an error like the following:

/path/to/Device/ARM/ARMCM4/Source/system_ARMCM4.c:31:4: error: device not specified!
  #error device not specified!
   ^

These files are located in the following directory within a CMSIS release:

/Device/ARM/<device>/Source

where <device> is as defined previously. For example, for Cortex-M4, <device> is ARMCM4 and subsequently the system configuration file is system_ARMCM4.c. The preprocessor macro to define is one of the following[1]:

  • ARMCM4 for a Cortex-M4 device without the Floating-Point Extension.
  • ARMCM4_FP for a Cortex-M4 device with the Floating-Point Extension.

With Arm Compiler 6, you can compile these files using the compiler, armclang, with a minimum set of command-line options:

armclang --target=arm-arm-none-eabi -mcpu=<name> -Os -c \
    -D<macro> \
    -I /path/to/CMSIS/Core/Include \
    -I /path/to/Device/ARM/<device>/Include
    /path/to/Device/ARM/<device>/Source/system_<device>.c -o system_<device>.o


where <name> is the target CPU you are building for. For a Cortex-M4 target, <name> is cortex-m4, <device> is ARMCM4, and <macro> is ARMCM4_FP[1]. To see a list of available options for the -mcpu=<name> option, use -mcpu=list and refer to the armclang Reference section of the Arm Compiler Reference Guide.

Example

This example explains how to build for a generic Cortex-M4 target with the Floating-Point Extension, and configure and trigger device-specific interrupt #0.

  1. Download and unzip the CMSIS 5.6.0 *.pack from the GitHub CMSIS releases page, and save it to a known location. We will refer to this location as:
    /path/to/CMSIS_5.6.0
    

    You can use any ZIP file utility, such as 7-zip on Windows and the unzip tool on Linux. After extraction, a Device and a CMSIS directory should be available. Device vendors create "Device Family Packs" that can be used to populate the Device directory with additional support files for vendor-specific devices.
  2. Save the following code to a file foo.c:
    #include <stdio.h>
    #include <ARMCM4_FP.h>
    
    void Interrupt0_Handler(void)
    {
        printf("Hello from Interrupt0_Handler()!\n");
    }
    
    int main(void)
    {
        printf("Hello, world!\n");
    
        /* Initialize Interrupt0_IRQn */
        NVIC_SetPriority(Interrupt0_IRQn, 1);
        NVIC_EnableIRQ(Interrupt0_IRQn);
        printf("Enabled device-specific interrupt #0\n");
    
        /* Set Interrupt0_IRQn to pending */
        NVIC_SetPendingIRQ(Interrupt0_IRQn);
    
        return 0;
    }
    

    This code uses CMSIS NVIC configuration functions to enable a device-specific timer, and sets it to pending to test its interrupt handler. It is important to note that:
    • The default interrupt handler function for the device-specific timer is defined in the startup file startup_ARMCM4.s, and is an infinite loop. Defining it in a C language source file like this will cause the compiler to select this definition instead of the default one. You must use the same name for the interrupt handler function as the one used in the startup file. In this example, the name is Interrupt0_Handler().
    • Interrupt number enums specified to the NVIC_*() functions are device-specific and are defined in the device header file ARMCM4_FP.h. You must use these enums instead of specifying a number directly. In this example, we’re using the enum Interrupt0_IRQn:
      /* -------------------------  Interrupt Number Definition  ------------------------ */
      
      typedef enum IRQn
      {
      /* -------------------  Processor Exceptions Numbers  ----------------------------- */
        NonMaskableInt_IRQn           = -14,     /*  2 Non Maskable Interrupt */
        HardFault_IRQn                = -13,     /*  3 HardFault Interrupt */
        MemoryManagement_IRQn         = -12,     /*  4 Memory Management Interrupt */
        BusFault_IRQn                 = -11,     /*  5 Bus Fault Interrupt */
        UsageFault_IRQn               = -10,     /*  6 Usage Fault Interrupt */
        SVCall_IRQn                   =  -5,     /* 11 SV Call Interrupt */
        DebugMonitor_IRQn             =  -4,     /* 12 Debug Monitor Interrupt */
        PendSV_IRQn                   =  -2,     /* 14 Pend SV Interrupt */
        SysTick_IRQn                  =  -1,     /* 15 System Tick Interrupt */
      
      /* -------------------  Processor Interrupt Numbers  ------------------------------ */
        Interrupt0_IRQn               =   0,
        Interrupt1_IRQn               =   1,
        Interrupt2_IRQn               =   2,
        Interrupt3_IRQn               =   3,
        Interrupt4_IRQn               =   4,
        Interrupt5_IRQn               =   5,
        Interrupt6_IRQn               =   6,
        Interrupt7_IRQn               =   7,
        Interrupt8_IRQn               =   8,
        Interrupt9_IRQn               =   9
        /* Interrupts 10 .. 224 are left out */
      } IRQn_Type;
      
  3. Find the scatter-file for Arm Compiler 6, ARMCM4_ac6.sct, from the directory containing the startup file:
    /path/to/CMSIS_5.6.0/Device/ARM/ARMCM4/Source/ARM
    

    This file will be specified to the linker.
  4. Build this example using the following command-line options:
    armclang --target=arm-arm-none-eabi -mcpu=cortex-m4 -Os -c \
        -I /path/to/CMSIS_5.6.0/CMSIS/Core/Include \
        -I /path/to/CMSIS_5.6.0/Device/ARM/ARMCM4/Include \
        foo.c -o foo.o
    armclang --target=arm-arm-none-eabi -mcpu=cortex-m4 -Os -c \
        -DARMCM4_FP \
        -I /path/to/CMSIS_5.6.0/CMSIS/Core/Include \
        -I /path/to/CMSIS_5.6.0/Device/ARM/ARMCM4/Include \
        /path/to/CMSIS_5.6.0/Device/ARM/ARMCM4/Source/system_ARMCM4.c -o system_ARMCM4.o
    armasm --cpu=Cortex-M4 \
        /path/to/CMSIS_5.6.0/Device/ARM/ARMCM4/Source/ARM/startup_ARMCM4.s -o startup_ARMCM4.o
    armlink --cpu=Cortex-M4 --entry=Reset_Handler \
        --scatter=/path/to/CMSIS_5.6.0/Device/ARM/ARMCM4/Source/ARM/ARMCM4_ac6.sct \
        startup_ARMCM4.o system_ARMCM4.o foo.o -o image.axf
    

That’s all! If you run the output image file, image.axf using a debugger with semihosting on a Cortex-M4 target with the Floating-Point Extension, it should print:

Hello, world!
Enabled device-specific interrupt #0
Hello from Interrupt0_Handler()!

Download CMSIS from GitHub


[1]
For devices with an optional extension, use the device string without any _<extension> suffix when selecting a startup file, system file, CMSIS-Core processor file, or include directory. Only use the full device string with suffixes for preprocessor macros and selecting a device header file. For example, for a Cortex-M4 device with the Floating-Point Extension:

  • The startup file is startup_ARMCM4.s.
  • The preprocessor macro is ARMCM4_FP.
  • The device header file is ARMCM4_FP.h.
Anonymous
  • devprodest
    devprodest over 5 years ago

    How to use arm compiler 6 without keil or ds?

    • Cancel
    • Up 0 Down
    • Reply
    • More
    • Cancel
Tools, Software and IDEs blog
  • Python on Arm: 2025 Update

    Diego Russo
    Diego Russo
    Python powers applications across Machine Learning (ML), automation, data science, DevOps, web development, and developer tooling.
    • August 21, 2025
  • Product update: Arm Development Studio 2025.0 now available

    Stephen Theobald
    Stephen Theobald
    Arm Development Studio 2025.0 now available with Arm Toolchain for Embedded Professional.
    • July 18, 2025
  • GCC 15: Continuously Improving

    Tamar Christina
    Tamar Christina
    GCC 15 brings major Arm optimizations: enhanced vectorization, FP8 support, Neoverse tuning, and 3–5% performance gains on SPEC CPU 2017.
    • June 26, 2025