Refactor upload methods to streamline the debugging experience (#129)

* Change all debug configurations to not reset and exit on error.  Initial tasks.json implementation.

* st-util working on command line and VS Code!

* Convert STM32CUBE, fix spaces in GDB server task command

* pyocd working!

* Convert OpenOCD

* Convert JLINK

* Convert REDLINK upload method

* Fix CMake typo

* Try and fix some variable scope issues

* Initial CLion implementation.  Need to test with hardware.

* Add a separate task that kicks off a build before each debug session
pull/15437/head
Jamie Smith 2023-02-20 00:16:15 -08:00 committed by GitHub
parent 141aaec603
commit eda766ec8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 353 additions and 99 deletions

View File

@ -14,6 +14,14 @@ set(UPLOAD_METHOD "${UPLOAD_METHOD_DEFAULT}" CACHE STRING "Method for uploading
# use a higher numbered port to allow use without root on Linux/Mac # use a higher numbered port to allow use without root on Linux/Mac
set(GDB_PORT 23331 CACHE STRING "Port that the GDB server will be started on.") set(GDB_PORT 23331 CACHE STRING "Port that the GDB server will be started on.")
# Load the upload method. This is expected to set the following variables:
# UPLOAD_${UPLOAD_METHOD}_FOUND - True iff the dependencies for this upload method were found
# UPLOAD_SUPPORTS_DEBUG - True iff this upload method supports debugging
# UPLOAD_GDBSERVER_DEBUG_COMMAND - Command to start a new GDB server
# UPLOAD_WANTS_EXTENDED_REMOTE - True iff GDB should use "target extended-remote" to connect to the GDB server
# UPLOAD_LAUNCH_COMMANDS - List of GDB commands to run after launching GDB.
# UPLOAD_RESTART_COMMANDS - List of commands to run when the "restart chip" function is used.
# See here for more info: https://github.com/mbed-ce/mbed-os/wiki/Debugger-Commands-and-State-in-Upload-Methods
include(UploadMethod${UPLOAD_METHOD}) include(UploadMethod${UPLOAD_METHOD})
if(NOT "${UPLOAD_${UPLOAD_METHOD}_FOUND}") if(NOT "${UPLOAD_${UPLOAD_METHOD}_FOUND}")
@ -24,46 +32,27 @@ if(NOT (("${UPLOAD_METHOD}" STREQUAL NONE) OR ("${${UPLOAD_METHOD}_UPLOAD_ENABLE
message(FATAL_ERROR "The upload method ${UPLOAD_METHOD} is not enabled in the config code for this target -- set ${UPLOAD_METHOD}_UPLOAD_ENABLED to TRUE to enable it.") message(FATAL_ERROR "The upload method ${UPLOAD_METHOD} is not enabled in the config code for this target -- set ${UPLOAD_METHOD}_UPLOAD_ENABLED to TRUE to enable it.")
endif() endif()
message(STATUS "Board upload method set to ${UPLOAD_METHOD}")
# ----------------------------------------------
# Generate gdbinit if needed
if(UPLOAD_SUPPORTS_DEBUG) if(UPLOAD_SUPPORTS_DEBUG)
# create init file for GDB client message(STATUS "Mbed: Code upload and debugging enabled via upload method ${UPLOAD_METHOD}")
if(UPLOAD_WANTS_EXTENDED_REMOTE) elseif(NOT "${UPLOAD_METHOD}" STREQUAL "NONE")
set(UPLOAD_GDB_REMOTE_KEYWORD "extended-remote") message(STATUS "Mbed: Code upload enabled via upload method ${UPLOAD_METHOD}")
else()
set(UPLOAD_GDB_REMOTE_KEYWORD "remote")
endif()
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit CONTENT
"# connect to GDB server
target ${UPLOAD_GDB_REMOTE_KEYWORD} localhost:${GDB_PORT}
")
endif() endif()
# UPLOAD_SUPPORTS_DEBUG needs to be made into a cache variable so that it can # These variables need to be made into a cache variables so that they can
# be seen by higher level directories when they call mbed_generate_upload_debug_targets() # be seen by higher level directories when they call mbed_generate_upload_debug_targets()
set(MBED_UPLOAD_SUPPORTS_DEBUG ${UPLOAD_SUPPORTS_DEBUG} CACHE INTERNAL "" FORCE) set(MBED_UPLOAD_SUPPORTS_DEBUG ${UPLOAD_SUPPORTS_DEBUG} CACHE INTERNAL "" FORCE)
set(MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND ${UPLOAD_GDBSERVER_DEBUG_COMMAND} CACHE INTERNAL "" FORCE)
set(MBED_UPLOAD_WANTS_EXTENDED_REMOTE ${UPLOAD_WANTS_EXTENDED_REMOTE} CACHE INTERNAL "" FORCE)
set(MBED_UPLOAD_LAUNCH_COMMANDS ${UPLOAD_LAUNCH_COMMANDS} CACHE INTERNAL "" FORCE)
set(MBED_UPLOAD_RESTART_COMMANDS ${UPLOAD_RESTART_COMMANDS} CACHE INTERNAL "" FORCE)
# ---------------------------------------------- # ----------------------------------------------
# Function for creating targets # Function for creating targets
function(mbed_generate_upload_debug_targets target) function(mbed_generate_upload_target target)
# add upload target # add upload target
gen_upload_target(${target} gen_upload_target(${target}
${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${target}>.bin ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${target}>.bin
${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${target}>.hex ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${target}>.hex
) )
# add debug target
if(MBED_UPLOAD_SUPPORTS_DEBUG AND MBED_GDB_FOUND)
add_custom_target(debug-${target}
COMMENT "starting GDB to debug ${target}..."
COMMAND ${MBED_GDB}
--command=${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit
$<TARGET_FILE:${target}>
USES_TERMINAL)
endif()
endfunction() endfunction()

View File

@ -7,13 +7,21 @@
# ------------------------------------------------------------- # -------------------------------------------------------------
if($ENV{CLION_IDE}) if($ENV{CLION_IDE})
message(STATUS "Detected CLion IDE, will generate CLion debug configurations") message(STATUS "Mbed: Detected CLion IDE, will generate CLion debug configurations")
set(MBED_GENERATE_CLION_DEBUG_CFGS TRUE) set(MBED_GENERATE_CLION_DEBUG_CFGS TRUE)
set(MBED_CLION_PROFILE_NAME "" CACHE STRING "Name of the Clion build profile (Settings > Build, Execution, Deployment > CMake > Name textbox")
if(MBED_CLION_PROFILE_NAME STREQUAL "")
message(FATAL_ERROR "In order to generate CLion configuration files, Mbed CE needs to know the name of the current CLion build profile. This name is the string typed into the Name textbox under Settings > Build, Execution, Deployment > CMake. Pass this name with '-DMBED_CLION_PROFILE_NAME=<name>'.")
endif()
elseif(CMAKE_EXPORT_COMPILE_COMMANDS) # TODO: Is this actually a reliable way of detecting VS Code? Not sure if it will create false positives. elseif(CMAKE_EXPORT_COMPILE_COMMANDS) # TODO: Is this actually a reliable way of detecting VS Code? Not sure if it will create false positives.
message(STATUS "Detected VS Code IDE, will generate VS Code debug configurations") message(STATUS "Mbed: Detected VS Code IDE, will generate VS Code debug configurations")
set(MBED_GENERATE_VS_CODE_DEBUG_CFGS TRUE) set(MBED_GENERATE_VS_CODE_DEBUG_CFGS TRUE)
elseif(MBED_UPLOAD_SUPPORTS_DEBUG)
message(STATUS "Mbed: No IDE detected, will generate configurations for command-line debugging (e.g. ninja gdbserver, then ninja debug-SomeProgram)")
endif() endif()
# CLion generator # CLion generator
@ -31,11 +39,43 @@ if(MBED_GENERATE_CLION_DEBUG_CFGS)
set(CONFIG_NAME "GDB ${CMAKE_TARGET} ${MBED_TARGET} ${CMAKE_BUILD_TYPE}") set(CONFIG_NAME "GDB ${CMAKE_TARGET} ${MBED_TARGET} ${CMAKE_BUILD_TYPE}")
set(RUN_CONF_PATH "${CLION_RUN_CONF_DIR}/${CONFIG_NAME}.xml") set(RUN_CONF_PATH "${CLION_RUN_CONF_DIR}/${CONFIG_NAME}.xml")
# Convert the CMake list into the correct format for the run configuration XML
list(GET MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 0 GDBSERVER_EXECUTABLE)
list(SUBLIST MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 1 -1 GDBSERVER_ARGS)
set(GDBSERVER_ARGS_STR "")
set(IS_FIRST_ARG TRUE)
foreach(ELEMENT ${GDBSERVER_ARGS})
if(IS_FIRST_ARG)
set(IS_FIRST_ARG FALSE)
else()
string(APPEND GDBSERVER_ARGS_STR " ")
endif()
# Escape quotes and ampersands
string(REPLACE "\"" "&quot;" ELEMENT "${ELEMENT}")
string(REPLACE "&" "&amp;" ELEMENT "${ELEMENT}")
if("${ELEMENT}" MATCHES " ")
string(APPEND GDBSERVER_ARGS_STR "&quot;${ELEMENT}&quot;")
else()
string(APPEND GDBSERVER_ARGS_STR "${ELEMENT}")
endif()
endforeach()
# Generate run configuration XML file.
# This file is based on a generic Embedded GDB Server run configuration generated by CLion,
# with constant strings replaced by placeholders for CMake.
file(GENERATE OUTPUT ${RUN_CONF_PATH} CONTENT file(GENERATE OUTPUT ${RUN_CONF_PATH} CONTENT
"<!-- Autogenerated by Mbed OS. Do not edit! --> "<!-- Autogenerated by Mbed OS. Do not edit! -->
<component name=\"ProjectRunConfigurationManager\"> <component name=\"ProjectRunConfigurationManager\">
<configuration default=\"false\" name=\"${CONFIG_NAME}\" type=\"CLion_Remote\" remoteCommand=\"localhost:${GDB_PORT}\" symbolFile=\"$<TARGET_FILE:${CMAKE_TARGET}>\" sysroot=\"\"> <configuration default=\"false\" name=\"${CONFIG_NAME}\" type=\"com.jetbrains.cidr.embedded.customgdbserver.type\" PROGRAM_PARAMS=\"${GDBSERVER_ARGS_STR}\" REDIRECT_INPUT=\"false\" ELEVATE=\"false\" USE_EXTERNAL_CONSOLE=\"false\" PASS_PARENT_ENVS_2=\"true\" PROJECT_NAME=\"${PROJECT_NAME}\" TARGET_NAME=\"${CMAKE_TARGET}\" CONFIG_NAME=\"${MBED_CLION_PROFILE_NAME}\" version=\"1\" RUN_TARGET_PROJECT_NAME=\"${PROJECT_NAME}\" RUN_TARGET_NAME=\"${CMAKE_TARGET}\">
<method v=\"2\" /> <custom-gdb-server version=\"1\" gdb-connect=\"localhost:${GDB_PORT}\" executable=\"${GDBSERVER_EXECUTABLE}\" warmup-ms=\"0\" download-type=\"UPDATED_ONLY\" reset-cmd=\"monitor reset\" reset-type=\"AFTER_DOWNLOAD\">
<debugger kind=\"GDB\" isBundled=\"true\" />
</custom-gdb-server>
<method v=\"2\">
<option name=\"CLION.COMPOUND.BUILD\" enabled=\"true\" />
</method>
</configuration> </configuration>
</component> </component>
") ")
@ -55,9 +95,16 @@ elseif(MBED_GENERATE_VS_CODE_DEBUG_CFGS)
# Start building up json file. Needs to be a global property so we can append to it from anywhere. # Start building up json file. Needs to be a global property so we can append to it from anywhere.
# Note: Cannot use a cache variable for this because cache variables aren't allowed to contain newlines. # Note: Cannot use a cache variable for this because cache variables aren't allowed to contain newlines.
set_property(GLOBAL PROPERTY VSCODE_LAUNCH_JSON_CONTENT set_property(GLOBAL PROPERTY VSCODE_LAUNCH_JSON_CONTENT
"{ "// Auto-generated by Mbed CE. Edits will be erased when CMake is rerun.
{
\"configurations\": [") \"configurations\": [")
set_property(GLOBAL PROPERTY VSCODE_TASKS_JSON_CONTENT
"// Auto-generated by Mbed CE. Edits will be erased when CMake is rerun.
{
\"version\": \"2.0.0\",
\"tasks\": [")
# Find objdump as the extension uses it. In a sane world it should be in the compiler bin dir. # Find objdump as the extension uses it. In a sane world it should be in the compiler bin dir.
find_program(MBED_OBJDUMP find_program(MBED_OBJDUMP
NAMES arm-none-eabi-objdump objdump NAMES arm-none-eabi-objdump objdump
@ -68,10 +115,15 @@ elseif(MBED_GENERATE_VS_CODE_DEBUG_CFGS)
function(mbed_generate_ide_debug_configuration CMAKE_TARGET) function(mbed_generate_ide_debug_configuration CMAKE_TARGET)
# Create name (combine target name, Mbed target, and build config to generate a unique string) # Create name (combine target name, Mbed target, and build config to generate a unique string)
set(CONFIG_NAME "Connect to GDB ${CMAKE_TARGET} ${MBED_TARGET} ${CMAKE_BUILD_TYPE}") set(CONFIG_NAME "Debug ${CMAKE_TARGET} ${MBED_TARGET} ${CMAKE_BUILD_TYPE}")
# Convert CMake lists to json
list(JOIN MBED_UPLOAD_LAUNCH_COMMANDS "\", \"" UPLOAD_LAUNCH_COMMANDS_FOR_JSON)
list(JOIN MBED_UPLOAD_RESTART_COMMANDS "\", \"" UPLOAD_RESTART_COMMANDS_FOR_JSON)
# property list here: https://github.com/Marus/cortex-debug/blob/master/debug_attributes.md # property list here: https://github.com/Marus/cortex-debug/blob/master/debug_attributes.md
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_LAUNCH_JSON_CONTENT " set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_LAUNCH_JSON_CONTENT "
// Debug launch for target ${CMAKE_TARGET}.
{ {
\"type\": \"cortex-debug\", \"type\": \"cortex-debug\",
\"name\": \"${CONFIG_NAME}\", \"name\": \"${CONFIG_NAME}\",
@ -81,13 +133,36 @@ elseif(MBED_GENERATE_VS_CODE_DEBUG_CFGS)
\"objdumpPath\": \"${MBED_OBJDUMP}\", \"objdumpPath\": \"${MBED_OBJDUMP}\",
\"servertype\": \"external\", \"servertype\": \"external\",
\"gdbTarget\": \"localhost:${GDB_PORT}\", \"gdbTarget\": \"localhost:${GDB_PORT}\",
\"request\": \"attach\" \"request\": \"launch\",
\"preLaunchTask\": \"Build ${CMAKE_TARGET} and start GDB server\",
// Override the command sequences used by VS Code to be correct for this GDB server
\"overrideLaunchCommands\": [\"${UPLOAD_LAUNCH_COMMANDS_FOR_JSON}\"],
\"overrideRestartCommands\": [\"${UPLOAD_RESTART_COMMANDS_FOR_JSON}\"],
},") },")
# Add tasks to both build only, and build and start the GDB server.
# Schema for tasks.json can be seen here https://code.visualstudio.com/docs/editor/tasks-appendix
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_TASKS_JSON_CONTENT "
// Build for target ${CMAKE_TARGET}
{
\"label\": \"Build ${CMAKE_TARGET}\",
\"type\": \"shell\",
\"command\": \"${CMAKE_COMMAND}\",
\"args\": [\"--build\", \"${CMAKE_BINARY_DIR}\", \"--target\", \"${CMAKE_TARGET}\"],
},
// Build ${CMAKE_TARGET} and run the GDB server
{
\"label\": \"Build ${CMAKE_TARGET} and start GDB server\",
\"dependsOn\": [\"Build ${CMAKE_TARGET}\", \"GDB Server\"],
\"dependsOrder\": \"sequence\",
},")
endfunction(mbed_generate_ide_debug_configuration) endfunction(mbed_generate_ide_debug_configuration)
# Take all generated debug configurations and write them to launch.json. # Take all generated debug configurations and write them to launch.json.
function(mbed_finalize_ide_debug_configurations) function(mbed_finalize_ide_debug_configurations)
# Add footer # Add footer
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_LAUNCH_JSON_CONTENT " set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_LAUNCH_JSON_CONTENT "
] ]
@ -95,18 +170,118 @@ elseif(MBED_GENERATE_VS_CODE_DEBUG_CFGS)
get_property(VSCODE_LAUNCH_JSON_CONTENT GLOBAL PROPERTY VSCODE_LAUNCH_JSON_CONTENT) get_property(VSCODE_LAUNCH_JSON_CONTENT GLOBAL PROPERTY VSCODE_LAUNCH_JSON_CONTENT)
file(GENERATE OUTPUT ${VSCODE_LAUNCH_JSON_PATH} CONTENT ${VSCODE_LAUNCH_JSON_CONTENT}) file(GENERATE OUTPUT ${VSCODE_LAUNCH_JSON_PATH} CONTENT ${VSCODE_LAUNCH_JSON_CONTENT})
# Convert the CMake list into the correct format for tasks.json
list(GET MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 0 GDBSERVER_EXECUTABLE)
list(SUBLIST MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 1 -1 GDBSERVER_ARGS)
set(GDBSERVER_ARGS_STR "")
set(IS_FIRST_ARG TRUE)
foreach(ELEMENT ${GDBSERVER_ARGS})
if(IS_FIRST_ARG)
set(IS_FIRST_ARG FALSE)
else()
string(APPEND GDBSERVER_ARGS_STR ", ")
endif()
# Escape any quotes in the element
string(REPLACE "\"" "\\\"" ELEMENT "${ELEMENT}")
string(APPEND GDBSERVER_ARGS_STR "\"${ELEMENT}\"")
endforeach()
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_TASKS_JSON_CONTENT "
{
\"label\": \"GDB Server\",
\"type\": \"shell\",
\"command\": \"${GDBSERVER_EXECUTABLE}\",
\"args\": [${GDBSERVER_ARGS_STR}],
\"isBackground\": true,
// This task is run to start the GDB server, so that the launch configuration can connect to it.
// Problem is, it's a GDB server, and since it never exits, VSCode
// will never start the debug session. All this is needed so VSCode just lets it run.
\"problemMatcher\": [
{
\"pattern\": [
{
\"regexp\": \"________________\",
\"file\": 1,
\"location\": 2,
\"message\": 3
}
],
\"background\": {
\"activeOnStart\": true,
\"beginsPattern\": \".*\",
\"endsPattern\": \".*\",
}
}
],
}
]
}
")
# Write out tasks.json
set(VSCODE_TASKS_JSON_PATH ${CMAKE_SOURCE_DIR}/.vscode/tasks.json)
get_property(VSCODE_TASKS_JSON_CONTENT GLOBAL PROPERTY VSCODE_TASKS_JSON_CONTENT)
file(GENERATE OUTPUT ${VSCODE_TASKS_JSON_PATH} CONTENT ${VSCODE_TASKS_JSON_CONTENT})
endfunction(mbed_finalize_ide_debug_configurations) endfunction(mbed_finalize_ide_debug_configurations)
# No-op generator # Command-line generator
# ------------------------------------------------------------- # -------------------------------------------------------------
else() elseif(MBED_UPLOAD_SUPPORTS_DEBUG)
function(mbed_generate_ide_debug_configuration CMAKE_TARGET) function(mbed_generate_ide_debug_configuration CMAKE_TARGET)
# Empty
# add debug target
if(MBED_UPLOAD_SUPPORTS_DEBUG AND MBED_GDB_FOUND)
add_custom_target(debug-${target}
COMMENT "Starting GDB to debug ${target}..."
COMMAND ${MBED_GDB}
--command=${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit
$<TARGET_FILE:${target}>
USES_TERMINAL)
endif()
endfunction(mbed_generate_ide_debug_configuration) endfunction(mbed_generate_ide_debug_configuration)
function(mbed_finalize_ide_debug_configurations) function(mbed_finalize_ide_debug_configurations)
# Empty
# create init file for GDB client
if(MBED_UPLOAD_WANTS_EXTENDED_REMOTE)
set(UPLOAD_GDB_REMOTE_KEYWORD "extended-remote")
else()
set(UPLOAD_GDB_REMOTE_KEYWORD "remote")
endif()
list(JOIN MBED_UPLOAD_LAUNCH_COMMANDS "\n" MBED_UPLOAD_LAUNCH_COMMANDS_FOR_GDBINIT)
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit CONTENT
"# connect to GDB server
target ${UPLOAD_GDB_REMOTE_KEYWORD} localhost:${GDB_PORT}
${MBED_UPLOAD_LAUNCH_COMMANDS_FOR_GDBINIT}
c"
)
# Create target to start the GDB server
add_custom_target(gdbserver
COMMENT "Starting ${UPLOAD_METHOD} GDB server"
COMMAND ${MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND}
USES_TERMINAL
VERBATIM)
endfunction(mbed_finalize_ide_debug_configurations) endfunction(mbed_finalize_ide_debug_configurations)
else()
# No-ops
function(mbed_generate_ide_debug_configuration CMAKE_TARGET)
endfunction()
function(mbed_finalize_ide_debug_configurations)
endfunction()
endif() endif()

View File

@ -181,7 +181,7 @@ function(mbed_set_post_build target)
mbed_generate_map_file(${target}) mbed_generate_map_file(${target})
endif() endif()
mbed_generate_upload_debug_targets(${target}) mbed_generate_upload_target(${target})
mbed_generate_ide_debug_configuration(${target}) mbed_generate_ide_debug_configuration(${target})
endfunction() endfunction()

View File

@ -2,10 +2,15 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
### J-Link Upload Method ### J-Link Upload Method
# This method needs the following parameters: # This method needs the following parameters:
# JLINK_UPLOAD_INTERFACE - Interface to use with J-Link. Should be "JTAG" or "SWD". # JLINK_UPLOAD_INTERFACE - Interface to use with J-Link. Should be "JTAG" or "SWD".
# JLINK_CLOCK_SPEED - Speed to run the J-Link at, in KHz. # JLINK_CLOCK_SPEED - Speed to run the J-Link at, in KHz.
# JLINK_CPU_NAME - Name of CPU to pass to J-Link # JLINK_CPU_NAME - Name of CPU to pass to J-Link
# This method has the following options:
# JLINK_USB_SERIAL_NUMBER - Use a J-Link connected over USB with the specified serial number.
# JLINK_NETWORK_ADDRESS - Use a J-Link connected over the network with the given <ip addr>[:port]
# JLINK_NO_GUI - If set to true, suppress GUI dialog boxes from the J-Link software.
#
set(UPLOAD_SUPPORTS_DEBUG TRUE) set(UPLOAD_SUPPORTS_DEBUG TRUE)
@ -13,11 +18,34 @@ set(UPLOAD_SUPPORTS_DEBUG TRUE)
find_package(JLINK) find_package(JLINK)
set(UPLOAD_JLINK_FOUND ${JLINK_FOUND}) set(UPLOAD_JLINK_FOUND ${JLINK_FOUND})
### Setup options
set(JLINK_USB_SERIAL_NUMBER "" CACHE STRING "Use a J-Link connected over USB with the specified serial number.")
set(JLINK_NETWORK_ADDRESS "" CACHE STRING "Use a J-Link connected over the network with the given <ip addr>[:port]")
# Figure out -select option. See here: https://wiki.segger.com/J-Link_GDB_Server#-select
if((NOT "${JLINK_USB_SERIAL_NUMBER}" STREQUAL "") AND (NOT "${JLINK_NETWORK_ADDRESS}" STREQUAL ""))
message(FATAL_ERROR "Cannot use both JLINK_USB_SERIAL_NUMBER and JLINK_NETWORK_ADDRESS at the same time!")
elseif(NOT "${JLINK_USB_SERIAL_NUMBER}" STREQUAL "")
set(JLINK_SELECT_ARG -Select usb=${JLINK_USB_SERIAL_NUMBER} CACHE INTERNAL "" FORCE)
elseif(NOT "${JLINK_NETWORK_ADDRESS}" STREQUAL "")
set(JLINK_SELECT_ARG -Select ip=${JLINK_NETWORK_ADDRESS} CACHE INTERNAL "" FORCE)
else()
# use default behavior
set(JLINK_SELECT_ARG "" CACHE INTERNAL "" FORCE)
endif()
# default to JTAG # default to JTAG
if(NOT DEFINED JLINK_UPLOAD_INTERFACE) if(NOT DEFINED JLINK_UPLOAD_INTERFACE)
set(JLINK_UPLOAD_INTERFACE JTAG CACHE INTERNAL "" FORCE) set(JLINK_UPLOAD_INTERFACE JTAG CACHE INTERNAL "" FORCE)
endif() endif()
option(JLINK_NO_GUI "If true, suppress GUI dialog boxes from the J-Link software. Note: does not suppress license dialogs from J-Link EDU and On-Board probes, these are intentionally impossible to disable." FALSE)
if(JLINK_NO_GUI)
set(JLINK_NOGUI_ARG -Nogui CACHE INTERNAL "" FORCE)
else()
set(JLINK_NOGUI_ARG "" CACHE INTERNAL "" FORCE)
endif()
### Function to generate upload target ### Function to generate upload target
function(gen_upload_target TARGET_NAME BIN_FILE HEX_FILE) function(gen_upload_target TARGET_NAME BIN_FILE HEX_FILE)
@ -31,26 +59,28 @@ exit
add_custom_target(flash-${TARGET_NAME} add_custom_target(flash-${TARGET_NAME}
COMMENT "Flashing ${TARGET_NAME} with J-Link..." COMMENT "Flashing ${TARGET_NAME} with J-Link..."
COMMAND ${JLINK} COMMAND ${JLINK}
${JLINK_SELECT_ARG}
${JLINK_NOGUI_ARG}
-Device ${JLINK_CPU_NAME} -Device ${JLINK_CPU_NAME}
-Speed ${JLINK_CLOCK_SPEED} -Speed ${JLINK_CLOCK_SPEED}
-if ${JLINK_UPLOAD_INTERFACE} -if ${JLINK_UPLOAD_INTERFACE}
-JTAGConf -1,-1 -JTAGConf -1,-1
-AutoConnect 1 -AutoConnect 1
-ExitOnError -ExitOnError
-CommandFile ${COMMAND_FILE_PATH} -CommandFile ${COMMAND_FILE_PATH})
VERBATIM)
add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME}) add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME})
endfunction(gen_upload_target) endfunction(gen_upload_target)
### Function to generate debug target ### Commands to run the debug server.
add_custom_target(gdbserver # Note: Command-line options for the GDB server are documented on the wiki here:
COMMENT "Starting J-Link GDB server" # https://wiki.segger.com/J-Link_GDB_Server:#Command_line_options
COMMAND set(UPLOAD_GDBSERVER_DEBUG_COMMAND
"${JLINK_GDBSERVER}" "${JLINK_GDBSERVER}"
-Select USB ${JLINK_SELECT_ARG}
${JLINK_NOGUI_ARG}
-Device ${JLINK_CPU_NAME} -Device ${JLINK_CPU_NAME}
-Speed ${JLINK_CLOCK_SPEED} -Speed ${JLINK_CLOCK_SPEED}
-endian little -endian little
@ -59,5 +89,18 @@ add_custom_target(gdbserver
-LocalhostOnly -LocalhostOnly
-noIR -noIR
-port ${GDB_PORT} -port ${GDB_PORT}
USES_TERMINAL -singlerun # Terminate GDB server after GDB disconnects
VERBATIM) )
# Reference: https://github.com/Marus/cortex-debug/blob/056c03f01e008828e6527c571ef5c9adaf64083f/src/jlink.ts#L42
set(UPLOAD_LAUNCH_COMMANDS
"monitor halt"
"monitor reset"
"load"
"break main"
"monitor reset"
)
set(UPLOAD_RESTART_COMMANDS
"monitor halt"
"monitor reset"
)

View File

@ -53,16 +53,30 @@ function(gen_upload_target TARGET_NAME BIN_FILE)
add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME}) add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME})
endfunction(gen_upload_target) endfunction(gen_upload_target)
### Function to generate debug target ### Commands to run the debug server.
add_custom_target(gdbserver set(UPLOAD_GDBSERVER_DEBUG_COMMAND
COMMENT "Starting OpenOCD GDB server"
COMMAND
${OpenOCD} ${OpenOCD}
${OPENOCD_CHIP_CONFIG_COMMANDS} ${OPENOCD_CHIP_CONFIG_COMMANDS}
${OPENOCD_ADAPTER_SERIAL_COMMAND} ${OPENOCD_ADAPTER_SERIAL_COMMAND}
-c "gdb_port ${GDB_PORT}" # Shut down OpenOCD when GDB disconnects.
USES_TERMINAL # see https://github.com/Marus/cortex-debug/issues/371#issuecomment-999727626
VERBATIM) -c "[target current] configure -event gdb-detach {shutdown}"
-c "gdb_port ${GDB_PORT}")
# request extended-remote GDB sessions # request extended-remote GDB sessions
set(UPLOAD_WANTS_EXTENDED_REMOTE TRUE) set(UPLOAD_WANTS_EXTENDED_REMOTE TRUE)
# Reference: https://github.com/Marus/cortex-debug/blob/056c03f01e008828e6527c571ef5c9adaf64083f/src/openocd.ts#L100
set(UPLOAD_LAUNCH_COMMANDS
"monitor reset halt"
"load"
"break main"
"monitor reset halt"
)
set(UPLOAD_RESTART_COMMANDS
"monitor reset halt"
# The following will force an sync between gdb and openocd
"monitor gdb_sync"
"stepi"
)

