Skip to content

How to create an application and use the Open IoT SDK

This document will walk you through the process of creating a simple application that prints Hello world to Corstone-300's serial terminal. It aims to give you a basic idea of how to create a CMake project and use components from the Open IoT SDK.

Overview

A project for an application that runs on a microcontroller often consists of the following items:

  • Application code
  • Libraries, such as
    • HAL (hardware abstraction layer) and drivers
    • Real-time Operating System (RTOS)
    • Middleware components, such as cloud clients
    • Others
  • Linker scripts
  • Additional firmware components, such as Trusted-Firmware M (if enabled)
  • Build scripts

Some of those items are optional. For example, the simple "Hello world" application that we will be creating in this document will be bare-metal (no RTOS), not connecting any cloud services (no cloud clients) and without Trusted-Firmware M enabled.

The Open IoT SDK is capable of fetching and enabling a number of components (i.e. libraries as well as Trusted-Firmware M) and pre-integrating some of those components together. The way to specify which components to enable will be described later in the document. The user can also add their own libraries as needed.

Currently, the application's build scripts need to be provided in CMake, because the Open IoT SDK is a CMake based project.

Creating a CMake project for your application

To start, you need to create a new CMake project:

  1. Create a new directory for the project and navigate to the directory.
  2. In the newly created directory, create a file named CMakeLists.txt, and begin the file with the following line:

    cmake_minimum_required(VERSION 3.21.0)
    

    It specifies the minimum CMake version that is supported by the Open IoT SDK.

Adding toolchain support

Currently, the Open IoT SDK project provides support for Arm Compiler 6 and GNU Arm Toolchain. It does so by providing a set of CMake files that set toolchain flags specific to each of them.

  1. Fetch toolchain support, by adding the following lines to CMakeLists.txt:

    include(FetchContent)
    FetchContent_Declare(iotsdk-toolchains
        GIT_REPOSITORY  https://git.gitlab.arm.com/iot/open-iot-sdk/toolchain.git
        GIT_TAG         main
        SOURCE_DIR      ${CMAKE_BINARY_DIR}/toolchains
    )
    FetchContent_MakeAvailable(iotsdk-toolchains)
    

    You can inspect the CMake toolchain files (toolchain-armclang.cmake and toolchain-arm-none-eabi-gcc.cmake) by opening the URL in the snippet above.

  2. Set the processor of your MCU target, in lowercase.

    For example, the processor of the Corstone-300 platform is cortex-m55. Add the following line to CMakeLists.txt:

    set(CMAKE_SYSTEM_PROCESSOR cortex-m55)
    
  3. Declare your project with a name of your choice. For example, add the following line to CMakeLists.txt:

    project(my_project)
    

    Note

    This must be done after you have added toolchain support and set the target processor, because CMake processes toolchain and processor options during the project() call.

Specifying software components

The Open IoT SDK is capable of fetching and adding a number of software components from other repositories. Your application project needs to tell the SDK which components to fetch. To do so,

  1. Go to the Open IoT SDK's cmake/DeclareSdkComponents.cmake file to see which components are supported. The first argument of each FetchContent_Declare() is the name of the component.
  2. Note down the component(s) you would like to use.

    In this simple example, we will only print a Hello message to Corstone-300's serial terminal, and the repository containing hardware support including serial support for Corstone-300 is cmsis-pack-utils.

    Note

    CMSIS-Pack-Utils is always available in the Open-IoT-SDK, therefore we do not need to include it in the IOTSDK_FETCH_LIST variable.

  3. Go back to your own project's CMakeLists.txt which you have created earlier, and set the IOTSDK_FETCH_LIST variable to a list of components you would like to fetch.

Adding the Open IoT SDK

First, we need to set a correct device identifier, SSE-300-MPS3.

Add the Open IoT SDK, by adding the following lines to your CMakeLists.txt:

set(CMSIS_PACK_PLATFORM_DEVICE_NAME SSE-300-MPS3)

FetchContent_Declare(
    open-iot-sdk
    GIT_REPOSITORY  https://git.gitlab.arm.com/iot/open-iot-sdk/sdk.git
    GIT_TAG         main
)
FetchContent_MakeAvailable(open-iot-sdk)

list(APPEND CMAKE_MODULE_PATH ${open-iot-sdk_SOURCE_DIR}/cmake)

This will also cause the components you have listed in IOTSDK_FETCH_LIST to be fetched and added automatically when you build the project.

Specifying libraries delivered via CMSIS-Packs

We will provide hardware support for Corstone-300 via the dedicated, platform-specific Open-CMSIS-Pack named V2M_MPS3_SSE_300_BSP. To have it available for our project, we will call add_cmsis_library() that is delivered by cmsis-pack-utils component. Please, take a look at How to use section for more details.

