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.
In general, you need the following steps to use CMSIS-Core:
*.pack
Reset_Handler
RESET
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/
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.
armasm
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>
ARMCM4[1]
/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.
<name>
Cortex-M4
--cpu=<name>
--cpu=list
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.
#include
core_<device>.h
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
-I /path/to/CMSIS/Core/Include
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].
<device>.h
ARMCM4_CM4
ARMCM4_FP.h
/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
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.
SystemInit()
Reset_Handler()
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! ^
/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
system_ARMCM4.c
ARMCM4_FP
With Arm Compiler 6, you can compile these files using the compiler, armclang, with a minimum set of command-line options:
armclang
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.
cortex-m4
<macro>
-mcpu=<name>
-mcpu=list
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.
/path/to/CMSIS_5.6.0
unzip
Device
CMSIS
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; }
startup_ARMCM4.s
Interrupt0_Handler()
NVIC_*()
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;
ARMCM4_ac6.sct
/path/to/CMSIS_5.6.0/Device/ARM/ARMCM4/Source/ARM
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:
image.axf
Hello, world! Enabled device-specific interrupt #0 Hello from Interrupt0_Handler()!
[CTAToken URL = "https://github.com/ARM-software/CMSIS_5/releases" target="_blank" text="Download CMSIS from GitHub" class ="green"]
[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:
_<extension>
How to use arm compiler 6 without keil or ds?