CMSIS Doulos Tutorial
CMSIS Doulos Tutorial
CMSIS Doulos Tutorial
You can find the full set of currently published Tutorials and register for notification of future additional at www.doulos.com/knowhow You can also download the full source code of the examples used within the Tutorial at the same URL. Also check out the Doulos ARM Training and service options at www.doulos.com/arm
First published by Doulos March 2009 Copyright 2009 Doulos. All rights reserved. All trademarks acknowledged. All information is provided as is without warranty of any kind.
Contents
Introduction ................................................................................................................ 5 CMSIS Structure ........................................................................................................ 6 Core Peripheral Access Layer (CPAL) .................................................................... 6 File Structure........................................................................................................... 7 Tool Independence.................................................................................................. 8 MISRA-C ................................................................................................................. 8 CPAL Functions ...................................................................................................... 9 Interrupt Service Routines....................................................................................... 9 Other Coding Conventions ...................................................................................... 9 Identifiers............................................................................................................ 10 Comments.......................................................................................................... 10 Data Types......................................................................................................... 10 Debugging............................................................................................................. 11 Future Updates ..................................................................................................... 11 Tutorial 1 A First Example ..................................................................................... 12 Example 1 Starting point ....................................................................................... 12 Example 2 Converting to CMSIS........................................................................... 13 Tutorial 2 ITM Debug............................................................................................. 15 Summary.................................................................................................................. 17
CMSIS
Introduction
The Cortex Microcontroller Software Interface Standard (CMSIS) supports developers and vendors in creating reusable software components for ARM Cortex-M based systems. The ARM Cortex-M3 processor is the first core from ARM specifically designed for the Microcontroller market. This core includes many common features (NVIC, Timer, Debug-hardware) needed for this market. This will enable developers to port and reuse software (e.g. a real time kernel) with much less effort to Cortex-M3 based MCUs. With a significant amount of hardware components being identical, a large portion of the Hardware Abstraction Layer (HAL) can be identical. However, reality has shown that lacking a common standard we find a variety of HAL/driver libraries for different devices, which, as far as the Cortex-M3 part is concerned essentially do the same thing just differently. The latest study of the development for the embedded market shows that software complexity and cost will increase over time, see figure left. Reusing Software and having a common standard to govern how to write and debug the software will be essential to minimising costs for future developments. With more Cortex-M3 based MCUs about to come onto the market, ARM has recognized that after solving the diversity issue on the hardware side, there is still a need to create a standard to access these hardware components. Figure 1 Development Costs
The result of that effort is CMSIS; a framework to be extended by vendors, while taking advantage of a common API (Application Programming Interface) for core specific components and conventions that define how the device specific portions should be implemented to make developers feel right at home when they reuse code or develop new code for ARM Cortex-M based devices.
CMSIS Structure
CMSIS can be divided into three basic function layers: Core Peripheral Access Layer (CPAL) Middleware Access Layer (MWAL) Device Peripheral Access Layer (DPAL)
The basic structure and the functional flow is illustrated in the Figure 2. below.
While DPAL is intended to be extended by the silicon vendor, lets not forget about Cortex-M based FPGA products, which effectively put developers into the position of a silicon vendor. As far as MCU based systems are concerned it might make sense for developers to treat the entire PCB system as monolithic block. There is no reason to differentiate between a memory mapped register inside the MCU and a memory mapped register external to the MCU, connected via external memory interface. The benefit of applying a standard like CMSIS is that existing guidelines on how to access these devices set a clear goal on how to implement and integrate critical parts of the software. Other team members will find a familiar environment.
File Structure
File names in CMSIS are standardized as follows: core_cm3.h core_cm3.c <device>.h Cortex-M3 global declarations and definitions, static function definitions Cortex-M3 global definitions Top-level header file (device specific). To be included by application code. Includes core_cm3.h and system_<device>.h Device specific declarations Device specific definitions, e.g. SystemInit()
system_<device>.h system_<device>.c
Application code will only include the top-level header file which implicitly pulls in all other essential
header files. The illustration below shows the flow and dependencies of the header files stm32.h, core_cm3.h and system_stm32.h, which are part of CMSIS release V1P0.
/* Configuration of the Cortex-M3 Processor and Core Peripherals */ #define __MPU_PRESENT #define __NVIC_PRIO_BITS 0 /*!< STM32 does not provide a MPU present or not*/ 4 /*!< STM32 uses 4 Bits for the Priority Levels */
*/ */
The <device>.h file is the central include file and provided by the silicon vendor. The application programmer is using that as the main include file in his C source code. Note that the ARM Cortex-M3
Copyright 2009 by Doulos. All rights reserved.
The __Vendor_SysTickConfig defined is showing in both cases the default setting. When this macro is set to 1, the SysTickConfig() function in the cm3_core.h is excluded. In this case the file <device>.h must contain a vendor specific implementation of this function.
Tool Independence
CMSIS exists in a three-dimensional space of the form vendordevicetool chain. In order to remove one dimension (tool chain), the common files core_cm3.c and core_cm3.h contain all essential tool specific declarations and definitions. Example:
/* define compiler specific symbols */ #if defined ( __CC_ARM ) #define __ASM __asm #define __INLINE __inline #elif defined ( __ICCARM__ ) #define __ASM __asm #define __INLINE inline avaiable in High optimization mode! */ #define __nop __no_operation #elif defined ( __GNUC__ ) #define __ASM asm #define __INLINE inline #endif
/*!< asm keyword for armcc /*!< inline keyword for armcc /*!< asm keyword for iarcc /*!< inline keyword for iarcc. Only
*/ */ */
/*!< no operation intrinsic in iarcc */ /*!< asm keyword for gcc /*!< inline keyword for gcc */ */
The remaining parts of CMSIS can now simply use the macro __INLINE to define an inline function. Currently three of the most important C-compilers are supported: ARM RealView (armcc), IAR EWARM (iccarm), and GNU Compiler Collection (gcc). This is expected to cover the majority of tool chains.
MISRA-C
Besides defining an API for Cortex-M core peripherals and guidelines on how to support device peripherals, CMSIS defines some coding guidelines and conventions. Most important is that the CMSIS code base is MISRA-C 2004 compliant, which implies that every extension should be compliant, too. MISRA-C is a set of safety rules established by the Motor Industry Software Reliability Association for the C programming language. Maintaining MISRA compliance can be tricky, in particular when implementing driver level software. Therefore, pragma-like exceptions in PCLint style
are scattered across the source code. Be aware that other tools, e.g. MISRA checker in IAR EWARM, might flag errors. Each exception is accompanied with a comment explaining why this exception was made.
CPAL Functions
All functions in the Core Peripheral Access Layer are reentrant and can be called from different 1 interrupt service routines (ISR). CPAL functions are also non-blocking in the sense that they do not contain any wait-loops. The majority of functions in the CPAL have been implemented in the header file core_cm3.h as static inline functions. This allows the compiler to optimize the function calls by placing the instructions that make up the called function along with other code from which the function was called.
Listing shows the generic part of the (<device>.h) file. All system handlers have negative virtual slot numbers so that they can be distinguished in functions that abstract from the differences between system handlers and external interrupt handlers. External interrupt handlers start at the index 0.
Memory barriers are exempt from that rule although they might stall the processor for a few cycles.
Through __weak declaration in EWARM and RVCT armcc, __attribute__((weak)) in GCC and RVCT armcc and [WEAK] export in RVCT/armasm.
Comments
CMSIS uses Doxygen style comments for all definitions and encourages developers to do the same. In particular, the comment for each function definition should at least contain one-line brief function overview. (Tag: @brief) detailed parameter explanation. (Tag: @param) detailed information about return values. (Tag: @return) detailed description of the actual function.
The tags can be parsed by the documentation tool Doxygen, which is used to create cross-referenced source code documentation in various formats (http://www.stack.nl/~dimitri/doxygen/index.html). The tag syntax is rather minimalistic and does not impair readability of the source code. Please consult the Doxygen Documentation for details about tag syntax. As an alternative to regular C block comments (/* */) CMSIS explicitly allows line comments (//, so called C++-comments). If you are concerned about MISRA compliance, be aware though, that MISRA-C 2004 doesnt allow line comments according to rule 2.2.
Data Types
All data types referenced by CMSIS are based on those defined in the standard C header file stdint.h. Data structures for core registers are defined CMSIS header file core_cm3.h, along with macros for qualifying registers according to their access permissions. The rationale is that tools might be able to automatically extract that information for debug purposes.
10
/*!< defines 'read only' permissions */ /*!< defines 'write only' permissions */ /*!< defines 'read / write' permissions */
Debugging
A common requirement in software development is some sort of terminal output for debugging. Text/graphics displays in embedded devices cannot be assumed to be at hand (or might be in use), which previously left the developer with essentially two choices: 1. Use one of the ubiquitous UARTs and connect a terminal Issues: All UARTs might be in use, access to UART signals might not be possible for reasons that include pin-sharing, PCB layout, etc. 2. Use the semihosting mechanism Issue: Significant software overhead on target CPU, might not be supported in the same way by all tool chains, potential impact on timing behavior. With Cortex-M3 the preferred method makes use of the Instrumentation Trace Macrocell (ITM), which 3 is part of the processor macro cell and thus always present. A Serial Wire Viewer (SWV) capable debug adapter can receive ITM data through the SWO (Serial Wire Out) debug pin. ITM implements 32 general purpose data channels. CMSIS builds on top of this and declares channel 0 to be used for terminal output, along with a function called ITM_SendChar() which can be used as low-level driver function for printf-style output. A second channel (31) has been reserved for OS aware debugging, which means that a kernel can use it to transmit kernel specific data which could then be interpreted by the debug tool. With this standardization, tool vendors have it much easier to implement specific debug features, such as e.g. terminal emulation for data received via ITM channel 0. Developers on the other hand can rely on this feature to dump state information, without having to configure UARTs and external terminal emulators. See our Tutorial 2 later in this document. Access privilege can be configured for groups of ITM channels. In order to use ITM channel 0, unprivileged access must be granted, whereas ITM channel 31 is in a different group and may allow privileged access only.
Future Updates
The CMSIS developers have taken care to provide macros indicating the CMSIS version used in a project. That way provisions can be made to prevent code to be used with a different CMSIS version than originally intended.
#define __CM3_CMSIS_VERSION_MAIN #define __CM3_CMSIS_VERSION_SUB #define __CM3_CMSIS_VERSION (0x01) (0x10) /* [31:16] main version /* [15:0] sub version */ */
Always present in Cortex-M3 rev1 cores. Cortex-M3 rev2 makes ITM an optional feature.
11
12
void SysTickHandler(void) { static BitAction toggle = Bit_SET; GPIO_WriteBit(GPIOB, GPIO_Pin_15, toggle); if (toggle == Bit_SET) { toggle = Bit_RESET; } else { toggle = Bit_SET; }
The two listings above show the contents of main.c, and stm32f10x_it.c. This latter file contains interrupt handler templates from STs FWLib, which have to be adapted to implement project specific functionality.
#if (__CM3_CMSIS_VERSION != 0x00010010) # error "__CM3_CMSIS_VERSION: Unexpected CMSIS version detected" #endif
Initial support for STM32 MCU is part of CMSIS and is pulled in by including the header file stm32.h. At the point of writing this tutorial a fully CMSIS complaint FWLib was not available so some compromises and hand adjustments hand to be made. Fore this reason, we will include both, FWLib and CMSIS files. Until vendors have full adopted CMSIS small issues will have to be dealt with when combining CMSIS with a vendor library. In this case simply including the FWLib main header file stm32f10x_lib.h in addition to stm32.h triggers a number of error messages caused by multiple definitions of functions and macros. To avoid this, we will have to selectively include individual FWLib headers (see below). All FWLib headers depend on definitions in the files cortexm3_core.h and stm32f10x_map.h. Most of the definitions in these two header files have already been defined by CMSIS (core_cm3.h and system_stm32.h) and we have to pretend to FWLib that both header files had been included already.
// Prevent interference with FWLib #define __STM32F10x_MAP_H #include <stm32f10x_type.h> #include <stm32f10x_gpio.h> #include <stm32f10x_rcc.h>
Actual system initialization will be encapsulated by the CMSIS function SystemInit(), which has to be implemented by the silicon vendor. As a minimal requirement, this function would initialize the MCUs clock system. In case of the reference implementation in system_stm32.c, SystemInit() also initializes the Flash memory interface. CMSIS defines a single system variable, SystemFrequency, which is
Copyright 2009 by Doulos. All rights reserved.
13
NVIC group- and sub-priority configuration is handled by the function NVIC_SetPriorityGrouping(), where the direct encoding of the PRIGROUP field in SCB->AIRCR is used. We choose the value 4, which represents 3 bits for group (preempting) and 5 bits for the sub priority. A general formula to calculate the proper value is PRIGROUP = bitssub-1.
// priority configuration: 3.5 NVIC_SetPriorityGrouping(4); // *** CMSIS change ***
Different from the initial version of the example, the CMSIS variant does not at this point set the SysTick handler priority. This is part of the SysTick initialization and will be covered later.
GPIO_Init(GPIOB, &GPIOB_InitStruct); GPIO_WriteBit(GPIOB, GPIO_Pin_All, Bit_RESET);
Following NVIC set-up, we use plain FWLib functions to configure GPIO port B.
SysTick_Config(SystemFrequency/2); // *** CMSIS Change ***
// SysTick_Config() hardcodes priority. We will overwrite this. NVIC_SetPriority(SysTick_IRQn, 14); // *** CMSIS change ***
SysTick_Config(), provided by CMSIS, programs the reload register with the parameter value. The function also selects HCLK (core clock) as clock source, enables SysTick interrupts and starts the counter. The function also fixes the SysTick handler priority to the lowest priority in the system, which is the recommended SysTick priority for use in an RTOS scheduler for instance. In our example, however, we prefer a different priority and override the hard coded value with an additional call to NVIC_SetPriority(). This function abstracts from the difference between Cortex-M3 system handlers and external interrupt handlers. All configurable system exceptions will be identified by negative IRQ numbers (see above).
14
GPIO_WriteBit(GPIOB, GPIO_Pin_15, toggle); if (toggle == Bit_SET) { puts("Pin state is ON"); toggle = Bit_RESET; } else { puts("Pin state is OFF"); toggle = Bit_SET; } }
The SysTick handler code above does not need any modification. FWLib naming conventions is complying with CMSIS, in that the names of all internal exception handlers must end in _Handler. Names of external interrupt handlers must end in _IRQHandler. The handler implementation accesses the GPIO port via FWLib functions and definitions.
The standard C function fputc(), which will eventually be called by puts() in our SysTick handler, will be re-implemented, taking advantage of the function ITM_SendChar(). The result of this retarget can now be easily monitored in Vision ITM viewer as shown in the screenshot below.
15
If you are going to use the IAR EWARM tool for this example it is not necessary to manually retarget this function. Instead, in the project options dialog under General Options there is a tab Library Configuration tab, which offers checkboxes to enable this functionality. The screenshot below shows which settings are required to redirect standard output.
16
Summary
The CMSIS will reduce the learning-curve for the application programmers by providing a consistent software framework, ensuring consistent documentation and easy deployment of boilerplate code across various compiler vendors. The consequent use and implementation of CMSIS across many silicon and middleware software partners will simplify the verification and certification process and therefore reduce future project risk. The adapted common programming techniques though CMSIS will simplify the long term maintenance due to easier to understand source code. The silicon vendors can focus on there added value and device features. All reasons together will reduce software development cost and time to get new products to the market.
17
18
19