View File

@ -41,19 +41,23 @@ function(gen_upload_target TARGET_NAME BIN_FILE)
add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME}) add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME})
endfunction(gen_upload_target) endfunction(gen_upload_target)
### Function to generate debug target ### Commands to run the debug server.
add_custom_target(gdbserver set(UPLOAD_GDBSERVER_DEBUG_COMMAND
COMMENT "Starting pyOCD GDB server"
COMMAND
${Python3_EXECUTABLE} ${Python3_EXECUTABLE}
-m pyocd -m pyocd
gdbserver gdbserver
-v
--no-wait --no-wait
-t ${PYOCD_TARGET_NAME} -t ${PYOCD_TARGET_NAME}
${PYOCD_PROBE_ARGS} ${PYOCD_PROBE_ARGS}
-f ${PYOCD_CLOCK_SPEED} -f ${PYOCD_CLOCK_SPEED}
-p ${GDB_PORT} -p ${GDB_PORT})
--persist
--semihosting # Reference: https://github.com/Marus/cortex-debug/blob/056c03f01e008828e6527c571ef5c9adaf64083f/src/pyocd.ts#L40
USES_TERMINAL) set(UPLOAD_LAUNCH_COMMANDS
"monitor reset halt"
"load"
"break main"
)
set(UPLOAD_RESTART_COMMANDS
"monitor reset"
)

