Embedded Development Board Learning


Visual Studio Code for ARM with CMake #3 Toolchain CMake

Fecha: enero 24, 2023

Autor: Guillermo Garcia

Categorías: Visual Studio Code Etiquetas:

Visual Studio Code como entorno de desarrollo para proyecto Embedded Systems

Hemos definido una estructura de proyecto además de agregar los archivos fuente necesarios en el artículo anterior Visual Studio Code for ARM with CMake #2 Proyecto. En este articulo vamos a configurar el entorno de compilación Toolchain CMake.

Toolchain CMake File

Para obtener un archivo ejecutable primero necesitamos invocar al compilador GCC indicando que archivos fuente se deben compilar para generar un archivo ejecutable. Interactuar con el compilador directamente médiate comandos suele ser poco práctico en el desarrollo de software donde los archivos fuente aumentan conforme crece un proyecto.

Como hacemos más ágil el proceso de interactuar con el compilador pues para eso emplearemos CMake.

CMake

CMake se usa para controlar el proceso de compilación de software mediante una plataforma simple y archivos de configuración independientes del compilador.

El compilador GNU C/C++ GCC esta basado en GNU make esto hace que sea posible gestionar el proceso de compilación mediante la utilidad make. La utilidad make requiere un archivo makefile que defina un conjunto de tareas a ejecutar.

Crear el archivo makefile no es algo complicado pero requiere algo de tiempo en escribir la rutina necesaria además de conocer el entorno GNU, para hacer esto más simple utilizamos CMake.

CMake genera canalizaciones de compilación (build pipelines) es decir un archivo para un sub entorno de compilación, en conclusión en el proceso de compilación CMake generara un archivo makefile con las instrucciones necesarias para que el entorno GNU make interactúe con el compilador y este genere el archivo ejecutable.

Proceso de compilación CMake

Archivos Toolchain CMake

Todos los proyectos basados en CMake contiene un script llamado CMakeLists.txt en este archivo se describen las instrucciones para realizar la compilación. También tenemos que indicarle a CMake donde están instaladas nuestras herramientas de compilación para esto agregamos un archivo llamado Buildtools.cmake.

Archivos CMake agregados

Archivo CMakeLists.txt

Creamos el archivo CMakeLists.txt vamos a describir principalmente los archivos fuente que deseamos sean compilados, así como parámetros específicos de nuestro hardware.

cmake_minimum_required(VERSION 3.23.1)

# Optional: print out extra messages to see what is going on. Comment it to have less verbose messages
set(CMAKE_VERBOSE_MAKEFILE ON)

# Path to toolchain file. This one has to be before 'project()' below
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/Buildtools.cmake)

# Setup project, output and linker file
project(VSC_G071RB)
set(EXECUTABLE ${PROJECT_NAME}.elf)
set(LINKER_FILE ${CMAKE_SOURCE_DIR}/Device/STM32G071RBTX_FLASH.ld)

enable_language(C ASM)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

# Optional: issue a message to be sure it uses the correct toolchain file.
message(STATUS "CMAKE_TOOLCHAIN_FILE is: ${CMAKE_TOOLCHAIN_FILE}")

# List of source files
set(SRC_FILES
        main/main.c

        Device/stm32g0xx_it.c
        Device/system_stm32g0xx.c
        Device/startup_stm32g071xx.s
        Device/stm32g0xx_hal_msp.c
        
        Drivers/Src/stm32g0xx_hal.c 
        Drivers/Src/stm32g0xx_hal_rcc.c 
        Drivers/Src/stm32g0xx_hal_rcc_ex.c 
        Drivers/Src/stm32g0xx_hal_flash.c
        Drivers/Src/stm32g0xx_hal_flash_ex.c
        Drivers/Src/stm32g0xx_hal_dma.c
        Drivers/Src/stm32g0xx_hal_dma_ex.c
        Drivers/Src/stm32g0xx_hal_gpio.c 
        Drivers/Src/stm32g0xx_hal_cortex.c 
        Drivers/Src/stm32g0xx_hal_exti.c 
        Drivers/Src/stm32g0xx_hal_tim.c 
        Drivers/Src/stm32g0xx_hal_tim_ex.c 
        Drivers/Src/stm32g0xx_hal_pwr.c
        Drivers/Src/stm32g0xx_hal_pwr_ex.c
        )

# Build the executable based on the source files
add_executable(${EXECUTABLE} ${SRC_FILES})

# List of compiler defines, prefix with -D compiler option
target_compile_definitions(${EXECUTABLE} PRIVATE
        -DSTM32G071xx
        -DUSE_HAL_DRIVER
        )

# List of includ directories
target_include_directories(${EXECUTABLE} PRIVATE
        main
        CMSIS/Include
        CMSIS/Device/ST/STM32G0xx
        Device
        Drivers/Inc/
        Drivers/Inc/Legacy
        )

