mirror of
https://github.com/iNavFlight/inav.git
synced 2025-07-13 03:19:58 +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
|
||||
.settings
|
||||
.cproject
|
||||
__pycache__
|
||||
startup_stm32f10x_md_gcc.s
|
||||
.vagrant/
|
||||
.vscode/
|
||||
|
|
|
@ -47,6 +47,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
|||
set(FIRMWARE_VERSION ${PROJECT_VERSION})
|
||||
|
||||
include(settings)
|
||||
include(openocd)
|
||||
include(main)
|
||||
include(stm32)
|
||||
|
||||
|
|
|
@ -53,6 +53,14 @@ function(setup_firmware_target name)
|
|||
)
|
||||
get_property(targets GLOBAL PROPERTY VALID_TARGETS)
|
||||
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()
|
||||
|
||||
function(collect_targets)
|
||||
|
@ -61,8 +69,5 @@ function(collect_targets)
|
|||
set(list_target_name "targets")
|
||||
add_custom_target(${list_target_name}
|
||||
COMMAND cmake -E echo "Valid targets: ${targets}")
|
||||
set_property(TARGET ${list_target_name} PROPERTY
|
||||
TARGET_MESSAGES OFF
|
||||
EXCLUDE_FROM_ALL 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD 1)
|
||||
exclude_from_all(${list_target_name})
|
||||
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
|
||||
cmake_parse_arguments(
|
||||
PARSED_ARGS
|
||||
"DISABLE_MSC" # Boolean arguments
|
||||
"HSE_MHZ" # Single value arguments
|
||||
"DEFINITIONS" # Multi-value arguments
|
||||
${ARGN} # Start parsing after the known arguments
|
||||
"DISABLE_MSC" # Boolean arguments
|
||||
"HSE_MHZ;OPENOCD_TARGET" # Single value arguments
|
||||
"DEFINITIONS" # Multi-value arguments
|
||||
${ARGN} # Start parsing after the known arguments
|
||||
)
|
||||
|
||||
if (PARSED_ARGS_HSE_MHZ)
|
||||
set(hse_mhz ${PARSED_ARGS_HSE_MHZ})
|
||||
else()
|
||||
|
@ -210,6 +211,8 @@ function(target_stm32 name startup ldscript)
|
|||
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
|
||||
# XXX: Generator expressions are not supported for add_custom_command()
|
||||
# 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)
|
||||
# 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
|
||||
if (IS_RELEASE_BUILD)
|
||||
target_compile_options(${name} PRIVATE "-Os")
|
||||
|
|
|
@ -76,7 +76,7 @@ set(STM32F4_DEFINITIONS
|
|||
)
|
||||
|
||||
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)
|
||||
target_compile_options(${name} PRIVATE "-O2")
|
||||
target_link_options(${name} PRIVATE "-O2")
|
||||
|
|
|
@ -83,7 +83,7 @@ set(STM32F7_DEFINITIONS
|
|||
)
|
||||
|
||||
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)
|
||||
target_compile_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