View File

@ -10,6 +10,8 @@
# This method creates the following options: # This method creates the following options:
# REDLINK_PROBE_SN - Serial number of the debug probe to connect to. If blank, will connect to any probe. # REDLINK_PROBE_SN - Serial number of the debug probe to connect to. If blank, will connect to any probe.
set(UPLOAD_SUPPORTS_DEBUG TRUE)
### Handle options ### Handle options
set(REDLINK_PROBE_SN "" CACHE STRING "Serial number of the debug probe to connect to for Redlink. Set to empty to detect any matching adapter.") set(REDLINK_PROBE_SN "" CACHE STRING "Serial number of the debug probe to connect to for Redlink. Set to empty to detect any matching adapter.")
@ -57,27 +59,34 @@ function(gen_upload_target TARGET_NAME BIN_FILE HEX_FILE)
endfunction(gen_upload_target) endfunction(gen_upload_target)
### Function to generate debug target ### Commands to run the debug server.
add_custom_target(gdbserver set(UPLOAD_GDBSERVER_DEBUG_COMMAND
COMMENT "Starting Redlink GDB server" ${crt_emu_cm_redlink_PATH}
COMMAND -p ${REDLINK_PART_NUMBER}
${crt_emu_cm_redlink_PATH} --flash-hashing
-p ${REDLINK_PART_NUMBER} -x ${REDLINK_PART_XML_DIR}
--flash-hashing --flash-dir ${REDLINK_FLASH_LOADER_PATH}
-x ${REDLINK_PART_XML_DIR} -g
--flash-dir ${REDLINK_FLASH_LOADER_PATH} -s ${REDLINK_CLOCK_SPEED}
-g -2
-s ${REDLINK_CLOCK_SPEED} ${REDLINK_CONNECT_ARGS}
-2 ${REDLINK_PROBE_ARGS}
${REDLINK_CONNECT_ARGS} --server :${GDB_PORT}
${REDLINK_PROBE_ARGS} --vc
--server :${GDB_PORT} --connect-reset system
--vc --kill-server # Close Redlink when GDB exits
--connect-reset system )
--kill-server # Because redlink seems to not handle Ctrl-C, we use this to close it when GDB exits
USES_TERMINAL
VERBATIM)
# request extended-remote GDB sessions # request extended-remote GDB sessions
set(UPLOAD_WANTS_EXTENDED_REMOTE TRUE) set(UPLOAD_WANTS_EXTENDED_REMOTE TRUE)
set(UPLOAD_LAUNCH_COMMANDS
"monitor reset" # undocumented, but works
"load"
"break main"
"monitor reset"
)
set(UPLOAD_RESTART_COMMANDS
"monitor reset"
)

