mirror of
https://github.com/iNavFlight/inav.git
synced 2025-07-13 19:40:27 +03:00
[BUILD] Add support for openocd helpers with cmake
- Add openocd_<target>: runs openocd for the target - Add openocd_flash_<target>: flashes the target using openocd. It works with both an already running openocd instance as well as launching its own one. Uses a helper tool that requires python. - Add openocd_cfg_<target>: generates openocd config for target. Used for generating an openocd config automatically when launching a debug session from an IDE.
This commit is contained in:
parent
fb9f61a583
commit
d6177e6933
10 changed files with 211 additions and 11 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,6 +8,7 @@
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
.cproject
|
.cproject
|
||||||
|
__pycache__
|
||||||
startup_stm32f10x_md_gcc.s
|
startup_stm32f10x_md_gcc.s
|
||||||
.vagrant/
|
.vagrant/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
|
@ -47,6 +47,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
set(FIRMWARE_VERSION ${PROJECT_VERSION})
|
set(FIRMWARE_VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
include(settings)
|
include(settings)
|
||||||
|
include(openocd)
|
||||||
include(main)
|
include(main)
|
||||||
include(stm32)
|
include(stm32)
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,14 @@ function(setup_firmware_target name)
|
||||||
)
|
)
|
||||||
get_property(targets GLOBAL PROPERTY VALID_TARGETS)
|
get_property(targets GLOBAL PROPERTY VALID_TARGETS)
|
||||||
set_property(GLOBAL PROPERTY VALID_TARGETS "${targets} ${name}")
|
set_property(GLOBAL PROPERTY VALID_TARGETS "${targets} ${name}")
|
||||||
|
setup_openocd(${name})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(exclude_from_all target)
|
||||||
|
set_property(TARGET ${target} PROPERTY
|
||||||
|
TARGET_MESSAGES OFF
|
||||||
|
EXCLUDE_FROM_ALL 1
|
||||||
|
EXCLUDE_FROM_DEFAULT_BUILD 1)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(collect_targets)
|
function(collect_targets)
|
||||||
|
@ -61,8 +69,5 @@ function(collect_targets)
|
||||||
set(list_target_name "targets")
|
set(list_target_name "targets")
|
||||||
add_custom_target(${list_target_name}
|
add_custom_target(${list_target_name}
|
||||||
COMMAND cmake -E echo "Valid targets: ${targets}")
|
COMMAND cmake -E echo "Valid targets: ${targets}")
|
||||||
set_property(TARGET ${list_target_name} PROPERTY
|
exclude_from_all(${list_target_name})
|
||||||
TARGET_MESSAGES OFF
|
|
||||||
EXCLUDE_FROM_ALL 1
|
|
||||||
EXCLUDE_FROM_DEFAULT_BUILD 1)
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
84
cmake/openocd.cmake
Normal file
84
cmake/openocd.cmake
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
set(OPENOCD "" CACHE STRING "path to openocd (default: search for it)")
|
||||||
|
set(OPENOCD_CFG "" CACHE STRING "path to openocd configuration (default: generate automatically)")
|
||||||
|
set(OPENOCD_INTERFACE "" CACHE STRING "openocd interface name (default: automatic depending on target)")
|
||||||
|
|
||||||
|
if (OPENOCD)
|
||||||
|
set(OPENOCD_PATH ${OPENOCD})
|
||||||
|
else()
|
||||||
|
find_program(OPENOCD_FOUND_PATH NAMES openocd openocd.exe)
|
||||||
|
if (NOT OPENOCD_FOUND_PATH)
|
||||||
|
message(STATUS "Could not find openocd, debugging won't be available")
|
||||||
|
else()
|
||||||
|
set(OPENOCD_PATH ${OPENOCD_FOUND_PATH})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(OPENOCD_PATH)
|
||||||
|
# Retrieve version number as a sanity check
|
||||||
|
execute_process(COMMAND ${OPENOCD_PATH} -v
|
||||||
|
OUTPUT_QUIET
|
||||||
|
ERROR_VARIABLE OPENOCD_HELP
|
||||||
|
RESULT_VARIABLE OPENOCD_RESULT)
|
||||||
|
|
||||||
|
string(REPLACE "\n" ";" OPENOCD_HELP_LINES ${OPENOCD_HELP})
|
||||||
|
list(GET OPENOCD_HELP_LINES 0 OPENOCD_FIRST_HELP_LINE)
|
||||||
|
string(REPLACE "\r" "" OPENOCD_HELP_LINE ${OPENOCD_FIRST_HELP_LINE})
|
||||||
|
if (NOT OPENOCD_RESULT EQUAL 0)
|
||||||
|
# User provided an incorrect path
|
||||||
|
message(FATAL_ERROR "error executing ${OPENOCD_PATH} (${OPENOCD_RESULT})")
|
||||||
|
endif()
|
||||||
|
message(STATUS "using openocd: ${OPENOCD_HELP_LINE}")
|
||||||
|
add_custom_target(openocd ${OPENOCD_PATH} -f ${OPENOCD_CFG}
|
||||||
|
COMMENT "Run openocd using OPENOCD_CFG=(${OPENOCD_CFG}) as configuration"
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(setup_openocd target_name)
|
||||||
|
if(OPENOCD_INTERFACE)
|
||||||
|
set(openocd_interface ${OPENOCD_INTERFACE})
|
||||||
|
else()
|
||||||
|
get_property(openocd_interface TARGET ${target_name} PROPERTY OPENOCD_DEFAULT_INTERFACE)
|
||||||
|
endif()
|
||||||
|
get_property(openocd_target TARGET ${target_name} PROPERTY OPENOCD_TARGET)
|
||||||
|
if(OPENOCD_CFG OR (openocd_target AND openocd_interface))
|
||||||
|
set(openocd_run_target "openocd_${target_name}")
|
||||||
|
if (OPENOCD_CFG AND NOT OPENOCD_CFG STREQUAL "")
|
||||||
|
get_filename_component(openocd_cfg_path ${OPENOCD_CFG}
|
||||||
|
ABSOLUTE
|
||||||
|
BASE_DIR ${CMAKE_BINARY_DIR})
|
||||||
|
else()
|
||||||
|
set(openocd_cfg_path ${CMAKE_BINARY_DIR}/openocd/${target_name}.cfg)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${openocd_cfg_path}
|
||||||
|
COMMENT "Generating openocd configuration for ${openocd_target} via ${openocd_interface}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -P ${MAIN_DIR}/cmake/openocd_cfg.cmake
|
||||||
|
${openocd_target} ${openocd_interface} ${openocd_cfg_path}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Target for openocd configuration
|
||||||
|
set(openocd_cfg_target "openocd_cfg_${target_name}")
|
||||||
|
add_custom_target(${openocd_cfg_target} DEPENDS ${openocd_cfg_path})
|
||||||
|
exclude_from_all(${openocd_cfg_target})
|
||||||
|
|
||||||
|
# Target for running openocd
|
||||||
|
add_custom_target(${openocd_run_target} ${OPENOCD_PATH} -f ${openocd_cfg_path}
|
||||||
|
COMMENT "Running openocd for target ${target_name} via ${openocd_interface}"
|
||||||
|
DEPENDS ${openocd_cfg_path}
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
exclude_from_all(${openocd_run_target})
|
||||||
|
# Target for flashing via openocd
|
||||||
|
set(openocd_flash_target "openocd_flash_${target_name}")
|
||||||
|
add_custom_target(${openocd_flash_target} ${CMAKE_COMMAND} -E env
|
||||||
|
OPENOCD_CMD=${OPENOCD_PATH}
|
||||||
|
${MAIN_UTILS_DIR}/openocd_flash.py -f
|
||||||
|
${openocd_cfg_path} $<TARGET_FILE:${target_name}>
|
||||||
|
|
||||||
|
COMMENT "Flashing ${target_name} with openocd"
|
||||||
|
DEPENDS ${openocd_cfg_path} ${target_name}
|
||||||
|
)
|
||||||
|
exclude_from_all(${openocd_flash_target})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
20
cmake/openocd_cfg.cmake
Normal file
20
cmake/openocd_cfg.cmake
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# This is called from the targets that build the
|
||||||
|
# openocd.cfg file
|
||||||
|
if(NOT CMAKE_ARGC EQUAL 6)
|
||||||
|
message(FATAL_ERROR "usage: cmake -P openocd_cfg.cmake <target> <interface> <output>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(OPENOCD_TARGET ${CMAKE_ARGV3})
|
||||||
|
set(OPENOCD_INTERFACE ${CMAKE_ARGV4})
|
||||||
|
set(OUTPUT ${CMAKE_ARGV5})
|
||||||
|
|
||||||
|
set(opencd_cfg)
|
||||||
|
list(APPEND openocd_cfg "source [find interface/${OPENOCD_INTERFACE}.cfg]")
|
||||||
|
list(APPEND openocd_cfg "source [find target/${OPENOCD_TARGET}.cfg]")
|
||||||
|
list(APPEND openocd_cfg "init")
|
||||||
|
list(APPEND openocd_cfg "arm semihosting enable")
|
||||||
|
list(APPEND openocd_cfg "reset halt")
|
||||||
|
list(JOIN openocd_cfg "\n" contents)
|
||||||
|
set(contents "${contents}\n")
|
||||||
|
|
||||||
|
file(WRITE ${OUTPUT} ${contents})
|
|
@ -157,11 +157,12 @@ function(target_stm32 name startup ldscript)
|
||||||
# Parse keyword arguments
|
# Parse keyword arguments
|
||||||
cmake_parse_arguments(
|
cmake_parse_arguments(
|
||||||
PARSED_ARGS
|
PARSED_ARGS
|
||||||
"DISABLE_MSC" # Boolean arguments
|
"DISABLE_MSC" # Boolean arguments
|
||||||
"HSE_MHZ" # Single value arguments
|
"HSE_MHZ;OPENOCD_TARGET" # Single value arguments
|
||||||
"DEFINITIONS" # Multi-value arguments
|
"DEFINITIONS" # Multi-value arguments
|
||||||
${ARGN} # Start parsing after the known arguments
|
${ARGN} # Start parsing after the known arguments
|
||||||
)
|
)
|
||||||
|
|
||||||
if (PARSED_ARGS_HSE_MHZ)
|
if (PARSED_ARGS_HSE_MHZ)
|
||||||
set(hse_mhz ${PARSED_ARGS_HSE_MHZ})
|
set(hse_mhz ${PARSED_ARGS_HSE_MHZ})
|
||||||
else()
|
else()
|
||||||
|
@ -210,6 +211,8 @@ function(target_stm32 name startup ldscript)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
set_property(TARGET ${name} PROPERTY OPENOCD_TARGET ${PARSED_ARGS_OPENOCD_TARGET})
|
||||||
|
set_property(TARGET ${name} PROPERTY OPENOCD_DEFAULT_INTERFACE stlink)
|
||||||
# Generate .hex
|
# Generate .hex
|
||||||
# XXX: Generator expressions are not supported for add_custom_command()
|
# XXX: Generator expressions are not supported for add_custom_command()
|
||||||
# OUTPUT nor BYPRODUCTS, so we can't rely of them. Instead, build the filename
|
# OUTPUT nor BYPRODUCTS, so we can't rely of them. Instead, build the filename
|
||||||
|
|
|
@ -60,7 +60,7 @@ set(STM32F303_DEFINITIONS
|
||||||
|
|
||||||
function(target_stm32f3xx name startup ldscript)
|
function(target_stm32f3xx name startup ldscript)
|
||||||
# F3 targets don't support MSC
|
# F3 targets don't support MSC
|
||||||
target_stm32(${name} ${startup} ${ldscript} DISABLE_MSC ${ARGN})
|
target_stm32(${name} ${startup} ${ldscript} DISABLE_MSC OPENOCD_TARGET stm32f3x ${ARGN})
|
||||||
# F3 targets don't use -O2 to save size
|
# F3 targets don't use -O2 to save size
|
||||||
if (IS_RELEASE_BUILD)
|
if (IS_RELEASE_BUILD)
|
||||||
target_compile_options(${name} PRIVATE "-Os")
|
target_compile_options(${name} PRIVATE "-Os")
|
||||||
|
|
|
@ -76,7 +76,7 @@ set(STM32F4_DEFINITIONS
|
||||||
)
|
)
|
||||||
|
|
||||||
function(target_stm32f4xx name startup ldscript)
|
function(target_stm32f4xx name startup ldscript)
|
||||||
target_stm32(${name} ${startup} ${ldscript} ${ARGN})
|
target_stm32(${name} ${startup} ${ldscript} OPENOCD_TARGET stm32f4x ${ARGN})
|
||||||
if (IS_RELEASE_BUILD)
|
if (IS_RELEASE_BUILD)
|
||||||
target_compile_options(${name} PRIVATE "-O2")
|
target_compile_options(${name} PRIVATE "-O2")
|
||||||
target_link_options(${name} PRIVATE "-O2")
|
target_link_options(${name} PRIVATE "-O2")
|
||||||
|
|
|
@ -83,7 +83,7 @@ set(STM32F7_DEFINITIONS
|
||||||
)
|
)
|
||||||
|
|
||||||
function(target_stm32f7xx name startup ldscript)
|
function(target_stm32f7xx name startup ldscript)
|
||||||
target_stm32(${name} ${startup} ${ldscript} ${ARGN})
|
target_stm32(${name} ${startup} ${ldscript} OPENOCD_TARGET stm32f7x ${ARGN})
|
||||||
if (IS_RELEASE_BUILD)
|
if (IS_RELEASE_BUILD)
|
||||||
target_compile_options(${name} PRIVATE "-O2")
|
target_compile_options(${name} PRIVATE "-O2")
|
||||||
target_link_options(${name} PRIVATE "-O2")
|
target_link_options(${name} PRIVATE "-O2")
|
||||||
|
|
86
src/utils/openocd_flash.py
Executable file
86
src/utils/openocd_flash.py
Executable file
|
@ -0,0 +1,86 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def openocd_telnet_await_prompt(s):
|
||||||
|
prev = None
|
||||||
|
while True:
|
||||||
|
b = s.recv(1)
|
||||||
|
if b == '':
|
||||||
|
# Closed
|
||||||
|
return False
|
||||||
|
if prev == '>' and b == ' ':
|
||||||
|
# Prompt for next command
|
||||||
|
return True
|
||||||
|
prev = b
|
||||||
|
print(b, end='')
|
||||||
|
|
||||||
|
def openocd_telnet_command(s, cmd):
|
||||||
|
s.send(cmd + '\n')
|
||||||
|
openocd_telnet_await_prompt(s)
|
||||||
|
|
||||||
|
def openocd_flash_telnet(port, filename):
|
||||||
|
try:
|
||||||
|
s = socket.create_connection(('localhost', port))
|
||||||
|
except socket.error:
|
||||||
|
return False
|
||||||
|
|
||||||
|
openocd_telnet_await_prompt(s)
|
||||||
|
openocd_telnet_command(s, 'halt')
|
||||||
|
openocd_telnet_command(s, 'program {} verify reset\n'.format(filename))
|
||||||
|
openocd_telnet_command(s, 'exit')
|
||||||
|
s.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def openocd_flash_cmd(openocd, args, filename):
|
||||||
|
cmd = [openocd]
|
||||||
|
cmd.extend(args)
|
||||||
|
cmd.extend(('-c', 'program {} verify reset exit'.format(filename)))
|
||||||
|
status = subprocess.call(cmd)
|
||||||
|
return status == 0
|
||||||
|
|
||||||
|
def usage():
|
||||||
|
print('Usage: {} <openocd_args> <elf_file>'.format(sys.argv[0]))
|
||||||
|
print('Environment variables: OPENOCD_CMD = path to openocd')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Default openocd telnet port
|
||||||
|
# TODO: Parse arguments and check if we
|
||||||
|
# should use a non-default port
|
||||||
|
port = 4444
|
||||||
|
openocd = os.environ.get('OPENOCD_CMD') or 'openocd'
|
||||||
|
|
||||||
|
openocd_args = []
|
||||||
|
flag = None
|
||||||
|
elf = None
|
||||||
|
for arg in sys.argv[1:]:
|
||||||
|
if flag:
|
||||||
|
openocd_args.append(arg)
|
||||||
|
flag = None
|
||||||
|
else:
|
||||||
|
if arg.startswith('-'):
|
||||||
|
openocd_args.append(arg)
|
||||||
|
flag = arg
|
||||||
|
elif elf is None:
|
||||||
|
elf = arg
|
||||||
|
else:
|
||||||
|
usage()
|
||||||
|
|
||||||
|
if len(openocd_args) == 0 or elf is None:
|
||||||
|
usage()
|
||||||
|
|
||||||
|
if not openocd_flash_telnet(port, elf):
|
||||||
|
if not openocd_flash_cmd(openocd, openocd_args, elf):
|
||||||
|
print('could not flash')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Add table
Add a link
Reference in a new issue