Several Open-CMSIS-Pack components are necessary for our demo: - From ARM::CMSIS@5.9.0 package: - ARM::CMSIS:CORE@5.6.0 CMSIS-CORE for all Cortex-M, including Cortex-M55, - From ARM::V2M_MPS3_SSE_300_BSP@1.3.0 package: - ARM::Device:Definition@1.2.0 SSE-300-MPS3 device definitions, - ARM::Device:Startup&Baremetal@1.2.0 Secure System and Startup for ARM SSE-300-MPS3 device, - ARM::Native Driver:SysCounter@1.1.0 Native SysCounter driver for SSE-300, - ARM::Native Driver:SysTimer@1.1.0 Native SysTimer driver for SSE-300, - ARM::Native Driver:Timeout@1.0.0 Systimer timeout driver for SSE-300, - ARM::Native Driver:UART@1.1.0 Native UART driver for SSE-300, - ARM::CMSIS Driver:USART@1.0.0 USART driver for SSE-300.

Add the following lines to your project's CMakeLists.txt:

set(OPEN_CMSIS_PACKS_TO_FETCH
    "ARM::CMSIS@5.9.0"
    "ARM::V2M_MPS3_SSE_300_BSP@1.3.0"
)

set(OPEN_CMSIS_PACK_COMPONENTS
    "ARM::CMSIS Driver:USART@1.0.0"
    "ARM::CMSIS:CORE@5.6.0"
    "ARM::Device:Definition@1.2.0"
    "ARM::Device:Startup&Baremetal@1.2.0"
    "ARM::Native Driver:SysCounter@1.1.0"
    "ARM::Native Driver:SysTimer@1.1.0"
    "ARM::Native Driver:Timeout@1.0.0"
    "ARM::Native Driver:UART@1.1.0"
)

add_cmsis_library(
    corstone-300-hardware-support
    ${CMSIS_PACK_PLATFORM_DEVICE_NAME}
    AC6
    OPEN_CMSIS_PACKS_TO_FETCH
    OPEN_CMSIS_PACK_COMPONENTS
)

(If you would like to use GNU Arm Toolchain instead, replace AC6 with GCC in the code snippet above.)

The result of add_cmsis_library() is a CMake library target named corstone-300-hardware-support.

Creating an application

At minimum, you need a C file containing the main() function and a linker script containing the memory layout:

  1. Inside your project's directory, create a main.c file with the following content:

    #include <stdio.h>
    
    int main(void)
    {
        printf("Hello world\r\n");
        return 0;
    }
    
  2. Download armclang.sct (for Arm Compiler 6) and/or gcc.ld (for GNU Arm Toolchain).

  3. Move the file(s) you have just downloaded into your project's directory.

    Note

    To keep this tutorial as simple as possible, we do not write our own linker scripts here because they are relatively complex.

  4. Declare your application and add the source file, by adding the following to your project's CMakeLists.txt:

    add_executable(my_application main.c)
    
  5. Link your application against libraries it needs.

    In this example, we need * iotsdk-serial-retarget, which retargets printf() to the serial output. This library is defined in the SDK's utils/CMakeLists.txt. * corstone-300-hardware-support, which provides the startup file needed for the boot sequence and interrupt handling, and CMSIS-Drivers implementation (including the serial) for Corstone-300. This is provided with the use of cmsis-pack-utils tool as we have specified above.

    Add the following lines to your project's CMakeLists.txt:

    target_link_libraries(my_application
        iotsdk-serial-retarget
        corstone-300-hardware-support
    )
    
  6. Link required libraries against their dependencies.

    The iotsdk-serial-retarget library also depends on platform-specific CMSIS-Driver implementation.

    Add the following lines to your project's CMakeLists.txt:

    target_link_libraries(iotsdk-serial-retarget
        PRIVATE
            corstone-300-hardware-support
    )
    
  7. Finally, set the linker flags to use the linker scripts:

    if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
        set(linker_script ${CMAKE_CURRENT_LIST_DIR}/gcc.ld)
        target_link_options(my_application PRIVATE -T ${linker_script})
    elseif(CMAKE_C_COMPILER_ID STREQUAL "ARMClang")
        set(linker_script ${CMAKE_CURRENT_LIST_DIR}/armclang.sct)
        target_link_options(my_application PRIVATE --scatter=${linker_script})
    endif()
    set_target_properties(my_application PROPERTIES LINK_DEPENDS ${linker_script})
    

    Note

    The elseif code block is not needed if you do not have Arm Compiler 6.

    At build time, CMake will set CMAKE_C_COMPILER_ID depending on which toolchain you use, and the snippet above will set the linker flag accordingly. ${CMAKE_CURRENT_LIST_DIR}/ is prepended to the linker scripts to make the paths absolute, because the build process will take place in a different working directory. The set_target_properties() directive ensures that CMake reruns the linker if the linker script has been modified since the last build.

Building and running the application

To configure this example using Arm Compiler 6:

cmake -B __build -GNinja --toolchain toolchains/toolchain-armclang.cmake

(If you would like to use GNU Arm Toolchain instead, replace toolchains/toolchain-armclang.cmake with toolchains/toolchain-arm-none-eabi-gcc.cmake in the command above.)

Once the command above has completed, run the build command:

cmake --build __build

Once the build is done, run the application:

FVP_Corstone_SSE-300_Ethos-U55 __build/my_application.elf

The FVP should open a serial terminal and print:

Hello world

Next steps

Having created and built your own project, you can go on to read advanced tips on building CMake based projects.

You can also have a look at the examples for the Open IoT SDK for more complex CMake projects that use various software components supported by the Open IoT SDK.