mbed cmake

Using Mbed with the cmake buildsystem

The new mbed feature of exporting to offline toolchains is absolutely fantastic, it has personally made mbed around 100 times more useful to me! Thanks an awful lot to Emilio and everyone else who has worked to make this happen.

In this page, I describe how to get exported mbed projects to build using the cmake buildsystem favoured by many, rather than the unix makefile supplied by default with the exported project. The instructions I am supplying are for the CodeSourcery toolchain, but should be reasonably easy to extend to others. I also am using Linux, but Windows/OSX setups should be just the same. It's called cmake for a reason, after all.

PLEASE NOTE: Currently only compiling for the LPC1768 is working. I think this is because I don't know the right compiler flags for the other models. I own a 2368 but can't export from the online compiler as it's not supported. I don't own a 11U24, so I think the flags may be all wrong... Any help much appreciated

Enjoy :-)

Comments and improvements are most welcome.

Toolchain Setup

you will need to have setup the http://www.mentor.com/embedded-software/codesourcery CodeSourcery Toolchain properly on your system. Make sure that the appropriate location is in your $PATH (i.e. on Linux you can run the command arm-none-eabi-gcc).

Cmake Toolchain File

In order to cross compile using cmake, we need to setup an appropriate cmake toolchain file. This file sets some cmake variables, and importantly forces cmake to use the appropriate compiler. There is a comprehensive guide http://www.vtk.org/Wiki/CMake_Cross_Compiling here but I also include a toolchain file which works for me. The file extension should be .cmake

It's good practice to have a file like this that works saved somewhere, so you can drop it straight into every new mbed project you export.

codesourcery_toolchain.cmake

INCLUDE(CMakeForceCompiler)

SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_SYSTEM_VERSION 1)

# specify the cross compiler
CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU)
CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU)

SET(COMMON_FLAGS "-mcpu=cortex-m3 -mthumb -mthumb-interwork -msoft-float -ffunction-sections -fdata-sections -g -fno-common -fmessage-length=0")
SET(CMAKE_CXX_FLAGS "${COMMON_FLAGS}  -std=gnu++0x")
SET(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu99")
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-gc-sections ")

Please note that I haven't included any CMAKE_FIND_ROOT_PATH type of thing here. If you are intending to import libraries which also have the same names on the root system (i.e. a c++ libraries not included with CodeSourcery like embedded versions of libJPG or something) this may be necessary. It hasn't been a problem in my case though. I also have added the flag -std=gnu++0x to the C++ compiler flags. This is necessary to get things including CodeSourcery libraries to compile without a warning, but is technically beta, so may (unlikely) cause issues.

To get the other mbeds working there will probably have to be fewer flags set here and more set in the cmake_header file.

cmake_header.txt file (mbed magic :-D)

I have written the following cmake script that allows certain useful building features on an exported mbed project, namely:

  • Changing target mbed platform as easily as you can with the online compiler (using a simple command line argument)
  • Creating an mbed flashable binary image from the ELF format generated by the GCC compiler
  • setup appropriate library and include directories and startup script
  • setup appropriate linker script

All the hard work, again, is done by the mbed guys and this is a very simple script. The Makefile included with downloaded projects has the mbed type "hardcoded" into it, but all necessaries are included in the directory to build for any type of mbed. The cmake script has the flexibility to choose this at any time.

So, you need to copy the following into a file named cmake_header.txt, and place it somewhere useful (this will be used in every exported mbed project too)

cmake_header.txt

CMAKE_MINIMUM_REQUIRED(VERSION 2.8.4)

#custom command to use objcopy to create .bin files out of ELF files
function(make_mbed_firmware INPUT)
		      add_custom_command(TARGET ${INPUT}
					  COMMAND arm-none-eabi-objcopy -O binary ${INPUT} ${INPUT}_${MBED_TARGET}.bin
					  COMMENT "objcopying to make mbed compatible firmware")
		      set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${INPUT}_${MBED_TARGET}.bin)
endfunction(make_mbed_firmware)