# Compiler options
target_compile_options(${EXECUTABLE} PRIVATE
        -mcpu=cortex-m0
        -mthumb
        -mfloat-abi=soft
        -fdata-sections
        -ffunction-sections
        
        -Wall
        -O0
        -Og
        -gdwarf-2
        )

# Linker options
target_link_options(${EXECUTABLE} PRIVATE
        -T${LINKER_FILE}
        -mcpu=cortex-m0
        -mthumb
        -mfloat-abi=soft
        --specs=rdimon.specs
        --specs=nano.specs
        -lc
        -lm
        -lnosys
        -Wl,-Map=${PROJECT_NAME}.map,--cref
        -Wl,--gc-sections
        -Xlinker -print-memory-usage -Xlinker
        )

# Optional: Print executable size as part of the post build process
add_custom_command(TARGET ${EXECUTABLE}
        POST_BUILD
        COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE})

# Optional: Create hex, bin and S-Record files after the build
add_custom_command(TARGET ${EXECUTABLE}
        POST_BUILD
        COMMAND ${CMAKE_OBJCOPY} -O srec --srec-len=64 ${EXECUTABLE} ${PROJECT_NAME}.s19
        COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex
        COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE} ${PROJECT_NAME}.bin)

Archivo Buildtools.cmake

Creamos el archivo Buildtools.cmake para que CMake conozca donde está instalado el compilador.

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)

set(ARM_TOOLCHAIN_DIR "C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin")
set(BINUTILS_PATH ${ARM_TOOLCHAIN_DIR}) 

set(TOOLCHAIN_PREFIX ${ARM_TOOLCHAIN_DIR}/arm-none-eabi-)

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

set(CMAKE_C_COMPILER "${TOOLCHAIN_PREFIX}gcc.exe")
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PREFIX}g++.exe")

set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy CACHE INTERNAL "objcopy tool")
set(CMAKE_SIZE_UTIL ${TOOLCHAIN_PREFIX}size CACHE INTERNAL "size tool")

set(CMAKE_FIND_ROOT_PATH ${BINUTILS_PATH})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

Pasos de configuración y generación

Cuando ejecutamos CMake se realizan dos pasos importantes primero se ejecuta un proceso de configuración (Configure step) donde se crean archivos internos de CMake como el archivo CMakeCache.txt. El segundo proceso es de generación (Generate step) es donde se obtiene un archivo makefile equivalente para nuestro proyecto.

Archivos CMake agregados

El CMakeLists.txt script se ejecuta durante el paso de configuración. Este script se encarga de definir los target (Archivos fuente). Cada target representa un ejecutable, una biblioteca o algún otro resultado de la canalización de compilación (build pipelines).

Si el paso de configuración tiene éxito, es decir, CMakeLists.txt se completó sin errores, CMake generará una canalización de compilación (build pipelines) utilizando los objetivos definidos por el script.

El proceso de configuración solo debe realizarse una vez, sino se desea limpiar toda la compilación esto es gracias al cache almacenado en el archivo CMakeCache.txt.

Compilando

Ahora vamos a ejecutar CMake observemos el resultado de la compilación.

Proceso Configuración

Para iniciar CMake indicaremos dos parámetros importantes primero CMAKE_MAKE_PROGRAM para indicar que utilizamos generador de makefile, CMAKE_TOOLCHAIN_FILE indicando el archivo donde se encuentran las herramientas de compilación.

Nos dirigiremos a la carpeta build en nuestro proyecto y ejecutamos el comando:

C:/VSC_G071RB\build>cmake -DCMAKE_MAKE_PROGRAM=make.exe -DCMAKE_TOOLCHAIN_FILE="Buildtools.cmake" -G "Unix Makefiles" ..
" -G "Unix Makefiles" ..
-- The C compiler identification is GNU 10.3.1
-- The CXX compiler identification is GNU 10.3.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-gcc.exe - skipped
-- Detecting C compile features       
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-g++.exe - skipped
-- Detecting CXX compile features       
-- Detecting CXX compile features - done
-- The ASM compiler identification is GNU
-- Found assembler: C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-gcc.exe
-- CMAKE_TOOLCHAIN_FILE is: C:/VSC_G071RB/Buildtools.cmake
-- Configuring done
-- Generating done
-- Build files have been written to: C:/VSC_G071RB/build

Vemos como esto nos genera archivos dentro de la carpeta build :

Archivos de configuración CMake

Si observamos el contenido de la carpeta build aun no tenemos un archivo ejecutable, esto es debido a que aún no se invoca al compilador.

Proceso Generación

Es momento de compilar el proyecto aquí es donde los archivos makefile generados por CMake son necesarios ejecutamos el comando:

