├── .gitmodules ├── CMakeLists.txt ├── FreeRTOS_Kernel_import.cmake ├── LICENSE ├── README.md ├── include ├── DAP_config.h ├── board_debug_probe_config.h ├── board_example_config.h └── board_pico_config.h ├── pico_sdk_import.cmake └── src ├── FreeRTOSConfig.h ├── cdc_uart.c ├── cdc_uart.h ├── get_serial.c ├── get_serial.h ├── main.c ├── probe.c ├── probe.h ├── probe.pio ├── probe_config.c ├── probe_config.h ├── probe_oen.pio ├── sw_dp_pio.c ├── tusb_config.h ├── tusb_edpt_handler.c ├── tusb_edpt_handler.h └── usb_descriptors.c /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "CMSIS_5"] 2 | path = CMSIS_5 3 | url = https://github.com/ARM-software/CMSIS_5 4 | [submodule "freertos"] 5 | path = freertos 6 | url = https://github.com/FreeRTOS/FreeRTOS-Kernel 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(pico_sdk_import.cmake) 4 | 5 | set(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/freertos) 6 | include(FreeRTOS_Kernel_import.cmake) 7 | 8 | project(debugprobe) 9 | 10 | pico_sdk_init() 11 | 12 | if (${PICO_SDK_VERSION_MAJOR} LESS 2) 13 | message(SEND_ERROR "Version 2 of the Pico SDK is required to compile this project. Please update your installation at ${PICO_SDK_PATH}") 14 | endif () 15 | 16 | add_executable(debugprobe 17 | src/probe_config.c 18 | src/main.c 19 | src/usb_descriptors.c 20 | src/probe.c 21 | src/cdc_uart.c 22 | src/get_serial.c 23 | src/sw_dp_pio.c 24 | src/tusb_edpt_handler.c 25 | ) 26 | 27 | target_sources(debugprobe PRIVATE 28 | CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c 29 | CMSIS_5/CMSIS/DAP/Firmware/Source/JTAG_DP.c 30 | CMSIS_5/CMSIS/DAP/Firmware/Source/DAP_vendor.c 31 | CMSIS_5/CMSIS/DAP/Firmware/Source/SWO.c 32 | #CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c 33 | ) 34 | 35 | target_include_directories(debugprobe PRIVATE 36 | CMSIS_5/CMSIS/DAP/Firmware/Include/ 37 | CMSIS_5/CMSIS/Core/Include/ 38 | include/ 39 | ) 40 | 41 | target_compile_options(debugprobe PRIVATE -Wall) 42 | 43 | pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio) 44 | pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe_oen.pio) 45 | 46 | target_include_directories(debugprobe PRIVATE src) 47 | 48 | target_compile_definitions (debugprobe PRIVATE 49 | PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1 50 | ) 51 | 52 | option (DEBUG_ON_PICO "Compile firmware for the Pico instead of Debug Probe" OFF) 53 | if (DEBUG_ON_PICO) 54 | target_compile_definitions (debugprobe PRIVATE 55 | DEBUG_ON_PICO=1 56 | ) 57 | if (PICO_BOARD STREQUAL "pico") 58 | set_target_properties(debugprobe PROPERTIES 59 | OUTPUT_NAME "debugprobe_on_pico" 60 | ) 61 | elseif (PICO_BOARD STREQUAL "pico2") 62 | set_target_properties(debugprobe PROPERTIES 63 | OUTPUT_NAME "debugprobe_on_pico2" 64 | ) 65 | else () 66 | message(SEND_ERROR "Unsupported board ${PICO_BOARD}") 67 | endif () 68 | endif () 69 | 70 | 71 | target_link_libraries(debugprobe PRIVATE 72 | pico_multicore 73 | pico_stdlib 74 | pico_unique_id 75 | tinyusb_device 76 | tinyusb_board 77 | hardware_pio 78 | FreeRTOS-Kernel 79 | FreeRTOS-Kernel-Heap1 80 | ) 81 | 82 | pico_set_binary_type(debugprobe copy_to_ram) 83 | 84 | pico_add_extra_outputs(debugprobe) 85 | -------------------------------------------------------------------------------- /FreeRTOS_Kernel_import.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-clause 2 | # This is a copy of /portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake 3 | 4 | # This can be dropped into an external project to help locate the FreeRTOS kernel 5 | # It should be include()ed prior to project(). Alternatively this file may 6 | # or the CMakeLists.txt in this directory may be included or added via add_subdirectory 7 | # respectively. 8 | 9 | if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH)) 10 | set(FREERTOS_KERNEL_PATH $ENV{FREERTOS_KERNEL_PATH}) 11 | message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')") 12 | endif () 13 | 14 | # first pass we look in old tree; second pass we look in new tree 15 | foreach(SEARCH_PASS RANGE 0 1) 16 | if (SEARCH_PASS) 17 | # ports may be moving to submodule in the future 18 | set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/Community-Supported-Ports/GCC") 19 | set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../../..") 20 | else() 21 | set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC") 22 | set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..") 23 | endif() 24 | 25 | if(PICO_PLATFORM STREQUAL "rp2040") 26 | set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2040") 27 | else() 28 | if (PICO_PLATFORM STREQUAL "rp2350-riscv") 29 | set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_RISC-V") 30 | else() 31 | set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_ARM_NTZ") 32 | endif() 33 | endif() 34 | 35 | if (NOT FREERTOS_KERNEL_PATH) 36 | # check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly) 37 | get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH) 38 | get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH) 39 | if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) 40 | get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) 41 | endif() 42 | if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) 43 | get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) 44 | message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake") 45 | break() 46 | elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel") 47 | set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel) 48 | message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}") 49 | break() 50 | endif() 51 | endif () 52 | 53 | if (NOT FREERTOS_KERNEL_PATH) 54 | foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source) 55 | # check if FreeRTOS-Kernel exists under directory that included us 56 | set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) 57 | get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH) 58 | if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) 59 | get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH) 60 | message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project") 61 | break() 62 | endif() 63 | endforeach() 64 | if (FREERTOS_KERNEL_PATH) 65 | break() 66 | endif() 67 | endif() 68 | 69 | # user must have specified 70 | if (FREERTOS_KERNEL_PATH) 71 | if (EXISTS "${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") 72 | break() 73 | endif() 74 | endif() 75 | endforeach () 76 | 77 | if (NOT FREERTOS_KERNEL_PATH) 78 | message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.") 79 | endif() 80 | 81 | set(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" CACHE PATH "Path to the FreeRTOS Kernel") 82 | 83 | get_filename_component(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 84 | if (NOT EXISTS ${FREERTOS_KERNEL_PATH}) 85 | message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found") 86 | endif() 87 | if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) 88 | message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain a '${PICO_PLATFORM}' port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") 89 | endif() 90 | set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE) 91 | 92 | add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL) 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Unless otherwise indicated in individual files, this project is licenced as: 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Raspberry Pi Ltd 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Debugprobe 2 | 3 | Firmware source for the Raspberry Pi Debug Probe SWD/UART accessory. Can also be run on a Raspberry Pi Pico. 4 | 5 | [Raspberry Pi Debug Probe product page](https://www.raspberrypi.com/products/debug-probe/) 6 | 7 | [Raspberry Pi Pico product page](https://www.raspberrypi.com/products/raspberry-pi-pico/) 8 | 9 | 10 | # Documentation 11 | 12 | Debug Probe documentation can be found at the [Raspberry Pi Microcontroller Documentation portal](https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html#about-the-debug-probe). 13 | 14 | # Hacking 15 | 16 | For the purpose of making changes or studying of the code, you may want to compile the code yourself. 17 | 18 | First, clone the repository: 19 | ``` 20 | git clone https://github.com/raspberrypi/debugprobe 21 | cd debugprobe 22 | ``` 23 | Initialize and update the submodules: 24 | ``` 25 | git submodule update --init --recursive 26 | ``` 27 | Then create and switch to the build directory: 28 | ``` 29 | mkdir build 30 | cd build 31 | ``` 32 | If your environment doesn't contain `PICO_SDK_PATH`, then either add it to your environment variables with `export PICO_SDK_PATH=/path/to/sdk` or add `PICO_SDK_PATH=/path/to/sdk` to the arguments to CMake below. 33 | 34 | Run cmake and build the code: 35 | ``` 36 | cmake .. 37 | make 38 | ``` 39 | Done! You should now have a `debugprobe.uf2` that you can upload to your Debug Probe via the UF2 bootloader. 40 | 41 | If you want to create the version that runs on the Pico, then you need to invoke `cmake` in the sequence above with the `DEBUG_ON_PICO=ON` option: 42 | ``` 43 | cmake -DDEBUG_ON_PICO=ON .. 44 | ``` 45 | 46 | This will build with the configuration for the Pico and call the output program `debugprobe_on_pico.uf2`, as opposed to `debugprobe.uf2` for the accessory hardware. 47 | 48 | Note that if you first ran through the whole sequence to compile for the Debug Probe, then you don't need to start back at the top. You can just go back to the `cmake` step and start from there. 49 | 50 | # Building for the Pico 2 51 | 52 | If using an existing debugprobe clone: 53 | - You must completely regenerate your build directory, or use a different one. 54 | - You must also sync and update submodules as rp2350 needs a downstream FreeRTOS port for now. 55 | - `PICO_SDK_PATH` must point to a version 2.0.0 or greater install. 56 | 57 | 58 | ``` 59 | git submodule sync 60 | git submodule update --init 61 | mkdir build-pico2 62 | cd build-pico2 63 | cmake -DDEBUG_ON_PICO=1 -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ../ 64 | ``` 65 | 66 | # TODO 67 | - AutoBaud selection, as PIO is a capable frequency counter 68 | - Possibly include RTT support 69 | -------------------------------------------------------------------------------- /include/DAP_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2021 ARM Limited. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the License); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ---------------------------------------------------------------------- 19 | * 20 | * $Date: 16. June 2021 21 | * $Revision: V2.1.0 22 | * 23 | * Project: CMSIS-DAP Configuration 24 | * Title: DAP_config.h CMSIS-DAP Configuration File (Template) 25 | * 26 | *---------------------------------------------------------------------------*/ 27 | 28 | #ifndef __DAP_CONFIG_H__ 29 | #define __DAP_CONFIG_H__ 30 | 31 | 32 | //************************************************************************************************** 33 | /** 34 | \defgroup DAP_Config_Debug_gr CMSIS-DAP Debug Unit Information 35 | \ingroup DAP_ConfigIO_gr 36 | @{ 37 | Provides definitions about the hardware and configuration of the Debug Unit. 38 | 39 | This information includes: 40 | - Definition of Cortex-M processor parameters used in CMSIS-DAP Debug Unit. 41 | - Debug Unit Identification strings (Vendor, Product, Serial Number). 42 | - Debug Unit communication packet size. 43 | - Debug Access Port supported modes and settings (JTAG/SWD and SWO). 44 | - Optional information about a connected Target Device (for Evaluation Boards). 45 | */ 46 | #include 47 | #include 48 | #include 49 | 50 | #include "cmsis_compiler.h" 51 | #include "probe_config.h" 52 | #include "probe.h" 53 | 54 | /// Processor Clock of the Cortex-M MCU used in the Debug Unit. 55 | /// This value is used to calculate the SWD/JTAG clock speed. 56 | /* Debugprobe uses PIO for clock generation, so return the current system clock. */ 57 | #define CPU_CLOCK clock_get_hz(clk_sys) 58 | 59 | /// Number of processor cycles for I/O Port write operations. 60 | /// This value is used to calculate the SWD/JTAG clock speed that is generated with I/O 61 | /// Port write operations in the Debug Unit by a Cortex-M MCU. Most Cortex-M processors 62 | /// require 2 processor cycles for a I/O Port Write operation. If the Debug Unit uses 63 | /// a Cortex-M0+ processor with high-speed peripheral I/O only 1 processor cycle might be 64 | /// required. 65 | #define IO_PORT_WRITE_CYCLES 1U ///< I/O Cycles: 2=default, 1=Cortex-M0+ fast I/0. 66 | #define DELAY_SLOW_CYCLES 1U // We don't differentiate between fast/slow, we've got a 16-bit divisor for that 67 | 68 | /// Indicate that Serial Wire Debug (SWD) communication mode is available at the Debug Access Port. 69 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 70 | #define DAP_SWD 1 ///< SWD Mode: 1 = available, 0 = not available. 71 | 72 | /// Indicate that JTAG communication mode is available at the Debug Port. 73 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 74 | #define DAP_JTAG 0 ///< JTAG Mode: 1 = available, 0 = not available. 75 | 76 | /// Configure maximum number of JTAG devices on the scan chain connected to the Debug Access Port. 77 | /// This setting impacts the RAM requirements of the Debug Unit. Valid range is 1 .. 255. 78 | #define DAP_JTAG_DEV_CNT 8U ///< Maximum number of JTAG devices on scan chain. 79 | 80 | /// Default communication mode on the Debug Access Port. 81 | /// Used for the command \ref DAP_Connect when Port Default mode is selected. 82 | #define DAP_DEFAULT_PORT 1U ///< Default JTAG/SWJ Port Mode: 1 = SWD, 2 = JTAG. 83 | 84 | /// Default communication speed on the Debug Access Port for SWD and JTAG mode. 85 | /// Used to initialize the default SWD/JTAG clock frequency. 86 | /// The command \ref DAP_SWJ_Clock can be used to overwrite this default setting. 87 | #define DAP_DEFAULT_SWJ_CLOCK 1000000U ///< Default SWD/JTAG clock frequency in Hz. 88 | 89 | /// Maximum Package Size for Command and Response data. 90 | /// This configuration settings is used to optimize the communication performance with the 91 | /// debugger and depends on the USB peripheral. Typical vales are 64 for Full-speed USB HID or WinUSB, 92 | /// 1024 for High-speed USB HID and 512 for High-speed USB WinUSB. 93 | #define DAP_PACKET_SIZE 64U ///< Specifies Packet Size in bytes. 94 | 95 | /// Maximum Package Buffers for Command and Response data. 96 | /// This configuration settings is used to optimize the communication performance with the 97 | /// debugger and depends on the USB peripheral. For devices with limited RAM or USB buffer the 98 | /// setting can be reduced (valid range is 1 .. 255). 99 | #define DAP_PACKET_COUNT 2U ///< Specifies number of packets buffered. 100 | 101 | /// Indicate that UART Serial Wire Output (SWO) trace is available. 102 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 103 | #define SWO_UART 0 ///< SWO UART: 1 = available, 0 = not available. 104 | 105 | /// USART Driver instance number for the UART SWO. 106 | #define SWO_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#). 107 | 108 | /// Maximum SWO UART Baudrate. 109 | #define SWO_UART_MAX_BAUDRATE 10000000U ///< SWO UART Maximum Baudrate in Hz. 110 | 111 | /// Indicate that Manchester Serial Wire Output (SWO) trace is available. 112 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 113 | #define SWO_MANCHESTER 0 ///< SWO Manchester: 1 = available, 0 = not available. 114 | 115 | /// SWO Trace Buffer Size. 116 | #define SWO_BUFFER_SIZE 4096U ///< SWO Trace Buffer Size in bytes (must be 2^n). 117 | 118 | /// SWO Streaming Trace. 119 | #define SWO_STREAM 0 ///< SWO Streaming Trace: 1 = available, 0 = not available. 120 | 121 | /// Clock frequency of the Test Domain Timer. Timer value is returned with \ref TIMESTAMP_GET. 122 | #define TIMESTAMP_CLOCK 1000000U ///< Timestamp clock in Hz (0 = timestamps not supported). 123 | 124 | /// Indicate that UART Communication Port is available. 125 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 126 | #define DAP_UART 0 ///< DAP UART: 1 = available, 0 = not available. 127 | 128 | /// USART Driver instance number for the UART Communication Port. 129 | #define DAP_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#). 130 | 131 | /// UART Receive Buffer Size. 132 | #define DAP_UART_RX_BUFFER_SIZE 1024U ///< Uart Receive Buffer Size in bytes (must be 2^n). 133 | 134 | /// UART Transmit Buffer Size. 135 | #define DAP_UART_TX_BUFFER_SIZE 1024U ///< Uart Transmit Buffer Size in bytes (must be 2^n). 136 | 137 | /// Indicate that UART Communication via USB COM Port is available. 138 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 139 | #define DAP_UART_USB_COM_PORT 0 ///< USB COM Port: 1 = available, 0 = not available. 140 | 141 | /// Debug Unit is connected to fixed Target Device. 142 | /// The Debug Unit may be part of an evaluation board and always connected to a fixed 143 | /// known device. In this case a Device Vendor, Device Name, Board Vendor and Board Name strings 144 | /// are stored and may be used by the debugger or IDE to configure device parameters. 145 | #define TARGET_FIXED 0 ///< Target: 1 = known, 0 = unknown; 146 | 147 | #define TARGET_DEVICE_VENDOR "Arm" ///< String indicating the Silicon Vendor 148 | #define TARGET_DEVICE_NAME "Cortex-M" ///< String indicating the Target Device 149 | #define TARGET_BOARD_VENDOR "Arm" ///< String indicating the Board Vendor 150 | #define TARGET_BOARD_NAME "Arm board" ///< String indicating the Board Name 151 | 152 | #if TARGET_FIXED != 0 153 | #include 154 | static const char TargetDeviceVendor [] = TARGET_DEVICE_VENDOR; 155 | static const char TargetDeviceName [] = TARGET_DEVICE_NAME; 156 | static const char TargetBoardVendor [] = TARGET_BOARD_VENDOR; 157 | static const char TargetBoardName [] = TARGET_BOARD_NAME; 158 | #endif 159 | 160 | /** Get Vendor Name string. 161 | \param str Pointer to buffer to store the string (max 60 characters). 162 | \return String length (including terminating NULL character) or 0 (no string). 163 | */ 164 | __STATIC_INLINE uint8_t DAP_GetVendorString (char *str) { 165 | (void)str; 166 | return (0U); 167 | } 168 | 169 | /** Get Product Name string. 170 | \param str Pointer to buffer to store the string (max 60 characters). 171 | \return String length (including terminating NULL character) or 0 (no string). 172 | */ 173 | __STATIC_INLINE uint8_t DAP_GetProductString (char *str) { 174 | (void)str; 175 | return (0U); 176 | } 177 | 178 | /** Get Serial Number string. 179 | \param str Pointer to buffer to store the string (max 60 characters). 180 | \return String length (including terminating NULL character) or 0 (no string). 181 | */ 182 | __STATIC_INLINE uint8_t DAP_GetSerNumString (char *str) { 183 | (void)str; 184 | return (0U); 185 | } 186 | 187 | /** Get Target Device Vendor string. 188 | \param str Pointer to buffer to store the string (max 60 characters). 189 | \return String length (including terminating NULL character) or 0 (no string). 190 | */ 191 | __STATIC_INLINE uint8_t DAP_GetTargetDeviceVendorString (char *str) { 192 | #if TARGET_FIXED != 0 193 | uint8_t len; 194 | 195 | strcpy(str, TargetDeviceVendor); 196 | len = (uint8_t)(strlen(TargetDeviceVendor) + 1U); 197 | return (len); 198 | #else 199 | (void)str; 200 | return (0U); 201 | #endif 202 | } 203 | 204 | /** Get Target Device Name string. 205 | \param str Pointer to buffer to store the string (max 60 characters). 206 | \return String length (including terminating NULL character) or 0 (no string). 207 | */ 208 | __STATIC_INLINE uint8_t DAP_GetTargetDeviceNameString (char *str) { 209 | #if TARGET_FIXED != 0 210 | uint8_t len; 211 | 212 | strcpy(str, TargetDeviceName); 213 | len = (uint8_t)(strlen(TargetDeviceName) + 1U); 214 | return (len); 215 | #else 216 | (void)str; 217 | return (0U); 218 | #endif 219 | } 220 | 221 | /** Get Target Board Vendor string. 222 | \param str Pointer to buffer to store the string (max 60 characters). 223 | \return String length (including terminating NULL character) or 0 (no string). 224 | */ 225 | __STATIC_INLINE uint8_t DAP_GetTargetBoardVendorString (char *str) { 226 | #if TARGET_FIXED != 0 227 | uint8_t len; 228 | 229 | strcpy(str, TargetBoardVendor); 230 | len = (uint8_t)(strlen(TargetBoardVendor) + 1U); 231 | return (len); 232 | #else 233 | (void)str; 234 | return (0U); 235 | #endif 236 | } 237 | 238 | /** Get Target Board Name string. 239 | \param str Pointer to buffer to store the string (max 60 characters). 240 | \return String length (including terminating NULL character) or 0 (no string). 241 | */ 242 | __STATIC_INLINE uint8_t DAP_GetTargetBoardNameString (char *str) { 243 | #if TARGET_FIXED != 0 244 | uint8_t len; 245 | 246 | strcpy(str, TargetBoardName); 247 | len = (uint8_t)(strlen(TargetBoardName) + 1U); 248 | return (len); 249 | #else 250 | (void)str; 251 | return (0U); 252 | #endif 253 | } 254 | 255 | /** Get Product Firmware Version string. 256 | \param str Pointer to buffer to store the string (max 60 characters). 257 | \return String length (including terminating NULL character) or 0 (no string). 258 | */ 259 | __STATIC_INLINE uint8_t DAP_GetProductFirmwareVersionString (char *str) { 260 | (void)str; 261 | return (0U); 262 | } 263 | 264 | ///@} 265 | 266 | 267 | //************************************************************************************************** 268 | /** 269 | \defgroup DAP_Config_PortIO_gr CMSIS-DAP Hardware I/O Pin Access 270 | \ingroup DAP_ConfigIO_gr 271 | @{ 272 | 273 | Standard I/O Pins of the CMSIS-DAP Hardware Debug Port support standard JTAG mode 274 | and Serial Wire Debug (SWD) mode. In SWD mode only 2 pins are required to implement the debug 275 | interface of a device. The following I/O Pins are provided: 276 | 277 | JTAG I/O Pin | SWD I/O Pin | CMSIS-DAP Hardware pin mode 278 | ---------------------------- | -------------------- | --------------------------------------------- 279 | TCK: Test Clock | SWCLK: Clock | Output Push/Pull 280 | TMS: Test Mode Select | SWDIO: Data I/O | Output Push/Pull; Input (for receiving data) 281 | TDI: Test Data Input | | Output Push/Pull 282 | TDO: Test Data Output | | Input 283 | nTRST: Test Reset (optional) | | Output Open Drain with pull-up resistor 284 | nRESET: Device Reset | nRESET: Device Reset | Output Open Drain with pull-up resistor 285 | 286 | 287 | DAP Hardware I/O Pin Access Functions 288 | ------------------------------------- 289 | The various I/O Pins are accessed by functions that implement the Read, Write, Set, or Clear to 290 | these I/O Pins. 291 | 292 | For the SWDIO I/O Pin there are additional functions that are called in SWD I/O mode only. 293 | This functions are provided to achieve faster I/O that is possible with some advanced GPIO 294 | peripherals that can independently write/read a single I/O pin without affecting any other pins 295 | of the same I/O port. The following SWDIO I/O Pin functions are provided: 296 | - \ref PIN_SWDIO_OUT_ENABLE to enable the output mode from the DAP hardware. 297 | - \ref PIN_SWDIO_OUT_DISABLE to enable the input mode to the DAP hardware. 298 | - \ref PIN_SWDIO_IN to read from the SWDIO I/O pin with utmost possible speed. 299 | - \ref PIN_SWDIO_OUT to write to the SWDIO I/O pin with utmost possible speed. 300 | */ 301 | 302 | 303 | // Configure DAP I/O pins ------------------------------ 304 | 305 | /** Setup JTAG I/O pins: TCK, TMS, TDI, TDO, nTRST, and nRESET. 306 | Configures the DAP Hardware I/O pins for JTAG mode: 307 | - TCK, TMS, TDI, nTRST, nRESET to output mode and set to high level. 308 | - TDO to input mode. 309 | */ 310 | __STATIC_INLINE void PORT_JTAG_SETUP (void) { 311 | ; 312 | } 313 | 314 | /** Setup SWD I/O pins: SWCLK, SWDIO, and nRESET. 315 | Configures the DAP Hardware I/O pins for Serial Wire Debug (SWD) mode: 316 | - SWCLK, SWDIO, nRESET to output mode and set to default high level. 317 | - TDI, nTRST to HighZ mode (pins are unused in SWD mode). 318 | */ 319 | // hack - zap our "stop doing divides everywhere" cache 320 | extern volatile uint32_t cached_delay; 321 | __STATIC_INLINE void PORT_SWD_SETUP (void) { 322 | probe_init(); 323 | cached_delay = 0; 324 | } 325 | 326 | /** Disable JTAG/SWD I/O Pins. 327 | Disables the DAP Hardware I/O pins which configures: 328 | - TCK/SWCLK, TMS/SWDIO, TDI, TDO, nTRST, nRESET to High-Z mode. 329 | */ 330 | __STATIC_INLINE void PORT_OFF (void) { 331 | probe_deinit(); 332 | } 333 | 334 | 335 | // SWCLK/TCK I/O pin ------------------------------------- 336 | 337 | /** SWCLK/TCK I/O pin: Get Input. 338 | \return Current status of the SWCLK/TCK DAP hardware I/O pin. 339 | */ 340 | __STATIC_FORCEINLINE uint32_t PIN_SWCLK_TCK_IN (void) { 341 | return (0U); 342 | } 343 | 344 | /** SWCLK/TCK I/O pin: Set Output to High. 345 | Set the SWCLK/TCK DAP hardware I/O pin to high level. 346 | */ 347 | __STATIC_FORCEINLINE void PIN_SWCLK_TCK_SET (void) { 348 | ; 349 | } 350 | 351 | /** SWCLK/TCK I/O pin: Set Output to Low. 352 | Set the SWCLK/TCK DAP hardware I/O pin to low level. 353 | */ 354 | __STATIC_FORCEINLINE void PIN_SWCLK_TCK_CLR (void) { 355 | ; 356 | } 357 | 358 | 359 | // SWDIO/TMS Pin I/O -------------------------------------- 360 | 361 | /** SWDIO/TMS I/O pin: Get Input. 362 | \return Current status of the SWDIO/TMS DAP hardware I/O pin. 363 | */ 364 | __STATIC_FORCEINLINE uint32_t PIN_SWDIO_TMS_IN (void) { 365 | return (0U); 366 | } 367 | 368 | /** SWDIO/TMS I/O pin: Set Output to High. 369 | Set the SWDIO/TMS DAP hardware I/O pin to high level. 370 | */ 371 | __STATIC_FORCEINLINE void PIN_SWDIO_TMS_SET (void) { 372 | ; 373 | } 374 | 375 | /** SWDIO/TMS I/O pin: Set Output to Low. 376 | Set the SWDIO/TMS DAP hardware I/O pin to low level. 377 | */ 378 | __STATIC_FORCEINLINE void PIN_SWDIO_TMS_CLR (void) { 379 | ; 380 | } 381 | 382 | /** SWDIO I/O pin: Get Input (used in SWD mode only). 383 | \return Current status of the SWDIO DAP hardware I/O pin. 384 | */ 385 | __STATIC_FORCEINLINE uint32_t PIN_SWDIO_IN (void) { 386 | return (0U); 387 | } 388 | 389 | /** SWDIO I/O pin: Set Output (used in SWD mode only). 390 | \param bit Output value for the SWDIO DAP hardware I/O pin. 391 | */ 392 | __STATIC_FORCEINLINE void PIN_SWDIO_OUT (uint32_t bit) { 393 | ; 394 | } 395 | 396 | /** SWDIO I/O pin: Switch to Output mode (used in SWD mode only). 397 | Configure the SWDIO DAP hardware I/O pin to output mode. This function is 398 | called prior \ref PIN_SWDIO_OUT function calls. 399 | */ 400 | __STATIC_FORCEINLINE void PIN_SWDIO_OUT_ENABLE (void) { 401 | probe_write_mode(); 402 | } 403 | 404 | /** SWDIO I/O pin: Switch to Input mode (used in SWD mode only). 405 | Configure the SWDIO DAP hardware I/O pin to input mode. This function is 406 | called prior \ref PIN_SWDIO_IN function calls. 407 | */ 408 | __STATIC_FORCEINLINE void PIN_SWDIO_OUT_DISABLE (void) { 409 | probe_read_mode(); 410 | } 411 | 412 | 413 | // TDI Pin I/O --------------------------------------------- 414 | 415 | /** TDI I/O pin: Get Input. 416 | \return Current status of the TDI DAP hardware I/O pin. 417 | */ 418 | __STATIC_FORCEINLINE uint32_t PIN_TDI_IN (void) { 419 | return (0U); 420 | } 421 | 422 | /** TDI I/O pin: Set Output. 423 | \param bit Output value for the TDI DAP hardware I/O pin. 424 | */ 425 | __STATIC_FORCEINLINE void PIN_TDI_OUT (uint32_t bit) { 426 | ; 427 | } 428 | 429 | 430 | // TDO Pin I/O --------------------------------------------- 431 | 432 | /** TDO I/O pin: Get Input. 433 | \return Current status of the TDO DAP hardware I/O pin. 434 | */ 435 | __STATIC_FORCEINLINE uint32_t PIN_TDO_IN (void) { 436 | return (0U); 437 | } 438 | 439 | 440 | // nTRST Pin I/O ------------------------------------------- 441 | 442 | /** nTRST I/O pin: Get Input. 443 | \return Current status of the nTRST DAP hardware I/O pin. 444 | */ 445 | __STATIC_FORCEINLINE uint32_t PIN_nTRST_IN (void) { 446 | return (0U); 447 | } 448 | 449 | /** nTRST I/O pin: Set Output. 450 | \param bit JTAG TRST Test Reset pin status: 451 | - 0: issue a JTAG TRST Test Reset. 452 | - 1: release JTAG TRST Test Reset. 453 | */ 454 | __STATIC_FORCEINLINE void PIN_nTRST_OUT (uint32_t bit) { 455 | ; 456 | } 457 | 458 | // nRESET Pin I/O------------------------------------------ 459 | 460 | /** nRESET I/O pin: Get Input. 461 | \return Current status of the nRESET DAP hardware I/O pin. 462 | */ 463 | __STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void) { 464 | #ifdef PROBE_PIN_RESET 465 | return probe_reset_level(); 466 | #else 467 | return (0U); 468 | #endif 469 | } 470 | 471 | /** nRESET I/O pin: Set Output. 472 | \param bit target device hardware reset pin status: 473 | - 0: issue a device hardware reset. 474 | - 1: release device hardware reset. 475 | */ 476 | __STATIC_FORCEINLINE void PIN_nRESET_OUT (uint32_t bit) { 477 | #ifdef PROBE_PIN_RESET 478 | probe_assert_reset(!!bit); 479 | #else 480 | (void) bit; 481 | #endif 482 | } 483 | 484 | ///@} 485 | 486 | 487 | //************************************************************************************************** 488 | /** 489 | \defgroup DAP_Config_LEDs_gr CMSIS-DAP Hardware Status LEDs 490 | \ingroup DAP_ConfigIO_gr 491 | @{ 492 | 493 | CMSIS-DAP Hardware may provide LEDs that indicate the status of the CMSIS-DAP Debug Unit. 494 | 495 | It is recommended to provide the following LEDs for status indication: 496 | - Connect LED: is active when the DAP hardware is connected to a debugger. 497 | - Running LED: is active when the debugger has put the target device into running state. 498 | */ 499 | 500 | /** Debug Unit: Set status of Connected LED. 501 | \param bit status of the Connect LED. 502 | - 1: Connect LED ON: debugger is connected to CMSIS-DAP Debug Unit. 503 | - 0: Connect LED OFF: debugger is not connected to CMSIS-DAP Debug Unit. 504 | */ 505 | __STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit) { 506 | #ifdef PROBE_DAP_CONNECTED_LED 507 | gpio_put(PROBE_DAP_CONNECTED_LED, bit); 508 | #endif 509 | } 510 | 511 | /** Debug Unit: Set status Target Running LED. 512 | \param bit status of the Target Running LED. 513 | - 1: Target Running LED ON: program execution in target started. 514 | - 0: Target Running LED OFF: program execution in target stopped. 515 | */ 516 | __STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit) { 517 | #ifdef PROBE_DAP_RUNNING_LED 518 | gpio_put(PROBE_DAP_RUNNING_LED, bit); 519 | #endif 520 | } 521 | 522 | ///@} 523 | 524 | 525 | //************************************************************************************************** 526 | /** 527 | \defgroup DAP_Config_Timestamp_gr CMSIS-DAP Timestamp 528 | \ingroup DAP_ConfigIO_gr 529 | @{ 530 | Access function for Test Domain Timer. 531 | 532 | The value of the Test Domain Timer in the Debug Unit is returned by the function \ref TIMESTAMP_GET. By 533 | default, the DWT timer is used. The frequency of this timer is configured with \ref TIMESTAMP_CLOCK. 534 | 535 | */ 536 | 537 | /** Get timestamp of Test Domain Timer. 538 | \return Current timestamp value. 539 | */ 540 | __STATIC_INLINE uint32_t TIMESTAMP_GET (void) { 541 | return time_us_32(); 542 | } 543 | 544 | ///@} 545 | 546 | 547 | //************************************************************************************************** 548 | /** 549 | \defgroup DAP_Config_Initialization_gr CMSIS-DAP Initialization 550 | \ingroup DAP_ConfigIO_gr 551 | @{ 552 | 553 | CMSIS-DAP Hardware I/O and LED Pins are initialized with the function \ref DAP_SETUP. 554 | */ 555 | 556 | /** Setup of the Debug Unit I/O pins and LEDs (called when Debug Unit is initialized). 557 | This function performs the initialization of the CMSIS-DAP Hardware I/O Pins and the 558 | Status LEDs. In detail the operation of Hardware I/O and LED pins are enabled and set: 559 | - I/O clock system enabled. 560 | - all I/O pins: input buffer enabled, output pins are set to HighZ mode. 561 | - for nTRST, nRESET a weak pull-up (if available) is enabled. 562 | - LED output pins are enabled and LEDs are turned off. 563 | */ 564 | __STATIC_INLINE void DAP_SETUP (void) { 565 | // We synchronously setup probe IOs when the respective PIO program is loaded - not at start of day 566 | 567 | #ifdef PROBE_DAP_CONNECTED_LED 568 | gpio_init(PROBE_DAP_CONNECTED_LED); 569 | gpio_set_dir(PROBE_DAP_CONNECTED_LED, GPIO_OUT); 570 | #endif 571 | #ifdef PROBE_DAP_RUNNING_LED 572 | gpio_init(PROBE_DAP_RUNNING_LED); 573 | gpio_set_dir(PROBE_DAP_RUNNING_LED, GPIO_OUT); 574 | #endif 575 | } 576 | 577 | /** Reset Target Device with custom specific I/O pin or command sequence. 578 | This function allows the optional implementation of a device specific reset sequence. 579 | It is called when the command \ref DAP_ResetTarget and is for example required 580 | when a device needs a time-critical unlock sequence that enables the debug port. 581 | \return 0 = no device specific reset sequence is implemented.\n 582 | 1 = a device specific reset sequence is implemented. 583 | */ 584 | __STATIC_INLINE uint8_t RESET_TARGET (void) { 585 | return (0U); // change to '1' when a device reset sequence is implemented 586 | } 587 | 588 | ///@} 589 | 590 | 591 | #endif /* __DAP_CONFIG_H__ */ 592 | -------------------------------------------------------------------------------- /include/board_debug_probe_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef BOARD_DEBUG_PROBE_H_ 27 | #define BOARD_DEBUG_PROBE_H_ 28 | 29 | #define PROBE_IO_SWDI 30 | #define PROBE_CDC_UART 31 | // No reset pin 32 | 33 | // PIO config 34 | #define PROBE_SM 0 35 | #define PROBE_PIN_OFFSET 12 36 | #define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) 37 | // For level-shifted input. 38 | #define PROBE_PIN_SWDI (PROBE_PIN_OFFSET + 1) 39 | #define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 2) 40 | 41 | // UART config 42 | #define PROBE_UART_TX 4 43 | #define PROBE_UART_RX 5 44 | #define PROBE_UART_INTERFACE uart1 45 | #define PROBE_UART_BAUDRATE 115200 46 | 47 | #define PROBE_USB_CONNECTED_LED 2 48 | #define PROBE_DAP_CONNECTED_LED 15 49 | #define PROBE_DAP_RUNNING_LED 16 50 | #define PROBE_UART_RX_LED 7 51 | #define PROBE_UART_TX_LED 8 52 | 53 | #define PROBE_PRODUCT_STRING "Debug Probe (CMSIS-DAP)" 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/board_example_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef BOARD_EXAMPLE_H_ 27 | #define BOARD_EXAMPLE_H_ 28 | #error "Example board configuration requested - specify PICO_BOARD and re-run CMake." 29 | 30 | /* Select one of these. */ 31 | /* Direct connection - SWCLK/SWDIO on two GPIOs */ 32 | #define PROBE_IO_RAW 33 | /* SWCLK connected to a GPIO, SWDO driven from a GPIO, SWDI sampled via a level shifter */ 34 | #define PROBE_IO_SWDI 35 | /* Level-shifted SWCLK, SWDIO with separate SWDO, SWDI and OE_N pin */ 36 | #define PROBE_IO_OEN 37 | 38 | /* Include CDC interface to bridge to target UART. Omit if not used. */ 39 | #define PROBE_CDC_UART 40 | 41 | /* Board implements hardware flow control for UART RTS/CTS instead of ACM control */ 42 | #define PROBE_UART_HWFC 43 | 44 | /* Target reset GPIO (active-low). Omit if not used.*/ 45 | #define PROBE_PIN_RESET 1 46 | 47 | #define PROBE_SM 0 48 | #define PROBE_PIN_OFFSET 12 49 | /* PIO config for PROBE_IO_RAW */ 50 | #if defined(PROBE_IO_RAW) 51 | #define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) 52 | #define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1) 53 | #endif 54 | 55 | /* PIO config for PROBE_IO_SWDI */ 56 | #if defined(PROBE_IO_SWDI) 57 | #define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) 58 | #define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1) 59 | #define PROBE_PIN_SWDI (PROBE_PIN_OFFSET + 2) 60 | #endif 61 | 62 | /* PIO config for PROBE_IO_OEN - note that SWDIOEN and SWCLK are both side_set signals, so must be consecutive. */ 63 | #if defined(PROBE_IO_OEN) 64 | #define PROBE_PIN_SWDIOEN (PROBE_PIN_OFFSET + 0) 65 | #define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 1) 66 | #define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 2) 67 | #define PROBE_PIN_SWDI (PROBE_PIN_OFFSET + 3) 68 | #endif 69 | 70 | #if defined(PROBE_CDC_UART) 71 | #define PROBE_UART_TX 4 72 | #define PROBE_UART_RX 5 73 | #define PROBE_UART_INTERFACE uart1 74 | #define PROBE_UART_BAUDRATE 115200 75 | 76 | #if defined(PROBE_UART_HWFC) 77 | /* Hardware flow control - see 1.4.3 in the RP2040 datasheet for valid pin settings */ 78 | #define PROBE_UART_CTS 6 79 | #define PROBE_UART_RTS 7 80 | #else 81 | /* Software flow control - RTS and DTR can be omitted if not used */ 82 | #define PROBE_UART_RTS 9 83 | #endif 84 | #define PROBE_UART_DTR 10 85 | 86 | #endif 87 | 88 | /* LED config - some or all of these can be omitted if not used */ 89 | #define PROBE_USB_CONNECTED_LED 2 90 | #define PROBE_DAP_CONNECTED_LED 15 91 | #define PROBE_DAP_RUNNING_LED 16 92 | #define PROBE_UART_RX_LED 7 93 | #define PROBE_UART_TX_LED 8 94 | 95 | #define PROBE_PRODUCT_STRING "Example Debug Probe" 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /include/board_pico_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef BOARD_PICO_H_ 27 | #define BOARD_PICO_H_ 28 | 29 | #define PROBE_IO_RAW 30 | #define PROBE_CDC_UART 31 | 32 | // PIO config 33 | #define PROBE_SM 0 34 | #define PROBE_PIN_OFFSET 2 35 | #define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) // 2 36 | #define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1) // 3 37 | // Target reset config 38 | #if false 39 | #define PROBE_PIN_RESET 1 40 | #endif 41 | 42 | // UART config 43 | #define PROBE_UART_TX 4 44 | #define PROBE_UART_RX 5 45 | #define PROBE_UART_INTERFACE uart1 46 | #define PROBE_UART_BAUDRATE 115200 47 | 48 | #define PROBE_USB_CONNECTED_LED 25 49 | 50 | #define PROBE_PRODUCT_STRING "Debugprobe on Pico (CMSIS-DAP)" 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | # This is a copy of /external/pico_sdk_import.cmake 3 | 4 | # This can be dropped into an external project to help locate this SDK 5 | # It should be include()ed prior to project() 6 | 7 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 8 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 9 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 10 | endif () 11 | 12 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 13 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 14 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 15 | endif () 16 | 17 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 18 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 19 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 20 | endif () 21 | 22 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 23 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 24 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 25 | 26 | if (NOT PICO_SDK_PATH) 27 | if (PICO_SDK_FETCH_FROM_GIT) 28 | include(FetchContent) 29 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 30 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 31 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 32 | endif () 33 | # GIT_SUBMODULES_RECURSE was added in 3.17 34 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 35 | FetchContent_Declare( 36 | pico_sdk 37 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 38 | GIT_TAG master 39 | GIT_SUBMODULES_RECURSE FALSE 40 | ) 41 | else () 42 | FetchContent_Declare( 43 | pico_sdk 44 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 45 | GIT_TAG master 46 | ) 47 | endif () 48 | 49 | if (NOT pico_sdk) 50 | message("Downloading Raspberry Pi Pico SDK") 51 | FetchContent_Populate(pico_sdk) 52 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 53 | endif () 54 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 55 | else () 56 | message(FATAL_ERROR 57 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 58 | ) 59 | endif () 60 | endif () 61 | 62 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 63 | if (NOT EXISTS ${PICO_SDK_PATH}) 64 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 65 | endif () 66 | 67 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 68 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 69 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 70 | endif () 71 | 72 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 73 | 74 | include(${PICO_SDK_INIT_CMAKE_FILE}) 75 | -------------------------------------------------------------------------------- /src/FreeRTOSConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeRTOS V202107.00 3 | * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | * the Software, and to permit persons to whom the Software is furnished to do so, 10 | * subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | * http://www.FreeRTOS.org 23 | * http://aws.amazon.com/freertos 24 | * 25 | * 1 tab == 4 spaces! 26 | */ 27 | 28 | #ifndef FREERTOS_CONFIG_H 29 | #define FREERTOS_CONFIG_H 30 | 31 | /*----------------------------------------------------------- 32 | * Application specific definitions. 33 | * 34 | * These definitions should be adjusted for your particular hardware and 35 | * application requirements. 36 | * 37 | * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE 38 | * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 39 | * 40 | * See http://www.freertos.org/a00110.html 41 | *----------------------------------------------------------*/ 42 | 43 | /* Scheduler Related */ 44 | #define configUSE_PREEMPTION 1 45 | #define configUSE_TICKLESS_IDLE 0 46 | #define configUSE_IDLE_HOOK 0 47 | #define configUSE_TICK_HOOK 1 48 | #define configTICK_RATE_HZ ( ( TickType_t ) 20000 ) 49 | #define configMAX_PRIORITIES 32 50 | #define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256 51 | #define configUSE_16_BIT_TICKS 0 52 | 53 | #define configIDLE_SHOULD_YIELD 1 54 | 55 | /* Synchronization Related */ 56 | #define configUSE_MUTEXES 1 57 | #define configUSE_RECURSIVE_MUTEXES 1 58 | #define configUSE_APPLICATION_TASK_TAG 0 59 | #define configUSE_COUNTING_SEMAPHORES 1 60 | #define configQUEUE_REGISTRY_SIZE 8 61 | #define configUSE_QUEUE_SETS 1 62 | #define configUSE_TIME_SLICING 1 63 | #define configUSE_NEWLIB_REENTRANT 0 64 | #define configENABLE_BACKWARD_COMPATIBILITY 0 65 | #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 66 | 67 | /* System */ 68 | #define configSTACK_DEPTH_TYPE uint32_t 69 | #define configMESSAGE_BUFFER_LENGTH_TYPE size_t 70 | 71 | /* Memory allocation related definitions. */ 72 | #define configSUPPORT_STATIC_ALLOCATION 0 73 | #define configSUPPORT_DYNAMIC_ALLOCATION 1 74 | #define configTOTAL_HEAP_SIZE (64*1024) 75 | #define configAPPLICATION_ALLOCATED_HEAP 0 76 | 77 | /* Hook function related definitions. */ 78 | #define configCHECK_FOR_STACK_OVERFLOW 2 79 | #define configUSE_MALLOC_FAILED_HOOK 1 80 | #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 81 | 82 | /* Run time and task stats gathering related definitions. */ 83 | #define configGENERATE_RUN_TIME_STATS 0 84 | #define configUSE_TRACE_FACILITY 1 85 | #define configUSE_STATS_FORMATTING_FUNCTIONS 0 86 | 87 | /* Co-routine related definitions. */ 88 | #define configUSE_CO_ROUTINES 0 89 | #define configMAX_CO_ROUTINE_PRIORITIES 1 90 | 91 | /* Software timer related definitions. */ 92 | #define configUSE_TIMERS 1 93 | #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) 94 | #define configTIMER_QUEUE_LENGTH 10 95 | #define configTIMER_TASK_STACK_DEPTH 1024 96 | 97 | /* Interrupt nesting behaviour configuration. */ 98 | /* 99 | #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] 100 | #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] 101 | #define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] 102 | */ 103 | 104 | /* SMP port only */ 105 | #define configNUM_CORES 1 106 | #define configTICK_CORE 1 107 | #define configRUN_MULTIPLE_PRIORITIES 1 108 | 109 | /* RP2040 specific */ 110 | #define configSUPPORT_PICO_SYNC_INTEROP 1 111 | #define configSUPPORT_PICO_TIME_INTEROP 1 112 | 113 | /* RP2350 grows some features */ 114 | #define configENABLE_FPU 1 115 | #define configENABLE_MPU 0 116 | #define configENABLE_TRUSTZONE 0 117 | #define configRUN_FREERTOS_SECURE_ONLY 1 118 | #define configMAX_SYSCALL_INTERRUPT_PRIORITY 16 119 | 120 | #include 121 | /* Define to trap errors during development. */ 122 | #define configASSERT(x) assert(x) 123 | 124 | /* Set the following definitions to 1 to include the API function, or zero 125 | to exclude the API function. */ 126 | #define INCLUDE_vTaskPrioritySet 1 127 | #define INCLUDE_uxTaskPriorityGet 1 128 | #define INCLUDE_vTaskDelete 1 129 | #define INCLUDE_vTaskSuspend 1 130 | #define INCLUDE_vTaskDelayUntil 1 131 | #define INCLUDE_vTaskDelay 1 132 | #define INCLUDE_xTaskGetSchedulerState 1 133 | #define INCLUDE_xTaskGetCurrentTaskHandle 1 134 | #define INCLUDE_uxTaskGetStackHighWaterMark 1 135 | #define INCLUDE_xTaskGetIdleTaskHandle 1 136 | #define INCLUDE_eTaskGetState 1 137 | #define INCLUDE_xTimerPendFunctionCall 1 138 | #define INCLUDE_xTaskAbortDelay 1 139 | #define INCLUDE_xTaskGetHandle 1 140 | #define INCLUDE_xTaskResumeFromISR 1 141 | #define INCLUDE_xQueueGetMutexHolder 1 142 | 143 | /* A header file that defines trace macro can be included here. */ 144 | 145 | #endif /* FREERTOS_CONFIG_H */ 146 | 147 | -------------------------------------------------------------------------------- /src/cdc_uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include "FreeRTOS.h" 28 | #include "task.h" 29 | #include "tusb.h" 30 | 31 | #include "probe_config.h" 32 | 33 | TaskHandle_t uart_taskhandle; 34 | TickType_t last_wake, interval = 100; 35 | volatile TickType_t break_expiry; 36 | volatile bool timed_break; 37 | 38 | /* Max 1 FIFO worth of data */ 39 | static uint8_t tx_buf[32]; 40 | static uint8_t rx_buf[32]; 41 | // Actually s^-1 so 25ms 42 | #define DEBOUNCE_MS 40 43 | static uint debounce_ticks = 5; 44 | 45 | #ifdef PROBE_UART_TX_LED 46 | static volatile uint tx_led_debounce; 47 | #endif 48 | 49 | #ifdef PROBE_UART_RX_LED 50 | static uint rx_led_debounce; 51 | #endif 52 | 53 | void cdc_uart_init(void) { 54 | gpio_set_function(PROBE_UART_TX, GPIO_FUNC_UART); 55 | gpio_set_function(PROBE_UART_RX, GPIO_FUNC_UART); 56 | gpio_set_pulls(PROBE_UART_TX, 1, 0); 57 | gpio_set_pulls(PROBE_UART_RX, 1, 0); 58 | uart_init(PROBE_UART_INTERFACE, PROBE_UART_BAUDRATE); 59 | 60 | #ifdef PROBE_UART_TX_LED 61 | tx_led_debounce = 0; 62 | gpio_init(PROBE_UART_TX_LED); 63 | gpio_set_dir(PROBE_UART_TX_LED, GPIO_OUT); 64 | #endif 65 | #ifdef PROBE_UART_RX_LED 66 | rx_led_debounce = 0; 67 | gpio_init(PROBE_UART_RX_LED); 68 | gpio_set_dir(PROBE_UART_RX_LED, GPIO_OUT); 69 | #endif 70 | 71 | #ifdef PROBE_UART_HWFC 72 | /* HWFC implies that hardware flow control is implemented and the 73 | * UART operates in "full-duplex" mode (See USB CDC PSTN120 6.3.12). 74 | * Default to pulling in the active direction, so an unconnected CTS 75 | * behaves the same as if CTS were not enabled. */ 76 | gpio_set_pulls(PROBE_UART_CTS, 0, 1); 77 | gpio_set_function(PROBE_UART_RTS, GPIO_FUNC_UART); 78 | gpio_set_function(PROBE_UART_CTS, GPIO_FUNC_UART); 79 | uart_set_hw_flow(PROBE_UART_INTERFACE, true, true); 80 | #else 81 | #ifdef PROBE_UART_RTS 82 | gpio_init(PROBE_UART_RTS); 83 | gpio_set_dir(PROBE_UART_RTS, GPIO_OUT); 84 | gpio_put(PROBE_UART_RTS, 1); 85 | #endif 86 | #endif 87 | 88 | #ifdef PROBE_UART_DTR 89 | gpio_init(PROBE_UART_DTR); 90 | gpio_set_dir(PROBE_UART_DTR, GPIO_OUT); 91 | gpio_put(PROBE_UART_DTR, 1); 92 | #endif 93 | } 94 | 95 | bool cdc_task(void) 96 | { 97 | static int was_connected = 0; 98 | static uint cdc_tx_oe = 0; 99 | uint rx_len = 0; 100 | bool keep_alive = false; 101 | 102 | // Consume uart fifo regardless even if not connected 103 | while(uart_is_readable(PROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) { 104 | rx_buf[rx_len++] = uart_getc(PROBE_UART_INTERFACE); 105 | } 106 | 107 | if (tud_cdc_connected()) { 108 | was_connected = 1; 109 | int written = 0; 110 | /* Implicit overflow if we don't write all the bytes to the host. 111 | * Also throw away bytes if we can't write... */ 112 | if (rx_len) { 113 | #ifdef PROBE_UART_RX_LED 114 | gpio_put(PROBE_UART_RX_LED, 1); 115 | rx_led_debounce = debounce_ticks; 116 | #endif 117 | written = MIN(tud_cdc_write_available(), rx_len); 118 | if (rx_len > written) 119 | cdc_tx_oe++; 120 | 121 | if (written > 0) { 122 | tud_cdc_write(rx_buf, written); 123 | tud_cdc_write_flush(); 124 | } 125 | } else { 126 | #ifdef PROBE_UART_RX_LED 127 | if (rx_led_debounce) 128 | rx_led_debounce--; 129 | else 130 | gpio_put(PROBE_UART_RX_LED, 0); 131 | #endif 132 | } 133 | 134 | /* Reading from a firehose and writing to a FIFO. */ 135 | size_t watermark = MIN(tud_cdc_available(), sizeof(tx_buf)); 136 | if (watermark > 0) { 137 | size_t tx_len; 138 | #ifdef PROBE_UART_TX_LED 139 | gpio_put(PROBE_UART_TX_LED, 1); 140 | tx_led_debounce = debounce_ticks; 141 | #endif 142 | /* Batch up to half a FIFO of data - don't clog up on RX */ 143 | watermark = MIN(watermark, 16); 144 | tx_len = tud_cdc_read(tx_buf, watermark); 145 | uart_write_blocking(PROBE_UART_INTERFACE, tx_buf, tx_len); 146 | } else { 147 | #ifdef PROBE_UART_TX_LED 148 | if (tx_led_debounce) 149 | tx_led_debounce--; 150 | else 151 | gpio_put(PROBE_UART_TX_LED, 0); 152 | #endif 153 | } 154 | /* Pending break handling */ 155 | if (timed_break) { 156 | if (((int)break_expiry - (int)xTaskGetTickCount()) < 0) { 157 | timed_break = false; 158 | uart_set_break(PROBE_UART_INTERFACE, false); 159 | #ifdef PROBE_UART_TX_LED 160 | tx_led_debounce = 0; 161 | #endif 162 | } else { 163 | keep_alive = true; 164 | } 165 | } 166 | } else if (was_connected) { 167 | tud_cdc_write_clear(); 168 | uart_set_break(PROBE_UART_INTERFACE, false); 169 | timed_break = false; 170 | was_connected = 0; 171 | #ifdef PROBE_UART_TX_LED 172 | tx_led_debounce = 0; 173 | #endif 174 | cdc_tx_oe = 0; 175 | } 176 | return keep_alive; 177 | } 178 | 179 | void cdc_thread(void *ptr) 180 | { 181 | BaseType_t delayed; 182 | last_wake = xTaskGetTickCount(); 183 | bool keep_alive; 184 | /* Threaded with a polling interval that scales according to linerate */ 185 | while (1) { 186 | keep_alive = cdc_task(); 187 | if (!keep_alive) { 188 | delayed = xTaskDelayUntil(&last_wake, interval); 189 | if (delayed == pdFALSE) 190 | last_wake = xTaskGetTickCount(); 191 | } 192 | } 193 | } 194 | 195 | void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) 196 | { 197 | uart_parity_t parity; 198 | uint data_bits, stop_bits; 199 | /* Set the tick thread interval to the amount of time it takes to 200 | * fill up half a FIFO. Millis is too coarse for integer divide. 201 | */ 202 | uint32_t micros = (1000 * 1000 * 16 * 10) / MAX(line_coding->bit_rate, 1); 203 | /* Modifying state, so park the thread before changing it. */ 204 | if (tud_cdc_connected()) 205 | vTaskSuspend(uart_taskhandle); 206 | interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ)); 207 | debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS)); 208 | probe_info("New baud rate %ld micros %ld interval %lu\n", 209 | line_coding->bit_rate, micros, interval); 210 | uart_deinit(PROBE_UART_INTERFACE); 211 | tud_cdc_write_clear(); 212 | tud_cdc_read_flush(); 213 | uart_init(PROBE_UART_INTERFACE, line_coding->bit_rate); 214 | 215 | switch (line_coding->parity) { 216 | case CDC_LINE_CODING_PARITY_ODD: 217 | parity = UART_PARITY_ODD; 218 | break; 219 | case CDC_LINE_CODING_PARITY_EVEN: 220 | parity = UART_PARITY_EVEN; 221 | break; 222 | default: 223 | probe_info("invalid parity setting %u\n", line_coding->parity); 224 | /* fallthrough */ 225 | case CDC_LINE_CODING_PARITY_NONE: 226 | parity = UART_PARITY_NONE; 227 | break; 228 | } 229 | 230 | switch (line_coding->data_bits) { 231 | case 5: 232 | case 6: 233 | case 7: 234 | case 8: 235 | data_bits = line_coding->data_bits; 236 | break; 237 | default: 238 | probe_info("invalid data bits setting: %u\n", line_coding->data_bits); 239 | data_bits = 8; 240 | break; 241 | } 242 | 243 | /* The PL011 only supports 1 or 2 stop bits. 1.5 stop bits is translated to 2, 244 | * which is safer than the alternative. */ 245 | switch (line_coding->stop_bits) { 246 | case CDC_LINE_CONDING_STOP_BITS_1_5: 247 | case CDC_LINE_CONDING_STOP_BITS_2: 248 | stop_bits = 2; 249 | break; 250 | default: 251 | probe_info("invalid stop bits setting: %u\n", line_coding->stop_bits); 252 | /* fallthrough */ 253 | case CDC_LINE_CONDING_STOP_BITS_1: 254 | stop_bits = 1; 255 | break; 256 | } 257 | 258 | uart_set_format(PROBE_UART_INTERFACE, data_bits, stop_bits, parity); 259 | /* Windows likes to arbitrarily set/get line coding after dtr/rts changes, so 260 | * don't resume if we shouldn't */ 261 | if(tud_cdc_connected()) 262 | vTaskResume(uart_taskhandle); 263 | } 264 | 265 | void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) 266 | { 267 | #ifdef PROBE_UART_RTS 268 | gpio_put(PROBE_UART_RTS, !rts); 269 | #endif 270 | #ifdef PROBE_UART_DTR 271 | gpio_put(PROBE_UART_DTR, !dtr); 272 | #endif 273 | 274 | /* CDC drivers use linestate as a bodge to activate/deactivate the interface. 275 | * Resume our UART polling on activate, stop on deactivate */ 276 | if (!dtr) { 277 | vTaskSuspend(uart_taskhandle); 278 | #ifdef PROBE_UART_RX_LED 279 | gpio_put(PROBE_UART_RX_LED, 0); 280 | rx_led_debounce = 0; 281 | #endif 282 | #ifdef PROBE_UART_TX_LED 283 | gpio_put(PROBE_UART_TX_LED, 0); 284 | tx_led_debounce = 0; 285 | #endif 286 | } else 287 | vTaskResume(uart_taskhandle); 288 | } 289 | 290 | void tud_cdc_send_break_cb(uint8_t itf, uint16_t wValue) { 291 | switch(wValue) { 292 | case 0: 293 | uart_set_break(PROBE_UART_INTERFACE, false); 294 | timed_break = false; 295 | #ifdef PROBE_UART_TX_LED 296 | tx_led_debounce = 0; 297 | #endif 298 | break; 299 | case 0xffff: 300 | uart_set_break(PROBE_UART_INTERFACE, true); 301 | timed_break = false; 302 | #ifdef PROBE_UART_TX_LED 303 | gpio_put(PROBE_UART_TX_LED, 1); 304 | tx_led_debounce = 1 << 30; 305 | #endif 306 | break; 307 | default: 308 | uart_set_break(PROBE_UART_INTERFACE, true); 309 | timed_break = true; 310 | #ifdef PROBE_UART_TX_LED 311 | gpio_put(PROBE_UART_TX_LED, 1); 312 | tx_led_debounce = 1 << 30; 313 | #endif 314 | break_expiry = xTaskGetTickCount() + (wValue * (configTICK_RATE_HZ / 1000)); 315 | break; 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/cdc_uart.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef CDC_UART_H 27 | #define CDC_UART_H 28 | 29 | void cdc_thread(void *ptr); 30 | void cdc_uart_init(void); 31 | bool cdc_task(void); 32 | 33 | extern TaskHandle_t uart_taskhandle; 34 | 35 | #endif -------------------------------------------------------------------------------- /src/get_serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Federico Zuccardi Merli 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include "pico.h" 28 | #include "pico/unique_id.h" 29 | #include "get_serial.h" 30 | 31 | /* C string for iSerialNumber in USB Device Descriptor, two chars per byte + terminating NUL */ 32 | char usb_serial[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1]; 33 | 34 | /* Why a uint8_t[8] array inside a struct instead of an uint64_t an inquiring mind might wonder */ 35 | static pico_unique_board_id_t uID; 36 | 37 | void usb_serial_init(void) 38 | { 39 | pico_get_unique_board_id(&uID); 40 | 41 | for (int i = 0; i < PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2; i++) 42 | { 43 | /* Byte index inside the uid array */ 44 | int bi = i / 2; 45 | /* Use high nibble first to keep memory order (just cosmetics) */ 46 | uint8_t nibble = (uID.id[bi] >> 4) & 0x0F; 47 | uID.id[bi] <<= 4; 48 | /* Binary to hex digit */ 49 | usb_serial[i] = nibble < 10 ? nibble + '0' : nibble + 'A' - 10; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/get_serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Federico Zuccardi Merli 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef GET_SERIAL_H_ 27 | #define GET_SERIAL_H_ 28 | 29 | /* Contains unique serial number string (NUL terminated) after call to init_usb_serial */ 30 | extern char usb_serial[]; 31 | 32 | /* Fills unique_serial with the flash unique id */ 33 | extern void usb_serial_init(void); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 5 | * Copyright (c) 2021 Peter Lawrence 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | #include "FreeRTOS.h" 28 | #include "task.h" 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #if PICO_SDK_VERSION_MAJOR >= 2 35 | #include "bsp/board_api.h" 36 | #else 37 | #include "bsp/board.h" 38 | #endif 39 | #include "tusb.h" 40 | 41 | #include "probe_config.h" 42 | #include "probe.h" 43 | #include "cdc_uart.h" 44 | #include "get_serial.h" 45 | #include "tusb_edpt_handler.h" 46 | #include "DAP.h" 47 | #include "hardware/structs/usb.h" 48 | 49 | // UART0 for debugprobe debug 50 | // UART1 for debugprobe to target device 51 | 52 | static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE]; 53 | static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE]; 54 | 55 | #define THREADED 1 56 | 57 | #define UART_TASK_PRIO (tskIDLE_PRIORITY + 3) 58 | #define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2) 59 | #define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1) 60 | 61 | TaskHandle_t dap_taskhandle, tud_taskhandle, mon_taskhandle; 62 | 63 | static int was_configured; 64 | 65 | void dev_mon(void *ptr) 66 | { 67 | uint32_t sof[3]; 68 | int i = 0; 69 | TickType_t wake; 70 | wake = xTaskGetTickCount(); 71 | do { 72 | /* ~5 SOF events per tick */ 73 | xTaskDelayUntil(&wake, 100); 74 | if (tud_connected() && !tud_suspended()) { 75 | sof[i++] = usb_hw->sof_rd & USB_SOF_RD_BITS; 76 | i = i % 3; 77 | } else { 78 | for (i = 0; i < 3; i++) 79 | sof[i] = 0; 80 | } 81 | if ((sof[0] | sof[1] | sof[2]) != 0) { 82 | if ((sof[0] == sof[1]) && (sof[1] == sof[2])) { 83 | probe_info("Watchdog timeout! Resetting USBD\n"); 84 | /* uh oh, signal disconnect (implicitly resets the controller) */ 85 | tud_deinit(0); 86 | /* Make sure the port got the message */ 87 | xTaskDelayUntil(&wake, 1); 88 | tud_init(0); 89 | } 90 | } 91 | } while (1); 92 | } 93 | 94 | void usb_thread(void *ptr) 95 | { 96 | #ifdef PROBE_USB_CONNECTED_LED 97 | gpio_init(PROBE_USB_CONNECTED_LED); 98 | gpio_set_dir(PROBE_USB_CONNECTED_LED, GPIO_OUT); 99 | #endif 100 | TickType_t wake; 101 | wake = xTaskGetTickCount(); 102 | do { 103 | tud_task(); 104 | #ifdef PROBE_USB_CONNECTED_LED 105 | if (!gpio_get(PROBE_USB_CONNECTED_LED) && tud_ready()) 106 | gpio_put(PROBE_USB_CONNECTED_LED, 1); 107 | else 108 | gpio_put(PROBE_USB_CONNECTED_LED, 0); 109 | #endif 110 | // If suspended or disconnected, delay for 1ms (20 ticks) 111 | if (tud_suspended() || !tud_connected()) 112 | xTaskDelayUntil(&wake, 20); 113 | // Go to sleep for up to a tick if nothing to do 114 | else if (!tud_task_event_ready()) 115 | xTaskDelayUntil(&wake, 1); 116 | } while (1); 117 | } 118 | 119 | // Workaround API change in 0.13 120 | #if (TUSB_VERSION_MAJOR == 0) && (TUSB_VERSION_MINOR <= 12) 121 | #define tud_vendor_flush(x) ((void)0) 122 | #endif 123 | 124 | int main(void) { 125 | // Declare pins in binary information 126 | bi_decl_config(); 127 | 128 | board_init(); 129 | usb_serial_init(); 130 | cdc_uart_init(); 131 | tusb_init(); 132 | stdio_uart_init(); 133 | 134 | DAP_Setup(); 135 | 136 | probe_info("Welcome to debugprobe!\n"); 137 | 138 | if (THREADED) { 139 | xTaskCreate(usb_thread, "TUD", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &tud_taskhandle); 140 | #if PICO_RP2040 141 | xTaskCreate(dev_mon, "WDOG", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &mon_taskhandle); 142 | #endif 143 | vTaskStartScheduler(); 144 | } 145 | 146 | while (!THREADED) { 147 | tud_task(); 148 | cdc_task(); 149 | 150 | #if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2) 151 | if (tud_vendor_available()) { 152 | uint32_t resp_len; 153 | tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer)); 154 | resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer); 155 | tud_vendor_write(TxDataBuffer, resp_len); 156 | } 157 | #endif 158 | } 159 | 160 | return 0; 161 | } 162 | 163 | uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) 164 | { 165 | // TODO not Implemented 166 | (void) itf; 167 | (void) report_id; 168 | (void) report_type; 169 | (void) buffer; 170 | (void) reqlen; 171 | 172 | return 0; 173 | } 174 | 175 | void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* RxDataBuffer, uint16_t bufsize) 176 | { 177 | uint32_t response_size = TU_MIN(CFG_TUD_HID_EP_BUFSIZE, bufsize); 178 | 179 | // This doesn't use multiple report and report ID 180 | (void) itf; 181 | (void) report_id; 182 | (void) report_type; 183 | 184 | DAP_ProcessCommand(RxDataBuffer, TxDataBuffer); 185 | 186 | tud_hid_report(0, TxDataBuffer, response_size); 187 | } 188 | 189 | #if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2) 190 | extern uint8_t const desc_ms_os_20[]; 191 | 192 | bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) 193 | { 194 | // nothing to with DATA & ACK stage 195 | if (stage != CONTROL_STAGE_SETUP) return true; 196 | 197 | switch (request->bmRequestType_bit.type) 198 | { 199 | case TUSB_REQ_TYPE_VENDOR: 200 | switch (request->bRequest) 201 | { 202 | case 1: 203 | if ( request->wIndex == 7 ) 204 | { 205 | // Get Microsoft OS 2.0 compatible descriptor 206 | uint16_t total_len; 207 | memcpy(&total_len, desc_ms_os_20+8, 2); 208 | 209 | return tud_control_xfer(rhport, request, (void*) desc_ms_os_20, total_len); 210 | }else 211 | { 212 | return false; 213 | } 214 | 215 | default: break; 216 | } 217 | break; 218 | default: break; 219 | } 220 | 221 | // stall unknown request 222 | return false; 223 | } 224 | #endif 225 | 226 | void tud_suspend_cb(bool remote_wakeup_en) 227 | { 228 | probe_info("Suspended\n"); 229 | /* Were we actually configured? If not, threads don't exist */ 230 | if (was_configured) { 231 | vTaskSuspend(uart_taskhandle); 232 | vTaskSuspend(dap_taskhandle); 233 | } 234 | /* slow down clk_sys for power saving ? */ 235 | } 236 | 237 | void tud_resume_cb(void) 238 | { 239 | probe_info("Resumed\n"); 240 | if (was_configured) { 241 | vTaskResume(uart_taskhandle); 242 | vTaskResume(dap_taskhandle); 243 | } 244 | } 245 | 246 | void tud_unmount_cb(void) 247 | { 248 | probe_info("Disconnected\n"); 249 | vTaskSuspend(uart_taskhandle); 250 | vTaskSuspend(dap_taskhandle); 251 | vTaskDelete(uart_taskhandle); 252 | vTaskDelete(dap_taskhandle); 253 | was_configured = 0; 254 | } 255 | 256 | void tud_mount_cb(void) 257 | { 258 | probe_info("Connected, Configured\n"); 259 | if (!was_configured) { 260 | /* UART needs to preempt USB as if we don't, characters get lost */ 261 | xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle); 262 | /* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */ 263 | xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle); 264 | was_configured = 1; 265 | } 266 | } 267 | 268 | void vApplicationTickHook (void) 269 | { 270 | }; 271 | 272 | void vApplicationStackOverflowHook(TaskHandle_t Task, char *pcTaskName) 273 | { 274 | panic("stack overflow (not the helpful kind) for %s\n", *pcTaskName); 275 | } 276 | 277 | void vApplicationMallocFailedHook(void) 278 | { 279 | panic("Malloc Failed\n"); 280 | }; 281 | -------------------------------------------------------------------------------- /src/probe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include "probe_config.h" 34 | #include "probe.h" 35 | #include "tusb.h" 36 | 37 | #define DIV_ROUND_UP(m, n) (((m) + (n) - 1) / (n)) 38 | 39 | // Only want to set / clear one gpio per event so go up in powers of 2 40 | enum _dbg_pins { 41 | DBG_PIN_WRITE = 1, 42 | DBG_PIN_WRITE_WAIT = 2, 43 | DBG_PIN_READ = 4, 44 | DBG_PIN_PKT = 8, 45 | }; 46 | 47 | CU_REGISTER_DEBUG_PINS(probe_timing) 48 | 49 | // Uncomment to enable debug 50 | //CU_SELECT_DEBUG_PINS(probe_timing) 51 | 52 | #define PROBE_BUF_SIZE 8192 53 | struct _probe { 54 | // PIO offset 55 | uint offset; 56 | uint initted; 57 | }; 58 | 59 | static struct _probe probe; 60 | 61 | void probe_set_swclk_freq(uint freq_khz) { 62 | uint clk_sys_freq_khz = clock_get_hz(clk_sys) / 1000; 63 | probe_info("Set swclk freq %dKHz sysclk %dkHz\n", freq_khz, clk_sys_freq_khz); 64 | // Round up (otherwise fast swclks get faster) 65 | uint32_t divider = (((clk_sys_freq_khz + freq_khz - 1)/ freq_khz) + 3) / 4; 66 | if (divider == 0) 67 | divider = 1; 68 | if (divider > 65535) 69 | divider = 65535; 70 | 71 | pio_sm_set_clkdiv_int_frac(pio0, PROBE_SM, divider, 0); 72 | } 73 | 74 | void probe_assert_reset(bool state) 75 | { 76 | #if defined(PROBE_PIN_RESET) 77 | /* Change the direction to out to drive pin to 0 or to in to emulate open drain */ 78 | gpio_set_dir(PROBE_PIN_RESET, state == 0 ? GPIO_OUT : GPIO_IN); 79 | #endif 80 | } 81 | 82 | int probe_reset_level(void) 83 | { 84 | #if defined(PROBE_PIN_RESET) 85 | return gpio_get(PROBE_PIN_RESET); 86 | #else 87 | return 0; 88 | #endif 89 | } 90 | 91 | typedef enum probe_pio_command { 92 | CMD_WRITE = 0, 93 | CMD_SKIP, 94 | CMD_TURNAROUND, 95 | CMD_READ 96 | } probe_pio_command_t; 97 | 98 | static inline uint32_t fmt_probe_command(uint bit_count, bool out_en, probe_pio_command_t cmd) { 99 | uint cmd_addr = 100 | cmd == CMD_WRITE ? probe.offset + probe_offset_write_cmd : 101 | cmd == CMD_SKIP ? probe.offset + probe_offset_get_next_cmd : 102 | cmd == CMD_TURNAROUND ? probe.offset + probe_offset_turnaround_cmd : 103 | probe.offset + probe_offset_read_cmd; 104 | return ((bit_count - 1) & 0xff) | ((uint)out_en << 8) | (cmd_addr << 9); 105 | } 106 | 107 | void probe_write_bits(uint bit_count, uint32_t data_byte) { 108 | DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE); 109 | pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, true, CMD_WRITE)); 110 | pio_sm_put_blocking(pio0, PROBE_SM, data_byte); 111 | probe_dump("Write %d bits 0x%x\n", bit_count, data_byte); 112 | // Return immediately so we can cue up the next command whilst this one runs 113 | DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE); 114 | } 115 | 116 | void probe_hiz_clocks(uint bit_count) { 117 | pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, false, CMD_TURNAROUND)); 118 | pio_sm_put_blocking(pio0, PROBE_SM, 0); 119 | } 120 | 121 | uint32_t probe_read_bits(uint bit_count) { 122 | DEBUG_PINS_SET(probe_timing, DBG_PIN_READ); 123 | pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, false, CMD_READ)); 124 | uint32_t data = pio_sm_get_blocking(pio0, PROBE_SM); 125 | uint32_t data_shifted = data; 126 | if (bit_count < 32) { 127 | data_shifted = data >> (32 - bit_count); 128 | } 129 | 130 | probe_dump("Read %d bits 0x%x (shifted 0x%x)\n", bit_count, data, data_shifted); 131 | DEBUG_PINS_CLR(probe_timing, DBG_PIN_READ); 132 | return data_shifted; 133 | } 134 | 135 | static void probe_wait_idle() { 136 | pio0->fdebug = 1u << (PIO_FDEBUG_TXSTALL_LSB + PROBE_SM); 137 | while (!(pio0->fdebug & (1u << (PIO_FDEBUG_TXSTALL_LSB + PROBE_SM)))) 138 | ; 139 | } 140 | 141 | void probe_read_mode(void) { 142 | pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(0, false, CMD_SKIP)); 143 | probe_wait_idle(); 144 | } 145 | 146 | void probe_write_mode(void) { 147 | pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(0, true, CMD_SKIP)); 148 | probe_wait_idle(); 149 | } 150 | 151 | void probe_init() { 152 | if (!probe.initted) { 153 | probe_gpio_init(); 154 | uint offset = pio_add_program(pio0, &probe_program); 155 | probe.offset = offset; 156 | 157 | pio_sm_config sm_config = probe_program_get_default_config(offset); 158 | probe_sm_init(&sm_config); 159 | pio_sm_init(pio0, PROBE_SM, offset, &sm_config); 160 | 161 | // Set up divisor 162 | probe_set_swclk_freq(1000); 163 | 164 | // Jump SM to command dispatch routine, and enable it 165 | pio_sm_exec(pio0, PROBE_SM, offset + probe_offset_get_next_cmd); 166 | pio_sm_set_enabled(pio0, PROBE_SM, 1); 167 | probe.initted = 1; 168 | } 169 | } 170 | 171 | void probe_deinit(void) 172 | { 173 | if (probe.initted) { 174 | probe_read_mode(); 175 | pio_sm_set_enabled(pio0, PROBE_SM, 0); 176 | pio_remove_program(pio0, &probe_program, probe.offset); 177 | 178 | probe_assert_reset(1); // de-assert nRESET 179 | probe_gpio_deinit(); 180 | probe.initted = 0; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/probe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef PROBE_H_ 27 | #define PROBE_H_ 28 | 29 | #if defined(PROBE_IO_RAW) || defined(PROBE_IO_SWDI) 30 | #include "probe.pio.h" 31 | #endif 32 | 33 | #if defined(PROBE_IO_OEN) 34 | #include "probe_oen.pio.h" 35 | #endif 36 | 37 | void probe_set_swclk_freq(uint freq_khz); 38 | 39 | // Bit counts in the range 1..256 40 | void probe_write_bits(uint bit_count, uint32_t data_byte); 41 | uint32_t probe_read_bits(uint bit_count); 42 | void probe_hiz_clocks(uint bit_count); 43 | 44 | void probe_read_mode(void); 45 | void probe_write_mode(void); 46 | 47 | void probe_init(void); 48 | void probe_deinit(void); 49 | void probe_assert_reset(bool state); 50 | int probe_reset_level(void); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/probe.pio: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021-2023 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | // Every TX FIFO entry is either a command, or up to 32 bits of data. 27 | // Command format: 28 | // 29 | // | 13:9 | 8 | 7:0 | 30 | // | Cmd | Dir | Count | 31 | // 32 | // Count is the number of bits to be transferred by this command, minus 1. 33 | // Dir is the output enable for the SWDIO pin. 34 | // Cmd is the address of the write_cmd, read_cmd or get_next_cmd label. 35 | // 36 | // write_cmd expects a FIFO data entry, but read_cmd does not. 37 | // 38 | // read_cmd pushes data to the FIFO, but write_cmd does not. (The lack of RX 39 | // garbage on writes allows the interface code to return early after pushing a 40 | // write command, as there is no need in general to poll for a command's 41 | // completion as long as all commands are executed in order.) 42 | // 43 | // The SWCLK period is 4 PIO SM execution cycles. 44 | 45 | .program probe 46 | .side_set 1 opt 47 | 48 | public write_cmd: 49 | public turnaround_cmd: ; Alias of write, used for probe_oen.pio 50 | pull 51 | write_bitloop: 52 | out pins, 1 [1] side 0x0 ; Data is output by host on negedge 53 | jmp x-- write_bitloop [1] side 0x1 ; ...and captured by target on posedge 54 | ; Fall through to next command 55 | .wrap_target 56 | public get_next_cmd: 57 | pull side 0x0 ; SWCLK is initially low 58 | out x, 8 ; Get bit count 59 | out pindirs, 1 ; Set SWDIO direction 60 | out pc, 5 ; Go to command routine 61 | 62 | read_bitloop: 63 | nop ; Additional delay on taken loop branch 64 | public read_cmd: 65 | in pins, 1 [1] side 0x1 ; Data is captured by host on posedge 66 | jmp x-- read_bitloop side 0x0 67 | push 68 | .wrap ; Wrap to next command 69 | 70 | 71 | ; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc 72 | % c-sdk { 73 | 74 | static inline void probe_gpio_init() 75 | { 76 | #if defined(PROBE_PIN_RESET) 77 | // Target reset pin: pull up, input to emulate open drain pin 78 | gpio_pull_up(PROBE_PIN_RESET); 79 | // gpio_init will leave the pin cleared and set as input 80 | gpio_init(PROBE_PIN_RESET); 81 | #endif 82 | // Funcsel pins 83 | pio_gpio_init(pio0, PROBE_PIN_SWCLK); 84 | pio_gpio_init(pio0, PROBE_PIN_SWDIO); 85 | // Make sure SWDIO has a pullup on it. Idle state is high 86 | gpio_pull_up(PROBE_PIN_SWDIO); 87 | } 88 | 89 | // DAP interface says all pins have to be High-Z when disabled 90 | static inline void probe_gpio_deinit() 91 | { 92 | #if defined(PROBE_PIN_RESET) 93 | gpio_deinit(PROBE_PIN_RESET); 94 | gpio_disable_pulls(PROBE_PIN_RESET); 95 | #endif 96 | gpio_deinit(PROBE_PIN_SWCLK); 97 | gpio_disable_pulls(PROBE_PIN_SWCLK); 98 | gpio_deinit(PROBE_PIN_SWDIO); 99 | gpio_disable_pulls(PROBE_PIN_SWDIO); 100 | } 101 | 102 | static inline void probe_sm_init(pio_sm_config* sm_config) { 103 | 104 | // Set SWCLK as a sideset pin 105 | sm_config_set_sideset_pins(sm_config, PROBE_PIN_SWCLK); 106 | 107 | // Set SWDIO offset 108 | sm_config_set_out_pins(sm_config, PROBE_PIN_SWDIO, 1); 109 | sm_config_set_set_pins(sm_config, PROBE_PIN_SWDIO, 1); 110 | #ifdef PROBE_IO_SWDI 111 | sm_config_set_in_pins(sm_config, PROBE_PIN_SWDI); 112 | #else 113 | sm_config_set_in_pins(sm_config, PROBE_PIN_SWDIO); 114 | #endif 115 | 116 | 117 | // Set SWD and SWDIO pins as output to start. This will be set in the sm 118 | pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 2, true); 119 | 120 | // shift output right, autopull off, autopull threshold 121 | sm_config_set_out_shift(sm_config, true, false, 0); 122 | // shift input right as swd data is lsb first, autopush off 123 | sm_config_set_in_shift(sm_config, true, false, 0); 124 | } 125 | 126 | %} 127 | -------------------------------------------------------------------------------- /src/probe_config.c: -------------------------------------------------------------------------------- 1 | #include "probe_config.h" 2 | #include "pico/binary_info.h" 3 | 4 | 5 | #define STR_HELPER(x) #x 6 | #define STR(x) STR_HELPER(x) 7 | 8 | 9 | void bi_decl_config() 10 | { 11 | #ifdef PROBE_PIN_RESET 12 | bi_decl(bi_1pin_with_name(PROBE_PIN_RESET, "PROBE RESET")); 13 | #endif 14 | 15 | #ifdef PROBE_PIN_SWCLK 16 | bi_decl(bi_1pin_with_name(PROBE_PIN_SWCLK, "PROBE SWCLK")); 17 | #endif 18 | 19 | #ifdef PROBE_PIN_SWDIO 20 | bi_decl(bi_1pin_with_name(PROBE_PIN_SWDIO, "PROBE SWDIO")); 21 | #endif 22 | 23 | #ifdef PROBE_PIN_SWDI 24 | bi_decl(bi_1pin_with_name(PROBE_PIN_SWDI, "PROBE SWDI")); 25 | #endif 26 | 27 | #ifdef PROBE_PIN_SWDIOEN 28 | bi_decl(bi_1pin_with_name(PROBE_PIN_SWDIOEN, "PROBE SWDIOEN")); 29 | #endif 30 | 31 | #ifdef PROBE_CDC_UART 32 | bi_decl(bi_program_feature("PROBE UART INTERFACE " STR(PROBE_UART_INTERFACE))); 33 | bi_decl(bi_program_feature("PROBE UART BAUDRATE " STR(PROBE_UART_BAUDRATE))); 34 | bi_decl(bi_1pin_with_name(PROBE_UART_TX, "PROBE UART TX")); 35 | bi_decl(bi_1pin_with_name(PROBE_UART_RX, "PROBE UART RX")); 36 | #endif 37 | 38 | #ifdef PROBE_UART_CTS 39 | bi_decl(bi_1pin_with_name(PROBE_UART_CTS, "PROBE UART CTS")); 40 | #endif 41 | #ifdef PROBE_UART_RTS 42 | bi_decl(bi_1pin_with_name(PROBE_UART_RTS, "PROBE UART RTS")); 43 | #endif 44 | #ifdef PROBE_UART_DTR 45 | bi_decl(bi_1pin_with_name(PROBE_UART_DTR, "PROBE UART DTR")); 46 | #endif 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/probe_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef PROBE_CONFIG_H_ 27 | #define PROBE_CONFIG_H_ 28 | 29 | #include "FreeRTOS.h" 30 | #include "task.h" 31 | 32 | #if false 33 | #define probe_info(format,args...) \ 34 | do { \ 35 | vTaskSuspendAll(); \ 36 | printf(format, ## args); \ 37 | xTaskResumeAll(); \ 38 | } while (0) 39 | #else 40 | #define probe_info(format,...) ((void)0) 41 | #endif 42 | 43 | 44 | #if false 45 | #define probe_debug(format,args...) \ 46 | do { \ 47 | vTaskSuspendAll(); \ 48 | printf(format, ## args); \ 49 | xTaskResumeAll(); \ 50 | } while (0) 51 | #else 52 | #define probe_debug(format,...) ((void)0) 53 | #endif 54 | 55 | #if false 56 | #define probe_dump(format,args...)\ 57 | do { \ 58 | vTaskSuspendAll(); \ 59 | printf(format, ## args); \ 60 | xTaskResumeAll(); \ 61 | } while (0) 62 | #else 63 | #define probe_dump(format,...) ((void)0) 64 | #endif 65 | 66 | // TODO tie this up with PICO_BOARD defines in the main SDK 67 | 68 | #ifdef DEBUG_ON_PICO 69 | #include "board_pico_config.h" 70 | #else 71 | #include "board_debug_probe_config.h" 72 | #endif 73 | //#include "board_example_config.h" 74 | 75 | // Add the configuration to binary information 76 | void bi_decl_config(); 77 | 78 | #define PROTO_DAP_V1 1 79 | #define PROTO_DAP_V2 2 80 | 81 | // Interface config 82 | #ifndef PROBE_DEBUG_PROTOCOL 83 | #define PROBE_DEBUG_PROTOCOL PROTO_DAP_V2 84 | #endif 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/probe_oen.pio: -------------------------------------------------------------------------------- 1 | ; SPDX-License-Identifier: MIT 2 | ; Copyright (c) 2023 Raspberry Pi Ltd 3 | 4 | ; Output-enable active-low variant of the SWD probe 5 | 6 | ; This program is very similar to the one in probe.pio. The only difference is 7 | ; that here write_cmd and turnaround_cmd are split into two separate routines, 8 | ; whose difference is OEn being high/low. 9 | 10 | ; SWDIO_OEn is pin 0, SWCLK pin 1, SWDIO (out) pin 2, SWDI (in) pin 3. 11 | ; Pin 0 and 1 are sideset pins 12 | 13 | .program probe 14 | .side_set 2 opt 15 | 16 | public turnaround_cmd: 17 | pull 18 | turnaround_bitloop: 19 | nop [1] side 0x1 20 | jmp x-- turnaround_bitloop [1] side 0x3 21 | jmp get_next_cmd 22 | 23 | public write_cmd: 24 | pull 25 | write_bitloop: 26 | out pins, 1 [1] side 0x0 ; Data is output by host on negedge 27 | jmp x-- write_bitloop [1] side 0x2 ; ...and captured by target on posedge 28 | ; Fall through to next command 29 | .wrap_target 30 | public get_next_cmd: 31 | pull side 0x1 ; SWCLK initially low, OEn disabled 32 | out x, 8 ; Get bit count 33 | out pindirs, 1 ; Set SWDIO direction 34 | out pc, 5 ; Go to command routine 35 | 36 | read_bitloop: 37 | nop ; Additional delay on taken loop branch 38 | public read_cmd: 39 | in pins, 1 [1] side 0x3 ; Data is captured by host on posedge 40 | jmp x-- read_bitloop side 0x1 41 | push 42 | .wrap ; Wrap to next command 43 | 44 | 45 | ; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc 46 | % c-sdk { 47 | 48 | static inline void probe_gpio_init() 49 | { 50 | #if defined(PROBE_PIN_RESET) 51 | // Target reset pin: pull up, input to emulate open drain pin 52 | gpio_pull_up(PROBE_PIN_RESET); 53 | // gpio_init will leave the pin cleared and set as input 54 | gpio_init(PROBE_PIN_RESET); 55 | #endif 56 | // Funcsel pins 57 | pio_gpio_init(pio0, PROBE_PIN_SWDIOEN); 58 | pio_gpio_init(pio0, PROBE_PIN_SWCLK); 59 | pio_gpio_init(pio0, PROBE_PIN_SWDIO); 60 | 61 | // Make sure SWDIO has a pullup on it. Idle state is high 62 | gpio_pull_up(PROBE_PIN_SWDIO); 63 | gpio_pull_up(PROBE_PIN_SWDIOEN); 64 | } 65 | 66 | // DAP interface says all pins have to be High-Z when disabled 67 | static inline void probe_gpio_deinit() 68 | { 69 | #if defined(PROBE_PIN_RESET) 70 | gpio_deinit(PROBE_PIN_RESET); 71 | gpio_disable_pulls(PROBE_PIN_RESET); 72 | #endif 73 | gpio_deinit(PROBE_PIN_SWCLK); 74 | gpio_disable_pulls(PROBE_PIN_SWCLK); 75 | // Note for SWDIOEN - make sure the driver output enable is removed before removing drive - leave pull-up enabled 76 | gpio_deinit(PROBE_PIN_SWDIOEN); 77 | gpio_deinit(PROBE_PIN_SWDIO); 78 | gpio_disable_pulls(PROBE_PIN_SWDIO); 79 | } 80 | 81 | static inline void probe_sm_init(pio_sm_config* sm_config) { 82 | 83 | // Set SWDIOEN and SWCLK as sideset pins 84 | sm_config_set_sideset_pins(sm_config, PROBE_PIN_SWDIOEN); 85 | 86 | // Set SWDIO offset 87 | sm_config_set_out_pins(sm_config, PROBE_PIN_SWDIO, 1); 88 | sm_config_set_set_pins(sm_config, PROBE_PIN_SWDIO, 1); 89 | sm_config_set_in_pins(sm_config, PROBE_PIN_SWDI); 90 | 91 | // Set SWDIOEN, SWD and SWDIO pins as output to start. This will be set in the sm 92 | pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 3, true); 93 | 94 | // shift output right, autopull off, autopull threshold 95 | sm_config_set_out_shift(sm_config, true, false, 0); 96 | // shift input right as swd data is lsb first, autopush off 97 | sm_config_set_in_shift(sm_config, true, false, 0); 98 | } 99 | 100 | %} 101 | -------------------------------------------------------------------------------- /src/sw_dp_pio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2022 ARM Limited. All rights reserved. 3 | * Copyright (c) 2022 Raspberry Pi Ltd 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the License); you may 8 | * not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 15 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | /* 21 | * This is a shim between the SW_DP functions and the PIO 22 | * implementation used for Debugprobe. Instead of calling bitbash functions, 23 | * hand off the bit sequences to a SM for asynchronous completion. 24 | */ 25 | 26 | #include 27 | 28 | #include "DAP_config.h" 29 | #include "DAP.h" 30 | #include "probe.h" 31 | 32 | /* Slight hack - we're not bitbashing so we need to set baudrate off the DAP's delay cycles. 33 | * Ideally we don't want calls to udiv everywhere... */ 34 | #define MAKE_KHZ(x) (CPU_CLOCK / (2000 * ((x) + 1))) 35 | volatile uint32_t cached_delay = 0; 36 | 37 | // Generate SWJ Sequence 38 | // count: sequence bit count 39 | // data: pointer to sequence bit data 40 | // return: none 41 | #if ((DAP_SWD != 0) || (DAP_JTAG != 0)) 42 | void SWJ_Sequence (uint32_t count, const uint8_t *data) { 43 | uint32_t bits; 44 | uint32_t n; 45 | 46 | if (DAP_Data.clock_delay != cached_delay) { 47 | probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay)); 48 | cached_delay = DAP_Data.clock_delay; 49 | } 50 | probe_debug("SWJ sequence count = %d FDB=0x%2x\n", count, data[0]); 51 | n = count; 52 | while (n > 0) { 53 | if (n > 8) 54 | bits = 8; 55 | else 56 | bits = n; 57 | probe_write_bits(bits, *data++); 58 | n -= bits; 59 | } 60 | } 61 | #endif 62 | 63 | // Generate SWD Sequence 64 | // info: sequence information 65 | // swdo: pointer to SWDIO generated data 66 | // swdi: pointer to SWDIO captured data 67 | // return: none 68 | #if (DAP_SWD != 0) 69 | void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) { 70 | uint32_t bits; 71 | uint32_t n; 72 | 73 | if (DAP_Data.clock_delay != cached_delay) { 74 | probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay)); 75 | cached_delay = DAP_Data.clock_delay; 76 | } 77 | probe_debug("SWD sequence\n"); 78 | n = info & SWD_SEQUENCE_CLK; 79 | if (n == 0U) { 80 | n = 64U; 81 | } 82 | bits = n; 83 | if (info & SWD_SEQUENCE_DIN) { 84 | while (n > 0) { 85 | if (n > 8) 86 | bits = 8; 87 | else 88 | bits = n; 89 | *swdi++ = probe_read_bits(bits); 90 | n -= bits; 91 | } 92 | } else { 93 | while (n > 0) { 94 | if (n > 8) 95 | bits = 8; 96 | else 97 | bits = n; 98 | probe_write_bits(bits, *swdo++); 99 | n -= bits; 100 | } 101 | } 102 | } 103 | #endif 104 | 105 | #if (DAP_SWD != 0) 106 | // SWD Transfer I/O 107 | // request: A[3:2] RnW APnDP 108 | // data: DATA[31:0] 109 | // return: ACK[2:0] 110 | uint8_t SWD_Transfer (uint32_t request, uint32_t *data) { 111 | uint8_t prq = 0; 112 | uint8_t ack; 113 | uint8_t bit; 114 | uint32_t val = 0; 115 | uint32_t parity = 0; 116 | uint32_t n; 117 | 118 | if (DAP_Data.clock_delay != cached_delay) { 119 | probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay)); 120 | cached_delay = DAP_Data.clock_delay; 121 | } 122 | probe_debug("SWD_transfer\n"); 123 | /* Generate the request packet */ 124 | prq |= (1 << 0); /* Start Bit */ 125 | for (n = 1; n < 5; n++) { 126 | bit = (request >> (n - 1)) & 0x1; 127 | prq |= bit << n; 128 | parity += bit; 129 | } 130 | prq |= (parity & 0x1) << 5; /* Parity Bit */ 131 | prq |= (0 << 6); /* Stop Bit */ 132 | prq |= (1 << 7); /* Park bit */ 133 | probe_write_bits(8, prq); 134 | 135 | /* Turnaround (ignore read bits) */ 136 | ack = probe_read_bits(DAP_Data.swd_conf.turnaround + 3); 137 | ack >>= DAP_Data.swd_conf.turnaround; 138 | 139 | if (ack == DAP_TRANSFER_OK) { 140 | /* Data transfer phase */ 141 | if (request & DAP_TRANSFER_RnW) { 142 | /* Read RDATA[0:31] - note probe_read shifts to LSBs */ 143 | val = probe_read_bits(32); 144 | bit = probe_read_bits(1); 145 | parity = __builtin_popcount(val); 146 | if ((parity ^ bit) & 1U) { 147 | /* Parity error */ 148 | ack = DAP_TRANSFER_ERROR; 149 | } 150 | if (data) 151 | *data = val; 152 | probe_debug("Read %02x ack %02x 0x%08x parity %01x\n", 153 | prq, ack, val, bit); 154 | /* Turnaround for line idle */ 155 | probe_hiz_clocks(DAP_Data.swd_conf.turnaround); 156 | } else { 157 | /* Turnaround for write */ 158 | probe_hiz_clocks(DAP_Data.swd_conf.turnaround); 159 | 160 | /* Write WDATA[0:31] */ 161 | val = *data; 162 | probe_write_bits(32, val); 163 | parity = __builtin_popcount(val); 164 | /* Write Parity Bit */ 165 | probe_write_bits(1, parity & 0x1); 166 | probe_debug("write %02x ack %02x 0x%08x parity %01x\n", 167 | prq, ack, val, parity); 168 | } 169 | /* Capture Timestamp */ 170 | if (request & DAP_TRANSFER_TIMESTAMP) { 171 | DAP_Data.timestamp = time_us_32(); 172 | } 173 | 174 | /* Idle cycles - drive 0 for N clocks */ 175 | if (DAP_Data.transfer.idle_cycles) { 176 | for (n = DAP_Data.transfer.idle_cycles; n; ) { 177 | if (n > 256) { 178 | probe_write_bits(256, 0); 179 | n -= 256; 180 | } else { 181 | probe_write_bits(n, 0); 182 | n -= n; 183 | } 184 | } 185 | } 186 | return ((uint8_t)ack); 187 | } 188 | 189 | if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) { 190 | if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) { 191 | /* Dummy Read RDATA[0:31] + Parity */ 192 | probe_read_bits(33); 193 | } 194 | probe_hiz_clocks(DAP_Data.swd_conf.turnaround); 195 | if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) { 196 | /* Dummy Write WDATA[0:31] + Parity */ 197 | probe_write_bits(32, 0); 198 | probe_write_bits(1, 0); 199 | } 200 | return ((uint8_t)ack); 201 | } 202 | 203 | /* Protocol error */ 204 | n = DAP_Data.swd_conf.turnaround + 32U + 1U; 205 | /* Back off data phase */ 206 | probe_read_bits(n); 207 | return ((uint8_t)ack); 208 | } 209 | 210 | #endif /* (DAP_SWD != 0) */ 211 | -------------------------------------------------------------------------------- /src/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // COMMON CONFIGURATION 35 | //-------------------------------------------------------------------- 36 | 37 | // defined by compiler flags for flexibility 38 | #ifndef CFG_TUSB_MCU 39 | #error CFG_TUSB_MCU must be defined 40 | #endif 41 | 42 | #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE 43 | 44 | #ifndef CFG_TUSB_OS 45 | #define CFG_TUSB_OS OPT_OS_PICO 46 | #endif 47 | 48 | #ifndef CFG_TUSB_MEM_SECTION 49 | #define CFG_TUSB_MEM_SECTION 50 | #endif 51 | 52 | #ifndef CFG_TUSB_MEM_ALIGN 53 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 54 | #endif 55 | 56 | //-------------------------------------------------------------------- 57 | // DEVICE CONFIGURATION 58 | //-------------------------------------------------------------------- 59 | 60 | #ifndef CFG_TUD_ENDPOINT0_SIZE 61 | #define CFG_TUD_ENDPOINT0_SIZE 64 62 | #endif 63 | 64 | //------------- CLASS -------------// 65 | #define CFG_TUD_HID 1 66 | #define CFG_TUD_CDC 1 67 | #define CFG_TUD_MSC 0 68 | #define CFG_TUD_MIDI 0 69 | #define CFG_TUD_VENDOR 1 70 | 71 | /* 72 | * TX bufsize (actually UART RX) is oversized because the Windows CDC-ACM 73 | * driver submits a grand total of _one_ URB at any one time. 74 | * This means the application must consume the data before the next IN token 75 | * is issued. At high datarates this leads to huge variation in instantaneous 76 | * throughput on USB, so a large runway is needed. 77 | */ 78 | #define CFG_TUD_CDC_RX_BUFSIZE 64 79 | #define CFG_TUD_CDC_TX_BUFSIZE 4096 80 | 81 | #define CFG_TUD_VENDOR_RX_BUFSIZE 8192 82 | #define CFG_TUD_VENDOR_TX_BUFSIZE 8192 83 | 84 | #ifndef TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX 85 | #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX 1 86 | #endif 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif /* _TUSB_CONFIG_H_ */ 93 | -------------------------------------------------------------------------------- /src/tusb_edpt_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "tusb_edpt_handler.h" 8 | #include "DAP.h" 9 | 10 | static uint8_t itf_num; 11 | static uint8_t _rhport; 12 | 13 | volatile uint32_t _resp_len; 14 | 15 | static uint8_t _out_ep_addr; 16 | static uint8_t _in_ep_addr; 17 | 18 | static buffer_t USBRequestBuffer; 19 | static buffer_t USBResponseBuffer; 20 | 21 | static uint8_t DAPRequestBuffer[DAP_PACKET_SIZE]; 22 | static uint8_t DAPResponseBuffer[DAP_PACKET_SIZE]; 23 | 24 | #define WR_IDX(x) (x.wptr % DAP_PACKET_COUNT) 25 | #define RD_IDX(x) (x.rptr % DAP_PACKET_COUNT) 26 | 27 | #define WR_SLOT_PTR(x) &(x.data[WR_IDX(x)][0]) 28 | #define RD_SLOT_PTR(x) &(x.data[RD_IDX(x)][0]) 29 | 30 | bool buffer_full(buffer_t *buffer) 31 | { 32 | return ((buffer->wptr + 1) % DAP_PACKET_COUNT == buffer->rptr); 33 | } 34 | 35 | bool buffer_empty(buffer_t *buffer) 36 | { 37 | return (buffer->wptr == buffer->rptr); 38 | } 39 | 40 | void dap_edpt_init(void) { 41 | 42 | } 43 | 44 | bool dap_edpt_deinit(void) 45 | { 46 | memset(DAPRequestBuffer, 0, sizeof(DAPRequestBuffer)); 47 | memset(DAPResponseBuffer, 0, sizeof(DAPResponseBuffer)); 48 | USBRequestBuffer.wptr = USBRequestBuffer.rptr = 0; 49 | USBResponseBuffer.wptr = USBResponseBuffer.rptr = 0; 50 | return true; 51 | } 52 | 53 | void dap_edpt_reset(uint8_t __unused rhport) 54 | { 55 | itf_num = 0; 56 | } 57 | 58 | char * dap_cmd_string[] = { 59 | [ID_DAP_Info ] = "DAP_Info", 60 | [ID_DAP_HostStatus ] = "DAP_HostStatus", 61 | [ID_DAP_Connect ] = "DAP_Connect", 62 | [ID_DAP_Disconnect ] = "DAP_Disconnect", 63 | [ID_DAP_TransferConfigure ] = "DAP_TransferConfigure", 64 | [ID_DAP_Transfer ] = "DAP_Transfer", 65 | [ID_DAP_TransferBlock ] = "DAP_TransferBlock", 66 | [ID_DAP_TransferAbort ] = "DAP_TransferAbort", 67 | [ID_DAP_WriteABORT ] = "DAP_WriteABORT", 68 | [ID_DAP_Delay ] = "DAP_Delay", 69 | [ID_DAP_ResetTarget ] = "DAP_ResetTarget", 70 | [ID_DAP_SWJ_Pins ] = "DAP_SWJ_Pins", 71 | [ID_DAP_SWJ_Clock ] = "DAP_SWJ_Clock", 72 | [ID_DAP_SWJ_Sequence ] = "DAP_SWJ_Sequence", 73 | [ID_DAP_SWD_Configure ] = "DAP_SWD_Configure", 74 | [ID_DAP_SWD_Sequence ] = "DAP_SWD_Sequence", 75 | [ID_DAP_JTAG_Sequence ] = "DAP_JTAG_Sequence", 76 | [ID_DAP_JTAG_Configure ] = "DAP_JTAG_Configure", 77 | [ID_DAP_JTAG_IDCODE ] = "DAP_JTAG_IDCODE", 78 | [ID_DAP_SWO_Transport ] = "DAP_SWO_Transport", 79 | [ID_DAP_SWO_Mode ] = "DAP_SWO_Mode", 80 | [ID_DAP_SWO_Baudrate ] = "DAP_SWO_Baudrate", 81 | [ID_DAP_SWO_Control ] = "DAP_SWO_Control", 82 | [ID_DAP_SWO_Status ] = "DAP_SWO_Status", 83 | [ID_DAP_SWO_ExtendedStatus ] = "DAP_SWO_ExtendedStatus", 84 | [ID_DAP_SWO_Data ] = "DAP_SWO_Data", 85 | [ID_DAP_QueueCommands ] = "DAP_QueueCommands", 86 | [ID_DAP_ExecuteCommands ] = "DAP_ExecuteCommands", 87 | }; 88 | 89 | 90 | uint16_t dap_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) 91 | { 92 | 93 | TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass && 94 | DAP_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass && 95 | DAP_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0); 96 | 97 | // Initialise circular buffer indices 98 | USBResponseBuffer.wptr = 0; 99 | USBResponseBuffer.rptr = 0; 100 | USBRequestBuffer.wptr = 0; 101 | USBRequestBuffer.rptr = 0; 102 | 103 | // Initialse full/empty flags 104 | USBResponseBuffer.wasFull = false; 105 | USBResponseBuffer.wasEmpty = true; 106 | USBRequestBuffer.wasFull = false; 107 | USBRequestBuffer.wasEmpty = true; 108 | 109 | uint16_t const drv_len = sizeof(tusb_desc_interface_t) + (itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); 110 | TU_VERIFY(max_len >= drv_len, 0); 111 | itf_num = itf_desc->bInterfaceNumber; 112 | 113 | // Initialising the OUT endpoint 114 | 115 | tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1); 116 | uint8_t ep_addr = edpt_desc->bEndpointAddress; 117 | 118 | _out_ep_addr = ep_addr; 119 | 120 | // The OUT endpoint requires a call to usbd_edpt_xfer to initialise the endpoint, giving tinyUSB a buffer to consume when a transfer occurs at the endpoint 121 | usbd_edpt_open(rhport, edpt_desc); 122 | usbd_edpt_xfer(rhport, ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE); 123 | 124 | // Initiliasing the IN endpoint 125 | 126 | edpt_desc++; 127 | ep_addr = edpt_desc->bEndpointAddress; 128 | 129 | _in_ep_addr = ep_addr; 130 | 131 | // The IN endpoint doesn't need a transfer to initialise it, as this will be done by the main loop of dap_thread 132 | usbd_edpt_open(rhport, edpt_desc); 133 | 134 | return drv_len; 135 | 136 | } 137 | 138 | bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request) 139 | { 140 | return false; 141 | } 142 | 143 | // Manage USBResponseBuffer (request) write and USBRequestBuffer (response) read indices 144 | bool dap_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) 145 | { 146 | const uint8_t ep_dir = tu_edpt_dir(ep_addr); 147 | 148 | if(ep_dir == TUSB_DIR_IN) 149 | { 150 | if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE) 151 | { 152 | USBResponseBuffer.rptr++; 153 | 154 | // This checks that the buffer was not empty in DAP thread, which means the next buffer was not queued up for the in endpoint callback 155 | // So, queue up the buffer at the new read index, since we expect read to catch up to write at this point. 156 | // It is possible for the read index to be multiple spaces behind the write index (if the USB callbacks are lagging behind dap thread), 157 | // so we account for this by only setting wasEmpty to true if the next callback will empty the buffer 158 | if(!USBResponseBuffer.wasEmpty) 159 | { 160 | usbd_edpt_xfer(rhport, ep_addr, RD_SLOT_PTR(USBResponseBuffer), (uint16_t) _resp_len); 161 | USBResponseBuffer.wasEmpty = (USBResponseBuffer.rptr + 1) == USBResponseBuffer.wptr; 162 | } 163 | 164 | // Wake up DAP thread after processing the callback 165 | vTaskResume(dap_taskhandle); 166 | return true; 167 | } 168 | 169 | return false; 170 | 171 | } else if(ep_dir == TUSB_DIR_OUT) { 172 | 173 | if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE) 174 | { 175 | // Only queue the next buffer in the out callback if the buffer is not full 176 | // If full, we set the wasFull flag, which will be checked by dap thread 177 | if(!buffer_full(&USBRequestBuffer)) 178 | { 179 | USBRequestBuffer.wptr++; 180 | usbd_edpt_xfer(rhport, ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE); 181 | USBRequestBuffer.wasFull = false; 182 | } 183 | else { 184 | USBRequestBuffer.wasFull = true; 185 | } 186 | 187 | // Wake up DAP thread after processing the callback 188 | vTaskResume(dap_taskhandle); 189 | return true; 190 | } 191 | 192 | return false; 193 | } 194 | else return false; 195 | } 196 | 197 | void dap_thread(void *ptr) 198 | { 199 | uint32_t n; 200 | do 201 | { 202 | while(USBRequestBuffer.rptr != USBRequestBuffer.wptr) 203 | { 204 | /* 205 | * Atomic command support - buffer QueueCommands, but don't process them 206 | * until a non-QueueCommands packet is seen. 207 | */ 208 | n = USBRequestBuffer.rptr; 209 | while (USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] == ID_DAP_QueueCommands) { 210 | probe_info("%lu %lu DAP queued cmd %s len %02x\n", 211 | USBRequestBuffer.wptr, USBRequestBuffer.rptr, 212 | dap_cmd_string[USBRequestBuffer.data[n % DAP_PACKET_COUNT][0]], USBRequestBuffer.data[n % DAP_PACKET_COUNT][1]); 213 | USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] = ID_DAP_ExecuteCommands; 214 | n++; 215 | while (n == USBRequestBuffer.wptr) { 216 | /* Need yield in a loop here, as IN callbacks will also wake the thread */ 217 | probe_info("DAP wait\n"); 218 | vTaskSuspend(dap_taskhandle); 219 | } 220 | } 221 | // Read a single packet from the USB buffer into the DAP Request buffer 222 | memcpy(DAPRequestBuffer, RD_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE); 223 | probe_info("%lu %lu DAP cmd %s len %02x\n", 224 | USBRequestBuffer.wptr, USBRequestBuffer.rptr, 225 | dap_cmd_string[DAPRequestBuffer[0]], DAPRequestBuffer[1]); 226 | USBRequestBuffer.rptr++; 227 | 228 | // If the buffer was full in the out callback, we need to queue up another buffer for the endpoint to consume, now that we know there is space in the buffer. 229 | if(USBRequestBuffer.wasFull) 230 | { 231 | vTaskSuspendAll(); // Suspend the scheduler to safely update the write index 232 | USBRequestBuffer.wptr++; 233 | usbd_edpt_xfer(_rhport, _out_ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE); 234 | USBRequestBuffer.wasFull = false; 235 | xTaskResumeAll(); 236 | } 237 | 238 | _resp_len = DAP_ExecuteCommand(DAPRequestBuffer, DAPResponseBuffer); 239 | probe_info("%lu %lu DAP resp %s\n", 240 | USBResponseBuffer.wptr, USBResponseBuffer.rptr, 241 | dap_cmd_string[DAPResponseBuffer[0]]); 242 | 243 | 244 | // Suspend the scheduler to avoid stale values/race conditions between threads 245 | vTaskSuspendAll(); 246 | 247 | if(buffer_empty(&USBResponseBuffer)) 248 | { 249 | memcpy(WR_SLOT_PTR(USBResponseBuffer), DAPResponseBuffer, (uint16_t) _resp_len); 250 | USBResponseBuffer.wptr++; 251 | 252 | usbd_edpt_xfer(_rhport, _in_ep_addr, RD_SLOT_PTR(USBResponseBuffer), (uint16_t) _resp_len); 253 | } else { 254 | 255 | memcpy(WR_SLOT_PTR(USBResponseBuffer), DAPResponseBuffer, (uint16_t) _resp_len); 256 | USBResponseBuffer.wptr++; 257 | 258 | // The In callback needs to check this flag to know when to queue up the next buffer. 259 | USBResponseBuffer.wasEmpty = false; 260 | } 261 | xTaskResumeAll(); 262 | } 263 | 264 | // Suspend DAP thread until it is awoken by a USB thread callback 265 | vTaskSuspend(dap_taskhandle); 266 | 267 | } while (1); 268 | 269 | } 270 | 271 | usbd_class_driver_t const _dap_edpt_driver = 272 | { 273 | .init = dap_edpt_init, 274 | .deinit = dap_edpt_deinit, 275 | .reset = dap_edpt_reset, 276 | .open = dap_edpt_open, 277 | .control_xfer_cb = dap_edpt_control_xfer_cb, 278 | .xfer_cb = dap_edpt_xfer_cb, 279 | .sof = NULL, 280 | #if CFG_TUSB_DEBUG >= 2 281 | .name = "DAP ENDPOINT" 282 | #endif 283 | }; 284 | 285 | // Add the custom driver to the tinyUSB stack 286 | usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) 287 | { 288 | *driver_count = 1; 289 | return &_dap_edpt_driver; 290 | } 291 | 292 | -------------------------------------------------------------------------------- /src/tusb_edpt_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef TUSB_EDPT_HANDLER_H 8 | #define TUSB_EDPT_HANDLER_H 9 | 10 | #include "tusb.h" 11 | 12 | #include "device/usbd_pvt.h" 13 | #include "DAP_config.h" 14 | 15 | #define DAP_INTERFACE_SUBCLASS 0x00 16 | #define DAP_INTERFACE_PROTOCOL 0x00 17 | 18 | typedef struct { 19 | uint8_t data[DAP_PACKET_COUNT][DAP_PACKET_SIZE]; 20 | volatile uint32_t wptr; 21 | volatile uint32_t rptr; 22 | volatile bool wasEmpty; 23 | volatile bool wasFull; 24 | } buffer_t; 25 | 26 | extern TaskHandle_t dap_taskhandle, tud_taskhandle; 27 | 28 | /* Main DAP loop */ 29 | void dap_thread(void *ptr); 30 | 31 | /* Endpoint Handling */ 32 | void dap_edpt_init(void); 33 | uint16_t dap_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len); 34 | bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request); 35 | bool dap_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); 36 | 37 | /* Helper Functions */ 38 | bool buffer_full(buffer_t *buffer); 39 | bool buffer_empty(buffer_t *buffer); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * Copyright (c) 2021 Peter Lawrence 6 | * Copyright (c) 2022 Raspberry Pi Ltd 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | * 26 | */ 27 | 28 | #include "tusb.h" 29 | #include "get_serial.h" 30 | #include "probe_config.h" 31 | 32 | //--------------------------------------------------------------------+ 33 | // Device Descriptors 34 | //--------------------------------------------------------------------+ 35 | tusb_desc_device_t const desc_device = 36 | { 37 | .bLength = sizeof(tusb_desc_device_t), 38 | .bDescriptorType = TUSB_DESC_DEVICE, 39 | #if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2) 40 | .bcdUSB = 0x0210, // USB Specification version 2.1 for BOS 41 | #else 42 | .bcdUSB = 0x0110, 43 | #endif 44 | .bDeviceClass = 0x00, // Each interface specifies its own 45 | .bDeviceSubClass = 0x00, // Each interface specifies its own 46 | .bDeviceProtocol = 0x00, 47 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 48 | 49 | .idVendor = 0x2E8A, // Pi 50 | .idProduct = 0x000c, // CMSIS-DAP Debug Probe 51 | .bcdDevice = 0x0222, // Version 02.22 52 | .iManufacturer = 0x01, 53 | .iProduct = 0x02, 54 | .iSerialNumber = 0x03, 55 | .bNumConfigurations = 0x01 56 | }; 57 | 58 | // Invoked when received GET DEVICE DESCRIPTOR 59 | // Application return pointer to descriptor 60 | uint8_t const * tud_descriptor_device_cb(void) 61 | { 62 | return (uint8_t const *) &desc_device; 63 | } 64 | 65 | //--------------------------------------------------------------------+ 66 | // Configuration Descriptor 67 | //--------------------------------------------------------------------+ 68 | 69 | enum 70 | { 71 | ITF_NUM_PROBE, // Old versions of Keil MDK only look at interface 0 72 | ITF_NUM_CDC_COM, 73 | ITF_NUM_CDC_DATA, 74 | ITF_NUM_TOTAL 75 | }; 76 | 77 | #define CDC_NOTIFICATION_EP_NUM 0x81 78 | #define CDC_DATA_OUT_EP_NUM 0x02 79 | #define CDC_DATA_IN_EP_NUM 0x83 80 | #define DAP_OUT_EP_NUM 0x04 81 | #define DAP_IN_EP_NUM 0x85 82 | 83 | #if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V1) 84 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN) 85 | #else 86 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN) 87 | #endif 88 | 89 | static uint8_t const desc_hid_report[] = 90 | { 91 | TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE) 92 | }; 93 | 94 | uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) 95 | { 96 | (void) itf; 97 | return desc_hid_report; 98 | } 99 | 100 | uint8_t desc_configuration[] = 101 | { 102 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100), 103 | // Interface 0 104 | #if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V1) 105 | // HID (named interface) 106 | TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_PROBE, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), DAP_OUT_EP_NUM, DAP_IN_EP_NUM, CFG_TUD_HID_EP_BUFSIZE, 1), 107 | #elif (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2) 108 | // Bulk (named interface) 109 | TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 5, DAP_OUT_EP_NUM, DAP_IN_EP_NUM, 64), 110 | #elif (PROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM) 111 | // Bulk 112 | TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, DAP_OUT_EP_NUM, DAP_IN_EP_NUM, 64), 113 | #endif 114 | // Interface 1 + 2 115 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 6, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64), 116 | }; 117 | 118 | // Invoked when received GET CONFIGURATION DESCRIPTOR 119 | // Application return pointer to descriptor 120 | // Descriptor contents must exist long enough for transfer to complete 121 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 122 | { 123 | (void) index; // for multiple configurations 124 | /* Hack in CAP_BREAK support */ 125 | desc_configuration[CONFIG_TOTAL_LEN - TUD_CDC_DESC_LEN + 8 + 9 + 5 + 5 + 4 - 1] = 0x6; 126 | return desc_configuration; 127 | } 128 | 129 | //--------------------------------------------------------------------+ 130 | // String Descriptors 131 | //--------------------------------------------------------------------+ 132 | 133 | // array of pointer to string descriptors 134 | char const* string_desc_arr [] = 135 | { 136 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 137 | "Raspberry Pi", // 1: Manufacturer 138 | PROBE_PRODUCT_STRING, // 2: Product 139 | usb_serial, // 3: Serial, uses flash unique ID 140 | "CMSIS-DAP v1 Interface", // 4: Interface descriptor for HID transport 141 | "CMSIS-DAP v2 Interface", // 5: Interface descriptor for Bulk transport 142 | "CDC-ACM UART Interface", // 6: Interface descriptor for CDC 143 | }; 144 | 145 | static uint16_t _desc_str[32]; 146 | 147 | // Invoked when received GET STRING DESCRIPTOR request 148 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 149 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 150 | { 151 | (void) langid; 152 | 153 | uint8_t chr_count; 154 | 155 | if ( index == 0) 156 | { 157 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 158 | chr_count = 1; 159 | }else 160 | { 161 | // Convert ASCII string into UTF-16 162 | 163 | if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; 164 | 165 | const char* str = string_desc_arr[index]; 166 | 167 | // Cap at max char 168 | chr_count = strlen(str); 169 | if ( chr_count > 31 ) chr_count = 31; 170 | 171 | for(uint8_t i=0; i