#assume we're using an LPC1768 model if it's not specified by -DMBED_TARGET= 
 if( NOT MBED_TARGET  MATCHES "LPC1768" AND NOT MBED_TARGET MATCHES "LPC2368" AND NOT MBED_TARGET MATCHES "LPC11U24")
   message(STATUS "invalid or no mbed target specified. Options are LPC1768, LPC2368 or LPC11U24. Assuming LPC1768 for now.
			Target may be specified using -DMBED_TARGET=")
   set(MBED_TARGET "LPC1768")
endif( NOT MBED_TARGET  MATCHES "LPC1768" AND NOT MBED_TARGET MATCHES "LPC2368" AND NOT MBED_TARGET MATCHES "LPC11U24")

set(MBED_INCLUDE "${CMAKE_SOURCE_DIR}/mbed/${MBED_TARGET}/GCC_CS/")

#setup target specific object files
if(MBED_TARGET MATCHES "LPC1768")
    set(MBED_PREFIX "LPC17")
    set(CORE "cm3")
    set(CHIP ${MBED_INCLUDE}sys.o
	    ${MBED_INCLUDE}startup_LPC17xx.o)
elseif(MBED_TARGET MATCHES "LPC2368")
    set(CHIP  ${MBED_INCLUDE}vector_functions.o
	      ${MBED_INCLUDE}vector_realmonitor.o
	      ${MBED_INCLUDE}vector_table.o)
	set(MBED_PREFIX "LPC23")
	set(CORE "arm7")
elseif(MBED_TARGET MATCHES "LPC11U24")
    set(CHIP 	${MBED_INCLUDE}sys.o
		${MBED_INCLUDE}startup_LPC11xx.o)
       set(CORE "cm0")
       set(MBED_PREFIX "LPC11U")
endif(MBED_TARGET MATCHES "LPC1768")

#setup precompiled mbed files which will be needed for all projects
set(CHIP     ${CHIP}
	    ${MBED_INCLUDE}system_${MBED_PREFIX}xx.o
	    ${MBED_INCLUDE}cmsis_nvic.o
	    ${MBED_INCLUDE}core_${CORE}.o)

#force min size build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING
        "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
        FORCE)
endif(NOT CMAKE_BUILD_TYPE)

#set correct linker script
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \"-T${CMAKE_SOURCE_DIR}/mbed/${MBED_TARGET}/GCC_CS/${MBED_TARGET}.ld\" -static")

#find CodeSourcery Toolchain for appropriate include dirs
find_path(CSPATH arm-none-eabi-g++ PATHS ENV)
message(STATUS "${CSPATH} is where CodeSourcery is installed")

#setup directories for  appropriate  C, C++, mbed libraries and includes
include_directories(${MBED_INCLUDE})
include_directories(mbed)
include_directories(${CSPATH}../arm-none-eabi/include)
include_directories(${CSPATH}../arm-none-eabi/include/c++/4.6.1)
link_directories(${MBED_INCLUDE})


AS NOTED ABOVE: only LPC1768 works at the moment, but this script I believe goes most of the way to sorting the other two out, just need some more information

CMakeLists.txt file

This is cmake's equivalent of a Makefile, and you will want one in the root of each project's directory. I have provided an example below. It's fairly easy to understand, but a good basic tutorial on cmake is found http://www.cmake.org/cmake/help/cmake_tutorial.html here . The things you want to change are the project name (it's called foo in the example) and the names of the source files (main.cpp in the example). The ${CHIP} variable should e left in each add_executable command (it includes everything needed from the mbed libraries), and the mbed and capi libraries should be linked to everything. The make_mbed_firmware command creates a suitable image to be flashed onto the mbed. You will have to alter the path of the include command if cmake_header.txt is not in the same directory

CMakeLists.txt

project (foo C CXX ASM)

include(cmake_header.txt)

cmake_minimum_required(VERSION 2.8.4)

add_executable(mbed_kitt main.cpp  ${CHIP})
target_link_libraries(mbed_kitt mbed capi)
make_mbed_firmware(mbed_kitt)

more executables can be added with more add_executable commands.

Configuring and running cmake

At last, we're ready to build stuff! I recommend you use the cmake-gui to configure cmake, but it can all be done by the command line (shown how below). I recomment out of source builds in a separate directory. The commands given work in Linux, I'm sure the Windows equivalent is similar. Sometime when I'm booted into Windows I'll try and write them specifically.

command line method

**from the source directory**
mkdir build
cd build

**configure cmake. Set toolchain file  properly **
ccmake .. -DCMAKE_TOOLCHAIN_FILE=../codesourcery_toolchain.cmake

**ccmake curses interface appears. You need to press C to load the variables,
then press C again to configure, then press G to generate the CMakeCache and CMakeFiles **

C C G

**this will assume an LPC1768 and generate a Makefile. Once it's working we can also use e.g. -DMBED_TARGET=LPC11U24 **

cmake .

** build the software. Using make VERBOSE=1 is a good way to check where things are going wrong **

make

** copy the resulting .bin file to mbed as with normal firmware **

**to clean the build directory for a new build **

make clean


All wikipages