C:\VSC_G071RB\build>cmake --build .
"C:/Program Files/CMake/bin/cmake.exe" -SC:/VSC_G071RB -BC:/VSC_G071RB/build --check-build-system CMakeFiles/Makefile.cmake 0
"C:/Program Files/CMake/bin/cmake.exe" -E cmake_progress_start C:/VSC_G071RB/build/CMakeFiles C:/VSC_G071RB/build//CMakeFiles/progress.marks
make.exe  -f CMakeFiles/Makefile2 all
make.exe[1]: Entering directory `/c/VSC_G071RB/build'
make.exe  -f CMakeFiles/VSC_G071RB.elf.dir/build.make CMakeFiles/VSC_G071RB.elf.dir/depend
make.exe[2]: Entering directory `/c/VSC_G071RB/build'
"C:/Program Files/CMake/bin/cmake.exe" -E cmake_depends "Unix Makefiles" C:/VSC_G071RB C:/VSC_G071RB C:/VSC_G071RB/build C:/VSC_G071RB/build C:/VSC_G071RB/build/CMakeFiles/VSC_G071RB.elf.dir/DependInfo.cmake --color=
Scanning dependencies of target VSC_G071RB.elf
make.exe[2]: Leaving directory `/c/VSC_G071RB/build'
make.exe  -f CMakeFiles/VSC_G071RB.elf.dir/build.make CMakeFiles/VSC_G071RB.elf.dir/build
make.exe[2]: Entering directory `/c/VSC_G071RB/build'
[  5%] Building C object CMakeFiles/VSC_G071RB.elf.dir/main/main.c.obj
[ 95%] Building C object CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_pwr_ex.c.obj
"C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-gcc.exe" -DSTM32G071xx -DUSE_HAL_DRIVER -IC:/Users/urie_/Documents/FreeRTOS/VSC_G071RB/main -IC:/CMSIS/Include -IC:/Users/urie_/Documents/FreeRTOS/VSC_G071RB/CMSIS/Device/ST/STM32G0xx -IC:/VSC_G071RB/Device -IC:/VSC_G071RB/Drivers/Inc -IC:/VSC_G071RB/Drivers/Inc/Legacy -mcpu=cortex-m0 -mthumb -mfloat-abi=soft -fdata-sections -ffunction-sections -Wall -O0 -Og -gdwarf-2 -std=c99 -MD -MT CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_pwr_ex.c.obj -MF CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_pwr_ex.c.obj.d -o CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_pwr_ex.c.obj -c C:/Users/urie_/Documents/FreeRTOS/VSC_G071RB/Drivers/Src/stm32g0xx_hal_pwr_ex.c
[100%] Linking C executable VSC_G071RB.elf
"C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-gcc.exe" -TC:/Users/urie_/Documents/FreeRTOS/VSC_G071RB/Device/STM32G071RBTX_FLASH.ld -mcpu=cortex-m0 -mthumb -mfloat-abi=soft --specs=rdimon.specs --specs=nano.specs -lc -lm -lnosys -Wl,-Map=VSC_G071RB.map,--cref -Wl,--gc-sections -Xlinker -print-memory-usage "CMakeFiles/VSC_G071RB.elf.dir/main/main.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Device/stm32g0xx_it.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Device/system_stm32g0xx.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Device/startup_stm32g071xx.s.obj" "CMakeFiles/VSC_G071RB.elf.dir/Device/stm32g0xx_hal_msp.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_rcc.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_rcc_ex.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_flash.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_flash_ex.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_dma.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_dma_ex.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_gpio.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_cortex.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_exti.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_tim.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_tim_ex.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_pwr.c.obj" "CMakeFiles/VSC_G071RB.elf.dir/Drivers/Src/stm32g0xx_hal_pwr_ex.c.obj"  -o VSC_G071RB.elf
Memory region         Used Size  Region Size  %age Used
             RAM:        1600 B        36 KB      4.34%
           FLASH:        3740 B       128 KB      2.85%
"C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-size" VSC_G071RB.elf
   text    data     bss     dec     hex filename
   3720      20    1588    5328    14d0 VSC_G071RB.elf
"C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-objcopy" -O srec --srec-len=64 VSC_G071RB.elf VSC_G071RB.s19
"C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-objcopy" -O ihex VSC_G071RB.elf VSC_G071RB.hex
"C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin/arm-none-eabi-objcopy" -O binary VSC_G071RB.elf VSC_G071RB.bin
make.exe[2]: Leaving directory `/c/Users/urie_/Documents/FreeRTOS/VSC_G071RB/build'
[100%] Built target VSC_G071RB.elf
make.exe[1]: Leaving directory `/c/Users/urie_/Documents/FreeRTOS/VSC_G071RB/build'
"C:/Program Files/CMake/bin/cmake.exe" -E cmake_progress_start
 C:/VSC_G071RB/build/CMakeFiles 0 

Si no hay errores en la compilación observamos nuestro archivo ejecutable dentro del directorio build.

Archivos ejecutables generados


Card image cap
Guillermo Garcia I am an embedded systems software engineer. I like constant learning, IoT systems and sharing knowledge


Comentarios... no existen comentarios.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subscribe


Subscribe to receive the latest content.
Loading