Hello folks,
I’m trying to create a CMake build for the CORTEX_MPU_M3_MPS2_QEMU_GCC demo. In case it matters, I’m doing this because I intend to use FreeRTOS and QEMU in another CMake based project of mine and am using this as a learning step to get the build working.
I’ve uploaded my mini project to github KartikAiyer/freertos_qemu_demo for your reference (new user so can’t add links)
I have a toolchain file that will download the ARM toolchain v13.2.Rel1 and use it for the CMake build. I have also provided a env.sh
which you can source to setup your path to run the FreeRTOS supplied makefile based build of the demo.
I’m currently trying this on an M1 based Macbook air but I’ve setup the build to work on Linux
as well (though I haven’t tried it on Linux).
My Problem
I generate the build using both the FreeRTOS supplied Makefile for the Demo and with my CMake build. When I launch my CMake based and connect GDB to it, I can see that my code calls the HardFault_Handler
the moment it tries to create the stack variable
TaskParameters_t xROAccessTaskParameters =
{
.pvTaskCode = prvROAccessTask,
.pcName = "ROAccess",
.usStackDepth = configMINIMAL_STACK_SIZE,
.pvParameters = NULL,
.uxPriority = tskIDLE_PRIORITY,
.puxStackBuffer = xROAccessTaskStack,
.xRegions =
{
{ ucSharedMemory, SHARED_MEMORY_SIZE,
portMPU_REGION_PRIVILEGED_READ_WRITE_UNPRIV_READ_ONLY |
portMPU_REGION_EXECUTE_NEVER },
{ ( void * ) ucROTaskFaultTracker, SHARED_MEMORY_SIZE,
portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER },
{ 0, 0, 0},
}
};
When I launch the build generated using the FreeRTOS based Makefile, the code does not crash.
I’ve tried to make my CMakeLists.txt file do exactly what the Makefile does and I’m not able to
trace why there is a difference. I tried comparing the output.map files from both builds it looks
like symbols are created at different addresses.
I could really use some help with debugging this.
Build Files
In case you can’t look up the GitHub repo, I’m providing my CMakeLists.txt file here
cmake_minimum_required(VERSION 3.22)
project(qemu_demo C)
set(FREERTOS_SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/FreeRTOS/FreeRTOS/Source)
set(FREERTOS_PORTABLE_DIR "${FREERTOS_SOURCES_DIR}/portable")
set(FREERTOS_PORTABLE_M3_DIR "${FREERTOS_PORTABLE_DIR}/GCC/ARM_CM3_MPU")
set(FREERTOS_QEMU_MPS2_DIR ${CMAKE_CURRENT_SOURCE_DIR}/FreeRTOS/FreeRTOS/Demo/CORTEX_MPU_M3_MPS2_QEMU_GCC)
list(APPEND
sources
"${FREERTOS_QEMU_MPS2_DIR}/init/startup.c"
"${FREERTOS_QEMU_MPS2_DIR}/syscall.c"
"${FREERTOS_QEMU_MPS2_DIR}/main.c"
"${FREERTOS_QEMU_MPS2_DIR}/app_main.c"
"${FREERTOS_QEMU_MPS2_DIR}/mpu_demo.c"
"${FREERTOS_PORTABLE_M3_DIR}/port.c"
"${FREERTOS_PORTABLE_M3_DIR}/mpu_wrappers_v2_asm.c"
"${FREERTOS_PORTABLE_DIR}/Common/mpu_wrappers.c"
"${FREERTOS_PORTABLE_DIR}/Common/mpu_wrappers_v2.c"
"${FREERTOS_PORTABLE_DIR}/MemMang/heap_4.c"
"${FREERTOS_SOURCES_DIR}/list.c"
"${FREERTOS_SOURCES_DIR}/tasks.c"
"${FREERTOS_SOURCES_DIR}/queue.c"
"${FREERTOS_SOURCES_DIR}/timers.c"
"${FREERTOS_SOURCES_DIR}/event_groups.c"
"${FREERTOS_SOURCES_DIR}/stream_buffer.c"
)
list(APPEND
includeDirs
"${FREERTOS_QEMU_MPS2_DIR}"
"${FREERTOS_QEMU_MPS2_DIR}/CMSIS"
"${FREERTOS_SOURCES_DIR}/include"
"${FREERTOS_PORTABLE_M3_DIR}"
)
list(APPEND
DEFINES
-DHEAP_4
)
list(APPEND
CFLAGS
-mthumb
-mcpu=cortex-m3
-Wall
-Wextra
-Wshadow
-Wno-unused-parameter
-g3
-Og
-ffunction-sections
-fdata-sections
-save-temps=obj
)
list(APPEND
LDFLAGS
"${CFLAGS}"
-T ${FREERTOS_QEMU_MPS2_DIR}/scripts/mps2_m3.ld
-Wl,-Map=${CMAKE_BINARY_DIR}/output.map,--gc-sections
-nostartfiles -nostdlib -nolibc -nodefaultlibs
)
add_executable(qemu_demo "${sources}")
target_include_directories(qemu_demo PRIVATE "${includeDirs}")
target_compile_definitions(qemu_demo PRIVATE "${DEFINES}")
target_compile_options(qemu_demo PRIVATE "${CFLAGS}")
target_link_options(qemu_demo PRIVATE "${LDFLAGS}")
And this the Corresponding Makefile
CC = arm-none-eabi-gcc
SIZE = arm-none-eabi-size
BIN := RTOSDemo.axf
BUILD_DIR := build
FREERTOS_DIR_REL := ../../../FreeRTOS
FREERTOS_DIR := $(abspath $(FREERTOS_DIR_REL))
KERNEL_DIR := $(FREERTOS_DIR)/Source
# Startup sources
SOURCE_FILES += init/startup.c syscall.c main.c
# platform portable source file
SOURCE_FILES += $(KERNEL_DIR)/portable/GCC/ARM_CM3_MPU/port.c
SOURCE_FILES += $(KERNEL_DIR)/portable/GCC/ARM_CM3_MPU/mpu_wrappers_v2_asm.c
# Kernel source files
SOURCE_FILES += $(KERNEL_DIR)/portable/Common/mpu_wrappers.c
SOURCE_FILES += $(KERNEL_DIR)/portable/Common/mpu_wrappers_v2.c
SOURCE_FILES += $(KERNEL_DIR)/tasks.c
SOURCE_FILES += $(KERNEL_DIR)/list.c
SOURCE_FILES += $(KERNEL_DIR)/queue.c
SOURCE_FILES += $(KERNEL_DIR)/timers.c
SOURCE_FILES += $(KERNEL_DIR)/event_groups.c
SOURCE_FILES += ${KERNEL_DIR}/portable/MemMang/heap_4.c
SOURCE_FILES += $(KERNEL_DIR)/stream_buffer.c
# application source files
SOURCE_FILES += app_main.c
SOURCE_FILES += mpu_demo.c
INCLUDE_DIRS += -I$(FREERTOS_DIR)/Demo/CORTEX_MPU_M3_MPS2_QEMU_GCC
INCLUDE_DIRS += -I$(FREERTOS_DIR)/Demo/CORTEX_MPU_M3_MPS2_QEMU_GCC/CMSIS
INCLUDE_DIRS += -I$(KERNEL_DIR)/include
INCLUDE_DIRS += -I$(KERNEL_DIR)/portable/GCC/ARM_CM3_MPU
DEFINES := -DHEAP4
CPPFLAGS += $(DEFINES)
CFLAGS += -mthumb -mcpu=cortex-m3
CFLAGS += -Wall -Wextra -Wshadow -Wno-unused-parameter
#CFLAGS += -Wpedantic -fanalyzer
CFLAGS += $(INCLUDE_DIRS)
LDFLAGS = -T ./scripts/mps2_m3.ld
LDFLAGS += -Xlinker -Map=${BUILD_DIR}/output.map
LDFLAGS += -Xlinker --gc-sections
LDFLAGS += -nostartfiles -nostdlib -nolibc -nodefaultlibs
ifeq ($(DEBUG), 1)
CFLAGS += -g3 -Og -ffunction-sections -fdata-sections -save-temps=obj
else
CFLAGS += -Os -ffunction-sections -fdata-sections
endif
ifeq ($(PICOLIBC), 1)
CFLAGS += --specs=picolibc.specs -DPICOLIBC_INTEGER_PRINTF_SCANF
LDFLAGS += --specs=picolibc.specs -DPICOLIBC_INTEGER_PRINTF_SCANF
endif
OBJ_FILES := $(SOURCE_FILES:%.c=$(BUILD_DIR)/%.o)
.PHONY: clean
$(BUILD_DIR)/$(BIN) : $(OBJ_FILES)
$(CC) $(CFLAGS) $(LDFLAGS) $+ -o $(@)
$(SIZE) $(@)
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
INCLUDES := $(SOURCE_FILES:%.c=$(BUILD_DIR)/%.d)
-include $(INCLUDES)
${BUILD_DIR}/%.o : %.c Makefile
-mkdir -p $(@D)
$(CC) $(CFLAGS) $(CPPFLAGS) -MMD -c $< -o $@
clean:
-rm -rf build
This is the toolchain.cmake file
# Set the desired ARM toolchain version
set(ARM_TOOLCHAIN_VERSION "13.2.Rel1")
# Set the download directory
set(ARM_TOOLCHAIN_PARENT_DIR "${CMAKE_BINARY_DIR}/arm-toolchain")
message(STATUS "Looking and downloading for toolchain")
set(HOST_CHIP_VERSION "")
if(APPLE)
message(STATUS "Selecting Apple Silicon host compiler toolchain for ${CMAKE_HOST_SYSTEM_PROCESSOR}")
set(HOST_CHIP_VERSION "darwin-${CMAKE_HOST_SYSTEM_PROCESSOR}")
elseif(CMAKE_HOST_WIN32)
if(CMAKE_GENERATOR MATCHES "MinGW")
set(HOST_CHIP_VERSION "mingw-w64-i686")
else()
message(FATAL_ERROR "Only MinGW Windows generator supported")
endif()
else()
message(
STATUS "Selecting Linux Host Chip version ${CMAKE_HOST_SYSTEM_PROCESSOR}")
set(HOST_CHIP_VERSION "${CMAKE_HOST_SYSTEM_PROCESSOR}")
endif()
# Set the URL to download the ARM toolchain
set(ARM_TOOLCHAIN_URL
"https://developer.arm.com/-/media/Files/downloads/gnu/${ARM_TOOLCHAIN_VERSION}/binrel/arm-gnu-toolchain-${ARM_TOOLCHAIN_VERSION}-${HOST_CHIP_VERSION}-arm-none-eabi.tar.xz"
)
set(ARM_TOOLCHAIN_FULL_NAME_DIR "${ARM_TOOLCHAIN_PARENT_DIR}/arm-gnu-toolchain-${ARM_TOOLCHAIN_VERSION}-${HOST_CHIP_VERSION}-arm-none-eabi")
set(ARM_TOOLCHAIN_DIR "${ARM_TOOLCHAIN_PARENT_DIR}/arm-gnu-toolchain")
# Set the toolchain paths
set(ARM_TOOLCHAIN_BIN_DIR
"${ARM_TOOLCHAIN_DIR}/bin"
)
# Create a custom command to download the ARM toolchain
if(NOT EXISTS "${ARM_TOOLCHAIN_BIN_DIR}/arm-none-eabi-gcc")
message(STATUS "Downloading ARM toolchain from ${ARM_TOOLCHAIN_URL}")
file(DOWNLOAD "${ARM_TOOLCHAIN_URL}"
"${ARM_TOOLCHAIN_PARENT_DIR}/arm-toolchain.tar.xz" SHOW_PROGRESS)
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xf
"${ARM_TOOLCHAIN_PARENT_DIR}/arm-toolchain.tar.xz"
WORKING_DIRECTORY "${ARM_TOOLCHAIN_PARENT_DIR}")
execute_process(COMMAND mv "${ARM_TOOLCHAIN_FULL_NAME_DIR}" "${ARM_TOOLCHAIN_DIR}" WORKING_DIRECTORY "${ARM_TOOLCHAIN_PARENT_DIR}")
endif()
# Set the target system root
set(CMAKE_FIND_ROOT_PATH "${ARM_TOOLCHAIN_DIR}")
# Search for programs only in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# Disable compiler checks
set(CMAKE_C_COMPILER_WORKS TRUE)
set(CMAKE_CXX_COMPILER_WORKS TRUE)
set(CMAKE_C_ABI_COMPILED TRUE)
set(CMAKE_CXX_ABI_COMPILED TRUE)
set(CMAKE_C_COMPILER "${ARM_TOOLCHAIN_BIN_DIR}/arm-none-eabi-gcc")
set(CMAKE_CXX_COMPILER "${ARM_TOOLCHAIN_BIN_DIR}/arm-none-eabi-g++")
set(CMAKE_ASM_COMPILER "${ARM_TOOLCHAIN_BIN_DIR}/arm-none-eabi-gcc")
# Set the target system and architecture
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
# Add the toolchain include directories
include_directories(
"${ARM_TOOLCHAIN_DIR}/arm-none-eabi/include"
)
# Add the toolchain library directories
link_directories(
"${ARM_TOOLCHAIN_DIR}/arm-none-eabi/lib"
)
Running My CMAKE Build
Launching QEMU
qemu-system-arm -machine mps2-an385 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel ./build/qemu_demo -serial stdio -nographic -s -S
I then launch GDB
arm-none-eabi-gdb -q ./build/qemu_demo
Reading symbols from ./build/qemu_demo...
(gdb) target remote:1234
Remote debugging using :1234
Reset_Handler () at /Users/kartikaiyer/fun/qemu_demo/FreeRTOS/FreeRTOS/Demo/CORTEX_MPU_M3_MPS2_QEMU_GCC/init/startup.c:50
50 __asm volatile ( "ldr r0, =_estack" );
(gdb) b HardFault_Handler
Breakpoint 1 at 0x10584: file /Users/kartikaiyer/fun/qemu_demo/FreeRTOS/FreeRTOS/Demo/CORTEX_MPU_M3_MPS2_QEMU_GCC/init/startup.c, line 130.
(gdb) c
Continuing.
Breakpoint 1, HardFault_Handler ()
at /Users/kartikaiyer/fun/qemu_demo/FreeRTOS/FreeRTOS/Demo/CORTEX_MPU_M3_MPS2_QEMU_GCC/init/startup.c:130
130 __asm volatile
(gdb)
Running the Makefile Build
The following is executed in the Demo folder
Launch QEMU
qemu-system-arm -machine mps2-an385 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel ./build/RTOSDemo.axf -serial stdio -nographic -s -S
Launching GDB
arm-none-eabi-gdb -q ./build/RTOSDemo.axf
Reading symbols from ./build/RTOSDemo.axf...
(gdb) target remote:1234
Remote debugging using :1234
Reset_Handler () at init/startup.c:50
50 __asm volatile ( "ldr r0, =_estack" );
(gdb) b HardFault_Handler
Breakpoint 1 at 0x10614: file init/startup.c, line 130.
(gdb) c
Continuing.
As you can see above, there is no crash.