View File

@ -35,12 +35,24 @@ function(gen_upload_target TARGET_NAME BIN_FILE)
add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME}) add_dependencies(flash-${TARGET_NAME} ${TARGET_NAME})
endfunction(gen_upload_target) endfunction(gen_upload_target)
### Function to generate debug target ### Commands to run the debug server.
add_custom_target(gdbserver set(UPLOAD_WANTS_EXTENDED_REMOTE TRUE)
COMMENT "Starting st-util GDB server" set(UPLOAD_GDBSERVER_DEBUG_COMMAND
COMMAND
${st-util_PATH} ${st-util_PATH}
${STLINK_SERIAL_ARGUMENT} ${STLINK_SERIAL_ARGUMENT}
${STLINK_ARGS} ${STLINK_ARGS}
--listen_port=${GDB_PORT} --listen_port=${GDB_PORT}
USES_TERMINAL) --multi)
# Reference: https://github.com/Marus/cortex-debug/blob/056c03f01e008828e6527c571ef5c9adaf64083f/src/stutil.ts#L39
# (except I had to change target-download to load for some reason)
set(UPLOAD_LAUNCH_COMMANDS
"monitor halt"
"monitor reset"
"load"
"break main"
"monitor reset"
)
set(UPLOAD_RESTART_COMMANDS
"monitor reset"
)

View File

@ -44,18 +44,26 @@ function(gen_upload_target TARGET_NAME BIN_FILE HEX_FILE)
endfunction(gen_upload_target) endfunction(gen_upload_target)
### Function to generate debug target ### Commands to run the debug server.
# The debugger needs to be passed the directory containing STM32CubeProg # The debugger needs to be passed the directory containing STM32CubeProg
get_filename_component(CUBE_PROG_DIR ${STM32CubeProg_PATH} DIRECTORY) get_filename_component(CUBE_PROG_DIR ${STM32CubeProg_PATH} DIRECTORY)
add_custom_target(gdbserver set(UPLOAD_GDBSERVER_DEBUG_COMMAND
COMMENT "Starting ST-LINK GDB server"
COMMAND
${STLINK_gdbserver_COMMAND} ${STLINK_gdbserver_COMMAND}
${STM32CUBE_GDBSERVER_ARGS} ${STM32CUBE_GDBSERVER_ARGS}
-cp "${CUBE_PROG_DIR}" -cp "${CUBE_PROG_DIR}"
--persistent # don't close debugger after GDB disconnects, matches behavior of other tools like J-Link
-p ${GDB_PORT} -p ${GDB_PORT}
${STM32CUBE_GDB_PROBE_ARGS} --halt
USES_TERMINAL) ${STM32CUBE_GDB_PROBE_ARGS})
# Reference: https://github.com/Marus/cortex-debug/blob/056c03f01e008828e6527c571ef5c9adaf64083f/src/stlink.ts#L113
set(UPLOAD_LAUNCH_COMMANDS
"monitor reset"
"load"
"break main"
"monitor reset"
)
set(UPLOAD_RESTART_COMMANDS
"monitor reset"
)