Compare commits
40 Commits
line_codin
...
debugprobe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53875ec320 | ||
|
|
9226fac61a | ||
|
|
06c7792560 | ||
|
|
1752f2a61b | ||
|
|
e887004fdf | ||
|
|
df68d70400 | ||
|
|
0c84dd0dc1 | ||
|
|
4238b780c2 | ||
|
|
189e367332 | ||
|
|
d9c507f579 | ||
|
|
6d2f83e30c | ||
|
|
1182803822 | ||
|
|
65b1e73589 | ||
|
|
152f85d2ec | ||
|
|
d85fd1f58d | ||
|
|
8f5d6fcc9f | ||
|
|
9aed4ca509 | ||
|
|
e4585d551a | ||
|
|
46b9613d14 | ||
|
|
2bbe900d68 | ||
|
|
7410b45e1d | ||
|
|
920f717e71 | ||
|
|
34a4ba0e01 | ||
|
|
0476e9cae5 | ||
|
|
c0ff91421a | ||
|
|
b5962e0818 | ||
|
|
ddc028fe18 | ||
|
|
af2540b045 | ||
|
|
d5047e7ef8 | ||
|
|
a7aa0766f0 | ||
|
|
b09854c639 | ||
|
|
327e15f176 | ||
|
|
62f4a31335 | ||
|
|
abf675ca8c | ||
|
|
d0c03d2564 | ||
|
|
1267a8c367 | ||
|
|
bdb1bf287d | ||
|
|
d9a975b24e | ||
|
|
721b69cf5c | ||
|
|
2658c2c997 |
@@ -5,12 +5,16 @@ include(pico_sdk_import.cmake)
|
||||
set(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/freertos)
|
||||
include(FreeRTOS_Kernel_import.cmake)
|
||||
|
||||
project(picoprobe)
|
||||
project(debugprobe)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(picoprobe
|
||||
src/led.c
|
||||
if (${PICO_SDK_VERSION_MAJOR} LESS 2)
|
||||
message(SEND_ERROR "Version 2 of the Pico SDK is required to compile this project. Please update your installation at ${PICO_SDK_PATH}")
|
||||
endif ()
|
||||
|
||||
add_executable(debugprobe
|
||||
src/probe_config.c
|
||||
src/main.c
|
||||
src/usb_descriptors.c
|
||||
src/probe.c
|
||||
@@ -20,7 +24,7 @@ add_executable(picoprobe
|
||||
src/tusb_edpt_handler.c
|
||||
)
|
||||
|
||||
target_sources(picoprobe PRIVATE
|
||||
target_sources(debugprobe PRIVATE
|
||||
CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c
|
||||
CMSIS_5/CMSIS/DAP/Firmware/Source/JTAG_DP.c
|
||||
CMSIS_5/CMSIS/DAP/Firmware/Source/DAP_vendor.c
|
||||
@@ -28,35 +32,43 @@ target_sources(picoprobe PRIVATE
|
||||
#CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c
|
||||
)
|
||||
|
||||
target_include_directories(picoprobe PRIVATE
|
||||
target_include_directories(debugprobe PRIVATE
|
||||
CMSIS_5/CMSIS/DAP/Firmware/Include/
|
||||
CMSIS_5/CMSIS/Core/Include/
|
||||
include/
|
||||
)
|
||||
|
||||
target_compile_options(picoprobe PRIVATE -Wall)
|
||||
target_compile_options(debugprobe PRIVATE -Wall)
|
||||
|
||||
pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio)
|
||||
pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe_oen.pio)
|
||||
pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio)
|
||||
pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe_oen.pio)
|
||||
|
||||
target_include_directories(picoprobe PRIVATE src)
|
||||
target_include_directories(debugprobe PRIVATE src)
|
||||
|
||||
target_compile_definitions (picoprobe PRIVATE
|
||||
target_compile_definitions (debugprobe PRIVATE
|
||||
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
|
||||
)
|
||||
|
||||
option (DEBUGPROBE "compile for the debugprobe" OFF)
|
||||
if (DEBUGPROBE)
|
||||
target_compile_definitions (picoprobe PRIVATE
|
||||
DEBUGPROBE=1
|
||||
)
|
||||
set_target_properties(picoprobe PROPERTIES
|
||||
OUTPUT_NAME "debugprobe"
|
||||
option (DEBUG_ON_PICO "Compile firmware for the Pico instead of Debug Probe" OFF)
|
||||
if (DEBUG_ON_PICO)
|
||||
target_compile_definitions (debugprobe PRIVATE
|
||||
DEBUG_ON_PICO=1
|
||||
)
|
||||
if (PICO_BOARD STREQUAL "pico")
|
||||
set_target_properties(debugprobe PROPERTIES
|
||||
OUTPUT_NAME "debugprobe_on_pico"
|
||||
)
|
||||
elseif (PICO_BOARD STREQUAL "pico2")
|
||||
set_target_properties(debugprobe PROPERTIES
|
||||
OUTPUT_NAME "debugprobe_on_pico2"
|
||||
)
|
||||
else ()
|
||||
error("Unsupported board ${PICO_BOARD}" PICO_BOARD)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
||||
target_link_libraries(picoprobe PRIVATE
|
||||
target_link_libraries(debugprobe PRIVATE
|
||||
pico_multicore
|
||||
pico_stdlib
|
||||
pico_unique_id
|
||||
@@ -67,6 +79,6 @@ target_link_libraries(picoprobe PRIVATE
|
||||
FreeRTOS-Kernel-Heap1
|
||||
)
|
||||
|
||||
pico_set_binary_type(picoprobe copy_to_ram)
|
||||
pico_set_binary_type(debugprobe copy_to_ram)
|
||||
|
||||
pico_add_extra_outputs(picoprobe)
|
||||
pico_add_extra_outputs(debugprobe)
|
||||
|
||||
91
FreeRTOS_Kernel_import.cmake
Executable file → Normal file
91
FreeRTOS_Kernel_import.cmake
Executable file → Normal file
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: BSD-3-clause
|
||||
# This is a copy of <FREERTOS_KERNEL_PATH>/portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate the FreeRTOS kernel
|
||||
@@ -10,38 +11,68 @@ if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH))
|
||||
message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC/RP2040")
|
||||
# undo the above
|
||||
set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..")
|
||||
|
||||
if (NOT FREERTOS_KERNEL_PATH)
|
||||
# check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly)
|
||||
get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH)
|
||||
get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH)
|
||||
if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH)
|
||||
get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH)
|
||||
# first pass we look in old tree; second pass we look in new tree
|
||||
foreach(SEARCH_PASS RANGE 0 1)
|
||||
if (SEARCH_PASS)
|
||||
# ports may be moving to submodule in the future
|
||||
set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/Community-Supported-Ports/GCC")
|
||||
set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../../..")
|
||||
else()
|
||||
set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC")
|
||||
set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..")
|
||||
endif()
|
||||
if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH)
|
||||
get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH)
|
||||
message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake")
|
||||
elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel")
|
||||
set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel)
|
||||
message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (NOT FREERTOS_KERNEL_PATH)
|
||||
foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source)
|
||||
# check if FreeRTOS-Kernel exists under directory that included us
|
||||
set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH)
|
||||
if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt)
|
||||
get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH)
|
||||
message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project")
|
||||
if(PICO_PLATFORM STREQUAL "rp2040")
|
||||
set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2040")
|
||||
else()
|
||||
if (PICO_PLATFORM STREQUAL "rp2350-riscv")
|
||||
set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_RISC-V")
|
||||
else()
|
||||
set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_ARM_NTZ")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT FREERTOS_KERNEL_PATH)
|
||||
# check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly)
|
||||
get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH)
|
||||
get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH)
|
||||
if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH)
|
||||
get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH)
|
||||
endif()
|
||||
if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH)
|
||||
get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH)
|
||||
message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake")
|
||||
break()
|
||||
elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel")
|
||||
set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel)
|
||||
message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (NOT FREERTOS_KERNEL_PATH)
|
||||
foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source)
|
||||
# check if FreeRTOS-Kernel exists under directory that included us
|
||||
set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH)
|
||||
if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt)
|
||||
get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH)
|
||||
message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if (FREERTOS_KERNEL_PATH)
|
||||
break()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# user must have specified
|
||||
if (FREERTOS_KERNEL_PATH)
|
||||
if (EXISTS "${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}")
|
||||
break()
|
||||
endif()
|
||||
endif()
|
||||
endforeach ()
|
||||
|
||||
if (NOT FREERTOS_KERNEL_PATH)
|
||||
message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.")
|
||||
@@ -54,8 +85,8 @@ if (NOT EXISTS ${FREERTOS_KERNEL_PATH})
|
||||
message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found")
|
||||
endif()
|
||||
if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt)
|
||||
message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain an RP2040 port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}")
|
||||
message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain a '${PICO_PLATFORM}' port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}")
|
||||
endif()
|
||||
set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE)
|
||||
|
||||
add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL)
|
||||
add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL)
|
||||
|
||||
23
LICENSE
Normal file
23
LICENSE
Normal file
@@ -0,0 +1,23 @@
|
||||
Unless otherwise indicated in individual files, this project is licenced as:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Raspberry Pi Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
55
README.md
55
README.md
@@ -1,41 +1,68 @@
|
||||
# Picoprobe
|
||||
Picoprobe allows a Pico / RP2040 to be used as USB -> SWD and UART bridge. This means it can be used as a debugger and serial console for another Pico.
|
||||
# Debugprobe
|
||||
|
||||
Firmware source for the Raspberry Pi Debug Probe SWD/UART accessory. Can also be run on a Raspberry Pi Pico.
|
||||
|
||||
[Raspberry Pi Debug Probe product page](https://www.raspberrypi.com/products/debug-probe/)
|
||||
|
||||
[Raspberry Pi Pico product page](https://www.raspberrypi.com/products/raspberry-pi-pico/)
|
||||
|
||||
|
||||
# Documentation
|
||||
Picoprobe documentation can be found in the [Pico Getting Started Guide](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf). See "Appendix A: Using Picoprobe".
|
||||
|
||||
Debug Probe documentation can be found in the [Pico Getting Started Guide](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf). See "Appendix A: Using the Debug Probe".
|
||||
|
||||
# Hacking
|
||||
|
||||
For the purpose of making changes or studying of the code, you may want to compile the code yourself.
|
||||
For the purpose of making changes or studying of the code, you may want to compile the code yourself.
|
||||
|
||||
To compile this project firstly initialize and update the submodules:
|
||||
First, clone the repository:
|
||||
```
|
||||
git clone https://github.com/raspberrypi/debugprobe
|
||||
cd debugprobe
|
||||
```
|
||||
Initialize and update the submodules:
|
||||
```
|
||||
git submodule update --init
|
||||
```
|
||||
then create and switch to the build directory:
|
||||
Then create and switch to the build directory:
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
then run cmake and build the code:
|
||||
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.
|
||||
|
||||
Run cmake and build the code:
|
||||
```
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
Done! You should now have a `picoprobe.uf2` that you can upload to your Pico in the normal way.
|
||||
Done! You should now have a `debugprobe.uf2` that you can upload to your Debug Probe via the UF2 bootloader.
|
||||
|
||||
If you want to create the version that runs on the Raspberry Pi Debug Probe, then you need to invoke `cmake` in the sequence above with the `DEBUGPROBE=ON` option:
|
||||
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:
|
||||
```
|
||||
cmake -DDEBUGPROBE=ON ..
|
||||
cmake -DDEBUG_ON_PICO=ON ..
|
||||
```
|
||||
|
||||
This will build with the configuration for the Debug Probe and call the output program `debugprobe.uf2`, as opposed to `picoprobe.uf2` for the vanilla version.
|
||||
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.
|
||||
|
||||
Note that if you first ran through the whole sequence to compile for the Pico, then you don't need to start back at the top. You can just go back to the `cmake` step and start from there.
|
||||
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.
|
||||
|
||||
# Building for the Pico 2
|
||||
|
||||
If using an existing debugprobe clone:
|
||||
- You must completely regenerate your build directory, or use a different one.
|
||||
- You must also sync and update submodules as rp2350 needs a downstream FreeRTOS port for now.
|
||||
- `PICO_SDK_PATH` must point to a version 2.0.0 or greater install.
|
||||
|
||||
|
||||
```
|
||||
git submodule sync
|
||||
git submodule update --init
|
||||
mkdir build-pico2
|
||||
cd build-pico2
|
||||
cmake -DDEBUG_ON_PICO=1 -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ../
|
||||
```
|
||||
|
||||
# TODO
|
||||
- TinyUSB's vendor interface is FIFO-based and not packet-based. Using raw tx/rx callbacks is preferable as this stops DAP command batches from being concatenated, which confused openOCD.
|
||||
- Instead of polling, move the DAP thread to an asynchronously started/stopped one-shot operation to reduce CPU wakeups
|
||||
- AutoBaud selection, as PIO is a capable frequency counter
|
||||
- Possibly include RTT support
|
||||
|
||||
2
freertos
2
freertos
Submodule freertos updated: 2dfdfc4ba4...682f0515c9
@@ -47,12 +47,12 @@ This information includes:
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "cmsis_compiler.h"
|
||||
#include "picoprobe_config.h"
|
||||
#include "probe_config.h"
|
||||
#include "probe.h"
|
||||
|
||||
/// Processor Clock of the Cortex-M MCU used in the Debug Unit.
|
||||
/// This value is used to calculate the SWD/JTAG clock speed.
|
||||
/* Picoprobe actually uses kHz rather than Hz, so just lie about it here */
|
||||
/* Debugprobe actually uses kHz rather than Hz, so just lie about it here */
|
||||
#define CPU_CLOCK 125000000U ///< Specifies the CPU Clock in Hz.
|
||||
|
||||
/// Number of processor cycles for I/O Port write operations.
|
||||
@@ -502,8 +502,8 @@ It is recommended to provide the following LEDs for status indication:
|
||||
- 0: Connect LED OFF: debugger is not connected to CMSIS-DAP Debug Unit.
|
||||
*/
|
||||
__STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit) {
|
||||
#ifdef PICOPROBE_DAP_CONNECTED_LED
|
||||
gpio_put(PICOPROBE_DAP_CONNECTED_LED, bit);
|
||||
#ifdef PROBE_DAP_CONNECTED_LED
|
||||
gpio_put(PROBE_DAP_CONNECTED_LED, bit);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -513,8 +513,8 @@ __STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit) {
|
||||
- 0: Target Running LED OFF: program execution in target stopped.
|
||||
*/
|
||||
__STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit) {
|
||||
#ifdef PICOPROBE_DAP_RUNNING_LED
|
||||
gpio_put(PICOPROBE_DAP_RUNNING_LED, bit);
|
||||
#ifdef PROBE_DAP_RUNNING_LED
|
||||
gpio_put(PROBE_DAP_RUNNING_LED, bit);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -561,7 +561,16 @@ Status LEDs. In detail the operation of Hardware I/O and LED pins are enabled an
|
||||
- LED output pins are enabled and LEDs are turned off.
|
||||
*/
|
||||
__STATIC_INLINE void DAP_SETUP (void) {
|
||||
probe_gpio_init();
|
||||
// We synchronously setup probe IOs when the respective PIO program is loaded - not at start of day
|
||||
|
||||
#ifdef PROBE_DAP_CONNECTED_LED
|
||||
gpio_init(PROBE_DAP_CONNECTED_LED);
|
||||
gpio_set_dir(PROBE_DAP_CONNECTED_LED, GPIO_OUT);
|
||||
#endif
|
||||
#ifdef PROBE_DAP_RUNNING_LED
|
||||
gpio_init(PROBE_DAP_RUNNING_LED);
|
||||
gpio_set_dir(PROBE_DAP_RUNNING_LED, GPIO_OUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Reset Target Device with custom specific I/O pin or command sequence.
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BOARD_DEBUGPROBE_H_
|
||||
#define BOARD_DEBUGPROBE_H_
|
||||
#ifndef BOARD_DEBUG_PROBE_H_
|
||||
#define BOARD_DEBUG_PROBE_H_
|
||||
|
||||
#define PROBE_IO_SWDI
|
||||
#define PROBE_CDC_UART
|
||||
@@ -39,16 +39,16 @@
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 2)
|
||||
|
||||
// UART config
|
||||
#define PICOPROBE_UART_TX 4
|
||||
#define PICOPROBE_UART_RX 5
|
||||
#define PICOPROBE_UART_INTERFACE uart1
|
||||
#define PICOPROBE_UART_BAUDRATE 115200
|
||||
#define PROBE_UART_TX 4
|
||||
#define PROBE_UART_RX 5
|
||||
#define PROBE_UART_INTERFACE uart1
|
||||
#define PROBE_UART_BAUDRATE 115200
|
||||
|
||||
#define PICOPROBE_USB_CONNECTED_LED 2
|
||||
#define PICOPROBE_DAP_CONNECTED_LED 15
|
||||
#define PICOPROBE_DAP_RUNNING_LED 16
|
||||
#define PICOPROBE_UART_RX_LED 7
|
||||
#define PICOPROBE_UART_TX_LED 8
|
||||
#define PROBE_USB_CONNECTED_LED 2
|
||||
#define PROBE_DAP_CONNECTED_LED 15
|
||||
#define PROBE_DAP_RUNNING_LED 16
|
||||
#define PROBE_UART_RX_LED 7
|
||||
#define PROBE_UART_TX_LED 8
|
||||
|
||||
#define PROBE_PRODUCT_STRING "Debug Probe (CMSIS-DAP)"
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
|
||||
/* Include CDC interface to bridge to target UART. Omit if not used. */
|
||||
#define PROBE_CDC_UART
|
||||
|
||||
/* Board implements hardware flow control for UART RTS/CTS instead of ACM control */
|
||||
#define PROBE_UART_HWFC
|
||||
|
||||
/* Target reset GPIO (active-low). Omit if not used.*/
|
||||
#define PROBE_PIN_RESET 1
|
||||
|
||||
@@ -56,7 +60,7 @@
|
||||
#endif
|
||||
|
||||
/* PIO config for PROBE_IO_OEN - note that SWDIOEN and SWCLK are both side_set signals, so must be consecutive. */
|
||||
#if defined(PROBE_IO_SWDIOEN)
|
||||
#if defined(PROBE_IO_OEN)
|
||||
#define PROBE_PIN_SWDIOEN (PROBE_PIN_OFFSET + 0)
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 1)
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 2)
|
||||
@@ -64,18 +68,29 @@
|
||||
#endif
|
||||
|
||||
#if defined(PROBE_CDC_UART)
|
||||
#define PICOPROBE_UART_TX 4
|
||||
#define PICOPROBE_UART_RX 5
|
||||
#define PICOPROBE_UART_INTERFACE uart1
|
||||
#define PICOPROBE_UART_BAUDRATE 115200
|
||||
#define PROBE_UART_TX 4
|
||||
#define PROBE_UART_RX 5
|
||||
#define PROBE_UART_INTERFACE uart1
|
||||
#define PROBE_UART_BAUDRATE 115200
|
||||
|
||||
#if defined(PROBE_UART_HWFC)
|
||||
/* Hardware flow control - see 1.4.3 in the RP2040 datasheet for valid pin settings */
|
||||
#define PROBE_UART_CTS 6
|
||||
#define PROBE_UART_RTS 7
|
||||
#else
|
||||
/* Software flow control - RTS and DTR can be omitted if not used */
|
||||
#define PROBE_UART_RTS 9
|
||||
#endif
|
||||
#define PROBE_UART_DTR 10
|
||||
|
||||
#endif
|
||||
|
||||
/* LED config - some or all of these can be omitted if not used */
|
||||
#define PICOPROBE_USB_CONNECTED_LED 2
|
||||
#define PICOPROBE_DAP_CONNECTED_LED 15
|
||||
#define PICOPROBE_DAP_RUNNING_LED 16
|
||||
#define PICOPROBE_UART_RX_LED 7
|
||||
#define PICOPROBE_UART_TX_LED 8
|
||||
#define PROBE_USB_CONNECTED_LED 2
|
||||
#define PROBE_DAP_CONNECTED_LED 15
|
||||
#define PROBE_DAP_RUNNING_LED 16
|
||||
#define PROBE_UART_RX_LED 7
|
||||
#define PROBE_UART_TX_LED 8
|
||||
|
||||
#define PROBE_PRODUCT_STRING "Example Debug Probe"
|
||||
|
||||
|
||||
@@ -40,13 +40,13 @@
|
||||
#endif
|
||||
|
||||
// UART config
|
||||
#define PICOPROBE_UART_TX 4
|
||||
#define PICOPROBE_UART_RX 5
|
||||
#define PICOPROBE_UART_INTERFACE uart1
|
||||
#define PICOPROBE_UART_BAUDRATE 115200
|
||||
#define PROBE_UART_TX 4
|
||||
#define PROBE_UART_RX 5
|
||||
#define PROBE_UART_INTERFACE uart1
|
||||
#define PROBE_UART_BAUDRATE 115200
|
||||
|
||||
#define PICOPROBE_USB_CONNECTED_LED 25
|
||||
#define PROBE_USB_CONNECTED_LED 25
|
||||
|
||||
#define PROBE_PRODUCT_STRING "Picoprobe (CMSIS-DAP)"
|
||||
#define PROBE_PRODUCT_STRING "Debugprobe on Pico (CMSIS-DAP)"
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
|
||||
@@ -110,6 +110,13 @@
|
||||
#define configSUPPORT_PICO_SYNC_INTEROP 1
|
||||
#define configSUPPORT_PICO_TIME_INTEROP 1
|
||||
|
||||
/* RP2350 grows some features */
|
||||
#define configENABLE_FPU 1
|
||||
#define configENABLE_MPU 0
|
||||
#define configENABLE_TRUSTZONE 0
|
||||
#define configRUN_FREERTOS_SECURE_ONLY 1
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 16
|
||||
|
||||
#include <assert.h>
|
||||
/* Define to trap errors during development. */
|
||||
#define configASSERT(x) assert(x)
|
||||
|
||||
216
src/cdc_uart.c
216
src/cdc_uart.c
@@ -26,13 +26,14 @@
|
||||
#include <pico/stdlib.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "picoprobe_config.h"
|
||||
#include "probe_config.h"
|
||||
|
||||
TaskHandle_t uart_taskhandle;
|
||||
TickType_t last_wake, interval = 100;
|
||||
volatile TickType_t break_expiry;
|
||||
volatile bool timed_break;
|
||||
|
||||
/* Max 1 FIFO worth of data */
|
||||
static uint8_t tx_buf[32];
|
||||
@@ -41,31 +42,66 @@ static uint8_t rx_buf[32];
|
||||
#define DEBOUNCE_MS 40
|
||||
static uint debounce_ticks = 5;
|
||||
|
||||
#ifdef PICOPROBE_UART_TX_LED
|
||||
static uint tx_led_debounce;
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
static volatile uint tx_led_debounce;
|
||||
#endif
|
||||
|
||||
#ifdef PICOPROBE_UART_RX_LED
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
static uint rx_led_debounce;
|
||||
#endif
|
||||
|
||||
void cdc_uart_init(void) {
|
||||
gpio_set_function(PICOPROBE_UART_TX, GPIO_FUNC_UART);
|
||||
gpio_set_function(PICOPROBE_UART_RX, GPIO_FUNC_UART);
|
||||
gpio_set_pulls(PICOPROBE_UART_TX, 1, 0);
|
||||
gpio_set_pulls(PICOPROBE_UART_RX, 1, 0);
|
||||
uart_init(PICOPROBE_UART_INTERFACE, PICOPROBE_UART_BAUDRATE);
|
||||
gpio_set_function(PROBE_UART_TX, GPIO_FUNC_UART);
|
||||
gpio_set_function(PROBE_UART_RX, GPIO_FUNC_UART);
|
||||
gpio_set_pulls(PROBE_UART_TX, 1, 0);
|
||||
gpio_set_pulls(PROBE_UART_RX, 1, 0);
|
||||
uart_init(PROBE_UART_INTERFACE, PROBE_UART_BAUDRATE);
|
||||
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
tx_led_debounce = 0;
|
||||
gpio_init(PROBE_UART_TX_LED);
|
||||
gpio_set_dir(PROBE_UART_TX_LED, GPIO_OUT);
|
||||
#endif
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
rx_led_debounce = 0;
|
||||
gpio_init(PROBE_UART_RX_LED);
|
||||
gpio_set_dir(PROBE_UART_RX_LED, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_UART_HWFC
|
||||
/* HWFC implies that hardware flow control is implemented and the
|
||||
* UART operates in "full-duplex" mode (See USB CDC PSTN120 6.3.12).
|
||||
* Default to pulling in the active direction, so an unconnected CTS
|
||||
* behaves the same as if CTS were not enabled. */
|
||||
gpio_set_pulls(PROBE_UART_CTS, 0, 1);
|
||||
gpio_set_function(PROBE_UART_RTS, GPIO_FUNC_UART);
|
||||
gpio_set_function(PROBE_UART_CTS, GPIO_FUNC_UART);
|
||||
uart_set_hw_flow(PROBE_UART_INTERFACE, true, true);
|
||||
#else
|
||||
#ifdef PROBE_UART_RTS
|
||||
gpio_init(PROBE_UART_RTS);
|
||||
gpio_set_dir(PROBE_UART_RTS, GPIO_OUT);
|
||||
gpio_put(PROBE_UART_RTS, 1);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_UART_DTR
|
||||
gpio_init(PROBE_UART_DTR);
|
||||
gpio_set_dir(PROBE_UART_DTR, GPIO_OUT);
|
||||
gpio_put(PROBE_UART_DTR, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cdc_task(void)
|
||||
bool cdc_task(void)
|
||||
{
|
||||
static int was_connected = 0;
|
||||
static uint cdc_tx_oe = 0;
|
||||
uint rx_len = 0;
|
||||
bool keep_alive = false;
|
||||
|
||||
// Consume uart fifo regardless even if not connected
|
||||
while(uart_is_readable(PICOPROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) {
|
||||
rx_buf[rx_len++] = uart_getc(PICOPROBE_UART_INTERFACE);
|
||||
while(uart_is_readable(PROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) {
|
||||
rx_buf[rx_len++] = uart_getc(PROBE_UART_INTERFACE);
|
||||
}
|
||||
|
||||
if (tud_cdc_connected()) {
|
||||
@@ -74,8 +110,8 @@ void cdc_task(void)
|
||||
/* Implicit overflow if we don't write all the bytes to the host.
|
||||
* Also throw away bytes if we can't write... */
|
||||
if (rx_len) {
|
||||
#ifdef PICOPROBE_UART_RX_LED
|
||||
gpio_put(PICOPROBE_UART_RX_LED, 1);
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
gpio_put(PROBE_UART_RX_LED, 1);
|
||||
rx_led_debounce = debounce_ticks;
|
||||
#endif
|
||||
written = MIN(tud_cdc_write_available(), rx_len);
|
||||
@@ -87,11 +123,11 @@ void cdc_task(void)
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
} else {
|
||||
#ifdef PICOPROBE_UART_RX_LED
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
if (rx_led_debounce)
|
||||
rx_led_debounce--;
|
||||
else
|
||||
gpio_put(PICOPROBE_UART_RX_LED, 0);
|
||||
gpio_put(PROBE_UART_RX_LED, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -99,75 +135,183 @@ void cdc_task(void)
|
||||
size_t watermark = MIN(tud_cdc_available(), sizeof(tx_buf));
|
||||
if (watermark > 0) {
|
||||
size_t tx_len;
|
||||
#ifdef PICOPROBE_UART_TX_LED
|
||||
gpio_put(PICOPROBE_UART_TX_LED, 1);
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
gpio_put(PROBE_UART_TX_LED, 1);
|
||||
tx_led_debounce = debounce_ticks;
|
||||
#endif
|
||||
/* Batch up to half a FIFO of data - don't clog up on RX */
|
||||
watermark = MIN(watermark, 16);
|
||||
tx_len = tud_cdc_read(tx_buf, watermark);
|
||||
uart_write_blocking(PICOPROBE_UART_INTERFACE, tx_buf, tx_len);
|
||||
uart_write_blocking(PROBE_UART_INTERFACE, tx_buf, tx_len);
|
||||
} else {
|
||||
#ifdef PICOPROBE_UART_TX_LED
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
if (tx_led_debounce)
|
||||
tx_led_debounce--;
|
||||
else
|
||||
gpio_put(PICOPROBE_UART_TX_LED, 0);
|
||||
gpio_put(PROBE_UART_TX_LED, 0);
|
||||
#endif
|
||||
}
|
||||
/* Pending break handling */
|
||||
if (timed_break) {
|
||||
if (((int)break_expiry - (int)xTaskGetTickCount()) < 0) {
|
||||
timed_break = false;
|
||||
uart_set_break(PROBE_UART_INTERFACE, false);
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
tx_led_debounce = 0;
|
||||
#endif
|
||||
} else {
|
||||
keep_alive = true;
|
||||
}
|
||||
}
|
||||
} else if (was_connected) {
|
||||
tud_cdc_write_clear();
|
||||
uart_set_break(PROBE_UART_INTERFACE, false);
|
||||
timed_break = false;
|
||||
was_connected = 0;
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
tx_led_debounce = 0;
|
||||
#endif
|
||||
cdc_tx_oe = 0;
|
||||
}
|
||||
return keep_alive;
|
||||
}
|
||||
|
||||
void cdc_thread(void *ptr)
|
||||
{
|
||||
BaseType_t delayed;
|
||||
last_wake = xTaskGetTickCount();
|
||||
bool keep_alive;
|
||||
/* Threaded with a polling interval that scales according to linerate */
|
||||
while (1) {
|
||||
cdc_task();
|
||||
delayed = xTaskDelayUntil(&last_wake, interval);
|
||||
if (delayed == pdFALSE)
|
||||
last_wake = xTaskGetTickCount();
|
||||
keep_alive = cdc_task();
|
||||
if (!keep_alive) {
|
||||
delayed = xTaskDelayUntil(&last_wake, interval);
|
||||
if (delayed == pdFALSE)
|
||||
last_wake = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
|
||||
{
|
||||
uart_parity_t parity;
|
||||
uint data_bits, stop_bits;
|
||||
/* Set the tick thread interval to the amount of time it takes to
|
||||
* fill up half a FIFO. Millis is too coarse for integer divide.
|
||||
*/
|
||||
uint32_t micros = (1000 * 1000 * 16 * 10) / MAX(line_coding->bit_rate, 1);
|
||||
/* Modifying state, so park the thread before changing it. */
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
if (tud_cdc_connected())
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ));
|
||||
debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS));
|
||||
picoprobe_info("New baud rate %ld micros %ld interval %lu\n",
|
||||
probe_info("New baud rate %ld micros %ld interval %lu\n",
|
||||
line_coding->bit_rate, micros, interval);
|
||||
uart_deinit(PICOPROBE_UART_INTERFACE);
|
||||
uart_deinit(PROBE_UART_INTERFACE);
|
||||
tud_cdc_write_clear();
|
||||
tud_cdc_read_flush();
|
||||
uart_init(PICOPROBE_UART_INTERFACE, line_coding->bit_rate);
|
||||
vTaskResume(uart_taskhandle);
|
||||
uart_init(PROBE_UART_INTERFACE, line_coding->bit_rate);
|
||||
|
||||
switch (line_coding->parity) {
|
||||
case CDC_LINE_CODING_PARITY_ODD:
|
||||
parity = UART_PARITY_ODD;
|
||||
break;
|
||||
case CDC_LINE_CODING_PARITY_EVEN:
|
||||
parity = UART_PARITY_EVEN;
|
||||
break;
|
||||
default:
|
||||
probe_info("invalid parity setting %u\n", line_coding->parity);
|
||||
/* fallthrough */
|
||||
case CDC_LINE_CODING_PARITY_NONE:
|
||||
parity = UART_PARITY_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (line_coding->data_bits) {
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
data_bits = line_coding->data_bits;
|
||||
break;
|
||||
default:
|
||||
probe_info("invalid data bits setting: %u\n", line_coding->data_bits);
|
||||
data_bits = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* The PL011 only supports 1 or 2 stop bits. 1.5 stop bits is translated to 2,
|
||||
* which is safer than the alternative. */
|
||||
switch (line_coding->stop_bits) {
|
||||
case CDC_LINE_CONDING_STOP_BITS_1_5:
|
||||
case CDC_LINE_CONDING_STOP_BITS_2:
|
||||
stop_bits = 2;
|
||||
break;
|
||||
default:
|
||||
probe_info("invalid stop bits setting: %u\n", line_coding->stop_bits);
|
||||
/* fallthrough */
|
||||
case CDC_LINE_CONDING_STOP_BITS_1:
|
||||
stop_bits = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
uart_set_format(PROBE_UART_INTERFACE, data_bits, stop_bits, parity);
|
||||
/* Windows likes to arbitrarily set/get line coding after dtr/rts changes, so
|
||||
* don't resume if we shouldn't */
|
||||
if(tud_cdc_connected())
|
||||
vTaskResume(uart_taskhandle);
|
||||
}
|
||||
|
||||
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
|
||||
{
|
||||
#ifdef PROBE_UART_RTS
|
||||
gpio_put(PROBE_UART_RTS, !rts);
|
||||
#endif
|
||||
#ifdef PROBE_UART_DTR
|
||||
gpio_put(PROBE_UART_DTR, !dtr);
|
||||
#endif
|
||||
|
||||
/* CDC drivers use linestate as a bodge to activate/deactivate the interface.
|
||||
* Resume our UART polling on activate, stop on deactivate */
|
||||
if (!dtr && !rts) {
|
||||
if (!dtr) {
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
#ifdef PICOPROBE_UART_RX_LED
|
||||
gpio_put(PICOPROBE_UART_RX_LED, 0);
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
gpio_put(PROBE_UART_RX_LED, 0);
|
||||
rx_led_debounce = 0;
|
||||
#endif
|
||||
#ifdef PICOPROBE_UART_RX_LED
|
||||
gpio_put(PICOPROBE_UART_TX_LED, 0);
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
gpio_put(PROBE_UART_TX_LED, 0);
|
||||
tx_led_debounce = 0;
|
||||
#endif
|
||||
} else
|
||||
vTaskResume(uart_taskhandle);
|
||||
}
|
||||
|
||||
void tud_cdc_send_break_cb(uint8_t itf, uint16_t wValue) {
|
||||
switch(wValue) {
|
||||
case 0:
|
||||
uart_set_break(PROBE_UART_INTERFACE, false);
|
||||
timed_break = false;
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
tx_led_debounce = 0;
|
||||
#endif
|
||||
break;
|
||||
case 0xffff:
|
||||
uart_set_break(PROBE_UART_INTERFACE, true);
|
||||
timed_break = false;
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
gpio_put(PROBE_UART_TX_LED, 1);
|
||||
tx_led_debounce = 1 << 30;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
uart_set_break(PROBE_UART_INTERFACE, true);
|
||||
timed_break = true;
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
gpio_put(PROBE_UART_TX_LED, 1);
|
||||
tx_led_debounce = 1 << 30;
|
||||
#endif
|
||||
break_expiry = xTaskGetTickCount() + (wValue * (configTICK_RATE_HZ / 1000));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
void cdc_thread(void *ptr);
|
||||
void cdc_uart_init(void);
|
||||
void cdc_task(void);
|
||||
bool cdc_task(void);
|
||||
|
||||
extern TaskHandle_t uart_taskhandle;
|
||||
|
||||
|
||||
52
src/led.c
52
src/led.c
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 a-pushkin on GitHub
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "picoprobe_config.h"
|
||||
|
||||
void led_init(void) {
|
||||
#ifdef PICOPROBE_USB_CONNECTED_LED
|
||||
gpio_init(PICOPROBE_USB_CONNECTED_LED);
|
||||
gpio_set_dir(PICOPROBE_USB_CONNECTED_LED, GPIO_OUT);
|
||||
#endif
|
||||
#ifdef PICOPROBE_DAP_CONNECTED_LED
|
||||
gpio_init(PICOPROBE_DAP_CONNECTED_LED);
|
||||
gpio_set_dir(PICOPROBE_DAP_CONNECTED_LED, GPIO_OUT);
|
||||
#endif
|
||||
#ifdef PICOPROBE_DAP_RUNNING_LED
|
||||
gpio_init(PICOPROBE_DAP_RUNNING_LED);
|
||||
gpio_set_dir(PICOPROBE_DAP_RUNNING_LED, GPIO_OUT);
|
||||
#endif
|
||||
#ifdef PICOPROBE_UART_RX_LED
|
||||
gpio_init(PICOPROBE_UART_RX_LED);
|
||||
gpio_set_dir(PICOPROBE_UART_RX_LED, GPIO_OUT);
|
||||
#endif
|
||||
#ifdef PICOPROBE_UART_TX_LED
|
||||
gpio_init(PICOPROBE_UART_TX_LED);
|
||||
gpio_set_dir(PICOPROBE_UART_TX_LED, GPIO_OUT);
|
||||
#endif
|
||||
}
|
||||
31
src/led.h
31
src/led.h
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 a-pushkin on GitHub
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LED_H
|
||||
#define LED_H
|
||||
|
||||
void led_init(void);
|
||||
|
||||
#endif
|
||||
123
src/main.c
123
src/main.c
@@ -31,19 +31,23 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if PICO_SDK_VERSION_MAJOR >= 2
|
||||
#include "bsp/board_api.h"
|
||||
#else
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "tusb.h"
|
||||
|
||||
#include "picoprobe_config.h"
|
||||
#include "probe_config.h"
|
||||
#include "probe.h"
|
||||
#include "cdc_uart.h"
|
||||
#include "get_serial.h"
|
||||
#include "led.h"
|
||||
#include "tusb_edpt_handler.h"
|
||||
#include "DAP.h"
|
||||
#include "hardware/structs/usb.h"
|
||||
|
||||
// UART0 for Picoprobe debug
|
||||
// UART1 for picoprobe to target device
|
||||
// UART0 for debugprobe debug
|
||||
// UART1 for debugprobe to target device
|
||||
|
||||
static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
|
||||
static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
|
||||
@@ -54,22 +58,60 @@ static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
|
||||
#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2)
|
||||
#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1)
|
||||
|
||||
TaskHandle_t dap_taskhandle, tud_taskhandle;
|
||||
TaskHandle_t dap_taskhandle, tud_taskhandle, mon_taskhandle;
|
||||
|
||||
static int was_configured;
|
||||
|
||||
void dev_mon(void *ptr)
|
||||
{
|
||||
uint32_t sof[3];
|
||||
int i = 0;
|
||||
TickType_t wake;
|
||||
wake = xTaskGetTickCount();
|
||||
do {
|
||||
/* ~5 SOF events per tick */
|
||||
xTaskDelayUntil(&wake, 100);
|
||||
if (tud_connected() && !tud_suspended()) {
|
||||
sof[i++] = usb_hw->sof_rd & USB_SOF_RD_BITS;
|
||||
i = i % 3;
|
||||
} else {
|
||||
for (i = 0; i < 3; i++)
|
||||
sof[i] = 0;
|
||||
}
|
||||
if ((sof[0] | sof[1] | sof[2]) != 0) {
|
||||
if ((sof[0] == sof[1]) && (sof[1] == sof[2])) {
|
||||
probe_info("Watchdog timeout! Resetting USBD\n");
|
||||
/* uh oh, signal disconnect (implicitly resets the controller) */
|
||||
tud_deinit(0);
|
||||
/* Make sure the port got the message */
|
||||
xTaskDelayUntil(&wake, 1);
|
||||
tud_init(0);
|
||||
}
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void usb_thread(void *ptr)
|
||||
{
|
||||
#ifdef PROBE_USB_CONNECTED_LED
|
||||
gpio_init(PROBE_USB_CONNECTED_LED);
|
||||
gpio_set_dir(PROBE_USB_CONNECTED_LED, GPIO_OUT);
|
||||
#endif
|
||||
TickType_t wake;
|
||||
wake = xTaskGetTickCount();
|
||||
do {
|
||||
tud_task();
|
||||
#ifdef PICOPROBE_USB_CONNECTED_LED
|
||||
if (!gpio_get(PICOPROBE_USB_CONNECTED_LED) && tud_ready())
|
||||
gpio_put(PICOPROBE_USB_CONNECTED_LED, 1);
|
||||
#ifdef PROBE_USB_CONNECTED_LED
|
||||
if (!gpio_get(PROBE_USB_CONNECTED_LED) && tud_ready())
|
||||
gpio_put(PROBE_USB_CONNECTED_LED, 1);
|
||||
else
|
||||
gpio_put(PICOPROBE_USB_CONNECTED_LED, 0);
|
||||
gpio_put(PROBE_USB_CONNECTED_LED, 0);
|
||||
#endif
|
||||
// If suspended or disconnected, delay for 1ms (20 ticks)
|
||||
if (tud_suspended() || !tud_connected())
|
||||
xTaskDelayUntil(&wake, 20);
|
||||
// Go to sleep for up to a tick if nothing to do
|
||||
if (!tud_task_event_ready())
|
||||
else if (!tud_task_event_ready())
|
||||
xTaskDelayUntil(&wake, 1);
|
||||
} while (1);
|
||||
}
|
||||
@@ -80,25 +122,24 @@ void usb_thread(void *ptr)
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
// Declare pins in binary information
|
||||
bi_decl_config();
|
||||
|
||||
board_init();
|
||||
usb_serial_init();
|
||||
cdc_uart_init();
|
||||
tusb_init();
|
||||
|
||||
DAP_Setup();
|
||||
stdio_uart_init();
|
||||
|
||||
led_init();
|
||||
DAP_Setup();
|
||||
|
||||
picoprobe_info("Welcome to Picoprobe!\n");
|
||||
probe_info("Welcome to debugprobe!\n");
|
||||
|
||||
if (THREADED) {
|
||||
/* UART needs to preempt USB as if we don't, characters get lost */
|
||||
xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle);
|
||||
xTaskCreate(usb_thread, "TUD", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &tud_taskhandle);
|
||||
/* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */
|
||||
xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle);
|
||||
#if PICO_RP2040
|
||||
xTaskCreate(dev_mon, "WDOG", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &mon_taskhandle);
|
||||
#endif
|
||||
vTaskStartScheduler();
|
||||
}
|
||||
|
||||
@@ -106,7 +147,7 @@ int main(void) {
|
||||
tud_task();
|
||||
cdc_task();
|
||||
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
if (tud_vendor_available()) {
|
||||
uint32_t resp_len;
|
||||
tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
|
||||
@@ -145,7 +186,7 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep
|
||||
tud_hid_report(0, TxDataBuffer, response_size);
|
||||
}
|
||||
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
extern uint8_t const desc_ms_os_20[];
|
||||
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
@@ -182,6 +223,48 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
|
||||
}
|
||||
#endif
|
||||
|
||||
void tud_suspend_cb(bool remote_wakeup_en)
|
||||
{
|
||||
probe_info("Suspended\n");
|
||||
/* Were we actually configured? If not, threads don't exist */
|
||||
if (was_configured) {
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
}
|
||||
/* slow down clk_sys for power saving ? */
|
||||
}
|
||||
|
||||
void tud_resume_cb(void)
|
||||
{
|
||||
probe_info("Resumed\n");
|
||||
if (was_configured) {
|
||||
vTaskResume(uart_taskhandle);
|
||||
vTaskResume(dap_taskhandle);
|
||||
}
|
||||
}
|
||||
|
||||
void tud_unmount_cb(void)
|
||||
{
|
||||
probe_info("Disconnected\n");
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
vTaskDelete(uart_taskhandle);
|
||||
vTaskDelete(dap_taskhandle);
|
||||
was_configured = 0;
|
||||
}
|
||||
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
probe_info("Connected, Configured\n");
|
||||
if (!was_configured) {
|
||||
/* UART needs to preempt USB as if we don't, characters get lost */
|
||||
xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle);
|
||||
/* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */
|
||||
xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle);
|
||||
was_configured = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void vApplicationTickHook (void)
|
||||
{
|
||||
};
|
||||
|
||||
17
src/probe.c
17
src/probe.c
@@ -30,9 +30,8 @@
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "led.h"
|
||||
#include "picoprobe_config.h"
|
||||
#include "probe.pio.h"
|
||||
#include "probe_config.h"
|
||||
#include "probe.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#define DIV_ROUND_UP(m, n) (((m) + (n) - 1) / (n))
|
||||
@@ -61,7 +60,7 @@ static struct _probe probe;
|
||||
|
||||
void probe_set_swclk_freq(uint freq_khz) {
|
||||
uint clk_sys_freq_khz = clock_get_hz(clk_sys) / 1000;
|
||||
picoprobe_info("Set swclk freq %dKHz sysclk %dkHz\n", freq_khz, clk_sys_freq_khz);
|
||||
probe_info("Set swclk freq %dKHz sysclk %dkHz\n", freq_khz, clk_sys_freq_khz);
|
||||
uint32_t divider = clk_sys_freq_khz / freq_khz / 4;
|
||||
if (divider == 0)
|
||||
divider = 1;
|
||||
@@ -72,7 +71,7 @@ void probe_assert_reset(bool state)
|
||||
{
|
||||
#if defined(PROBE_PIN_RESET)
|
||||
/* Change the direction to out to drive pin to 0 or to in to emulate open drain */
|
||||
gpio_set_dir(PROBE_PIN_RESET, state);
|
||||
gpio_set_dir(PROBE_PIN_RESET, state == 0 ? GPIO_OUT : GPIO_IN);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -105,7 +104,7 @@ void probe_write_bits(uint bit_count, uint32_t data_byte) {
|
||||
DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE);
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, true, CMD_WRITE));
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, data_byte);
|
||||
picoprobe_dump("Write %d bits 0x%x\n", bit_count, data_byte);
|
||||
probe_dump("Write %d bits 0x%x\n", bit_count, data_byte);
|
||||
// Return immediately so we can cue up the next command whilst this one runs
|
||||
DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE);
|
||||
}
|
||||
@@ -124,7 +123,7 @@ uint32_t probe_read_bits(uint bit_count) {
|
||||
data_shifted = data >> (32 - bit_count);
|
||||
}
|
||||
|
||||
picoprobe_dump("Read %d bits 0x%x (shifted 0x%x)\n", bit_count, data, data_shifted);
|
||||
probe_dump("Read %d bits 0x%x (shifted 0x%x)\n", bit_count, data, data_shifted);
|
||||
DEBUG_PINS_CLR(probe_timing, DBG_PIN_READ);
|
||||
return data_shifted;
|
||||
}
|
||||
@@ -147,6 +146,7 @@ void probe_write_mode(void) {
|
||||
|
||||
void probe_init() {
|
||||
if (!probe.initted) {
|
||||
probe_gpio_init();
|
||||
uint offset = pio_add_program(pio0, &probe_program);
|
||||
probe.offset = offset;
|
||||
|
||||
@@ -170,6 +170,9 @@ void probe_deinit(void)
|
||||
probe_read_mode();
|
||||
pio_sm_set_enabled(pio0, PROBE_SM, 0);
|
||||
pio_remove_program(pio0, &probe_program, probe.offset);
|
||||
|
||||
probe_assert_reset(1); // de-assert nRESET
|
||||
probe_gpio_deinit();
|
||||
probe.initted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,19 @@ static inline void probe_gpio_init()
|
||||
gpio_pull_up(PROBE_PIN_SWDIO);
|
||||
}
|
||||
|
||||
// DAP interface says all pins have to be High-Z when disabled
|
||||
static inline void probe_gpio_deinit()
|
||||
{
|
||||
#if defined(PROBE_PIN_RESET)
|
||||
gpio_deinit(PROBE_PIN_RESET);
|
||||
gpio_disable_pulls(PROBE_PIN_RESET);
|
||||
#endif
|
||||
gpio_deinit(PROBE_PIN_SWCLK);
|
||||
gpio_disable_pulls(PROBE_PIN_SWCLK);
|
||||
gpio_deinit(PROBE_PIN_SWDIO);
|
||||
gpio_disable_pulls(PROBE_PIN_SWDIO);
|
||||
}
|
||||
|
||||
static inline void probe_sm_init(pio_sm_config* sm_config) {
|
||||
|
||||
// Set SWCLK as a sideset pin
|
||||
|
||||
48
src/probe_config.c
Normal file
48
src/probe_config.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "probe_config.h"
|
||||
#include "pico/binary_info.h"
|
||||
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
|
||||
void bi_decl_config()
|
||||
{
|
||||
#ifdef PROBE_PIN_RESET
|
||||
bi_decl(bi_1pin_with_name(PROBE_PIN_RESET, "PROBE RESET"));
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_PIN_SWCLK
|
||||
bi_decl(bi_1pin_with_name(PROBE_PIN_SWCLK, "PROBE SWCLK"));
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_PIN_SWDIO
|
||||
bi_decl(bi_1pin_with_name(PROBE_PIN_SWDIO, "PROBE SWDIO"));
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_PIN_SWDI
|
||||
bi_decl(bi_1pin_with_name(PROBE_PIN_SWDI, "PROBE SWDI"));
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_PIN_SWDIOEN
|
||||
bi_decl(bi_1pin_with_name(PROBE_PIN_SWDIOEN, "PROBE SWDIOEN"));
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_CDC_UART
|
||||
bi_decl(bi_program_feature("PROBE UART INTERFACE " STR(PROBE_UART_INTERFACE)));
|
||||
bi_decl(bi_program_feature("PROBE UART BAUDRATE " STR(PROBE_UART_BAUDRATE)));
|
||||
bi_decl(bi_1pin_with_name(PROBE_UART_TX, "PROBE UART TX"));
|
||||
bi_decl(bi_1pin_with_name(PROBE_UART_RX, "PROBE UART RX"));
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_UART_CTS
|
||||
bi_decl(bi_1pin_with_name(PROBE_UART_CTS, "PROBE UART CTS"));
|
||||
#endif
|
||||
#ifdef PROBE_UART_RTS
|
||||
bi_decl(bi_1pin_with_name(PROBE_UART_RTS, "PROBE UART RTS"));
|
||||
#endif
|
||||
#ifdef PROBE_UART_DTR
|
||||
bi_decl(bi_1pin_with_name(PROBE_UART_DTR, "PROBE UART DTR"));
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -23,62 +23,64 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PICOPROBE_H_
|
||||
#define PICOPROBE_H_
|
||||
#ifndef PROBE_CONFIG_H_
|
||||
#define PROBE_CONFIG_H_
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#if false
|
||||
#define picoprobe_info(format,args...) \
|
||||
#define probe_info(format,args...) \
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define picoprobe_info(format,...) ((void)0)
|
||||
#define probe_info(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
#if false
|
||||
#define picoprobe_debug(format,args...) \
|
||||
#define probe_debug(format,args...) \
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define picoprobe_debug(format,...) ((void)0)
|
||||
#define probe_debug(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if false
|
||||
#define picoprobe_dump(format,args...)\
|
||||
#define probe_dump(format,args...)\
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define picoprobe_dump(format,...) ((void)0)
|
||||
#define probe_dump(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
// TODO tie this up with PICO_BOARD defines in the main SDK
|
||||
|
||||
#ifndef DEBUGPROBE
|
||||
#ifdef DEBUG_ON_PICO
|
||||
#include "board_pico_config.h"
|
||||
#else
|
||||
#include "board_debugprobe_config.h"
|
||||
#include "board_debug_probe_config.h"
|
||||
#endif
|
||||
//#include "board_example_config.h"
|
||||
|
||||
// Add the configuration to binary information
|
||||
void bi_decl_config();
|
||||
|
||||
#define PROTO_DAP_V1 1
|
||||
#define PROTO_DAP_V2 2
|
||||
|
||||
// Interface config
|
||||
#ifndef PICOPROBE_DEBUG_PROTOCOL
|
||||
#define PICOPROBE_DEBUG_PROTOCOL PROTO_DAP_V2
|
||||
#ifndef PROBE_DEBUG_PROTOCOL
|
||||
#define PROBE_DEBUG_PROTOCOL PROTO_DAP_V2
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,6 @@
|
||||
; SPDX-License-Identifier: MIT
|
||||
; Copyright (c) 2023 Raspberry Pi Ltd
|
||||
|
||||
; Output-enable active-low variant of the SWD probe
|
||||
|
||||
; This program is very similar to the one in probe.pio. The only difference is
|
||||
@@ -60,6 +63,21 @@ static inline void probe_gpio_init()
|
||||
gpio_pull_up(PROBE_PIN_SWDIOEN);
|
||||
}
|
||||
|
||||
// DAP interface says all pins have to be High-Z when disabled
|
||||
static inline void probe_gpio_deinit()
|
||||
{
|
||||
#if defined(PROBE_PIN_RESET)
|
||||
gpio_deinit(PROBE_PIN_RESET);
|
||||
gpio_disable_pulls(PROBE_PIN_RESET);
|
||||
#endif
|
||||
gpio_deinit(PROBE_PIN_SWCLK);
|
||||
gpio_disable_pulls(PROBE_PIN_SWCLK);
|
||||
// Note for SWDIOEN - make sure the driver output enable is removed before removing drive - leave pull-up enabled
|
||||
gpio_deinit(PROBE_PIN_SWDIOEN);
|
||||
gpio_deinit(PROBE_PIN_SWDIO);
|
||||
gpio_disable_pulls(PROBE_PIN_SWDIO);
|
||||
}
|
||||
|
||||
static inline void probe_sm_init(pio_sm_config* sm_config) {
|
||||
|
||||
// Set SWDIOEN and SWCLK as sideset pins
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
/*
|
||||
* This is a shim between the SW_DP functions and the PIO
|
||||
* implementation used for Picoprobe. Instead of calling bitbash functions,
|
||||
* implementation used for Debugprobe. Instead of calling bitbash functions,
|
||||
* hand off the bit sequences to a SM for asynchronous completion.
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,7 @@ void SWJ_Sequence (uint32_t count, const uint8_t *data) {
|
||||
probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
|
||||
cached_delay = DAP_Data.clock_delay;
|
||||
}
|
||||
picoprobe_debug("SWJ sequence count = %d FDB=0x%2x\n", count, data[0]);
|
||||
probe_debug("SWJ sequence count = %d FDB=0x%2x\n", count, data[0]);
|
||||
n = count;
|
||||
while (n > 0) {
|
||||
if (n > 8)
|
||||
@@ -74,7 +74,7 @@ void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
|
||||
probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
|
||||
cached_delay = DAP_Data.clock_delay;
|
||||
}
|
||||
picoprobe_debug("SWD sequence\n");
|
||||
probe_debug("SWD sequence\n");
|
||||
n = info & SWD_SEQUENCE_CLK;
|
||||
if (n == 0U) {
|
||||
n = 64U;
|
||||
@@ -119,7 +119,7 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
|
||||
cached_delay = DAP_Data.clock_delay;
|
||||
}
|
||||
picoprobe_debug("SWD_transfer\n");
|
||||
probe_debug("SWD_transfer\n");
|
||||
/* Generate the request packet */
|
||||
prq |= (1 << 0); /* Start Bit */
|
||||
for (n = 1; n < 5; n++) {
|
||||
@@ -149,7 +149,7 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
}
|
||||
if (data)
|
||||
*data = val;
|
||||
picoprobe_debug("Read %02x ack %02x 0x%08x parity %01x\n",
|
||||
probe_debug("Read %02x ack %02x 0x%08x parity %01x\n",
|
||||
prq, ack, val, bit);
|
||||
/* Turnaround for line idle */
|
||||
probe_hiz_clocks(DAP_Data.swd_conf.turnaround);
|
||||
@@ -163,7 +163,7 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
parity = __builtin_popcount(val);
|
||||
/* Write Parity Bit */
|
||||
probe_write_bits(1, parity & 0x1);
|
||||
picoprobe_debug("write %02x ack %02x 0x%08x parity %01x\n",
|
||||
probe_debug("write %02x ack %02x 0x%08x parity %01x\n",
|
||||
prq, ack, val, parity);
|
||||
}
|
||||
/* Capture Timestamp */
|
||||
|
||||
@@ -12,207 +12,280 @@ static uint8_t _rhport;
|
||||
|
||||
volatile uint32_t _resp_len;
|
||||
|
||||
uint8_t _out_ep_addr;
|
||||
uint8_t _in_ep_addr;
|
||||
static uint8_t _out_ep_addr;
|
||||
static uint8_t _in_ep_addr;
|
||||
|
||||
buffer_t USBRequestBuffer;
|
||||
buffer_t USBResponseBuffer;
|
||||
static buffer_t USBRequestBuffer;
|
||||
static buffer_t USBResponseBuffer;
|
||||
|
||||
static uint8_t DAPRequestBuffer[DAP_PACKET_SIZE];
|
||||
static uint8_t DAPResponseBuffer[DAP_PACKET_SIZE];
|
||||
|
||||
#define WR_IDX(x) (x.wptr % DAP_PACKET_COUNT)
|
||||
#define RD_IDX(x) (x.rptr % DAP_PACKET_COUNT)
|
||||
|
||||
#define WR_SLOT_PTR(x) &(x.data[WR_IDX(x)][0])
|
||||
#define RD_SLOT_PTR(x) &(x.data[RD_IDX(x)][0])
|
||||
|
||||
bool buffer_full(buffer_t *buffer)
|
||||
{
|
||||
return ((buffer->wptr + 1) % DAP_PACKET_COUNT == buffer->rptr);
|
||||
}
|
||||
|
||||
bool buffer_empty(buffer_t *buffer)
|
||||
{
|
||||
return (buffer->wptr == buffer->rptr);
|
||||
}
|
||||
|
||||
void dap_edpt_init(void) {
|
||||
|
||||
}
|
||||
|
||||
void dap_edpt_reset(uint8_t __unused rhport)
|
||||
|
||||
bool dap_edpt_deinit(void)
|
||||
{
|
||||
itf_num = 0;
|
||||
memset(DAPRequestBuffer, 0, sizeof(DAPRequestBuffer));
|
||||
memset(DAPResponseBuffer, 0, sizeof(DAPResponseBuffer));
|
||||
USBRequestBuffer.wptr = USBRequestBuffer.rptr = 0;
|
||||
USBResponseBuffer.wptr = USBResponseBuffer.rptr = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void dap_edpt_reset(uint8_t __unused rhport)
|
||||
{
|
||||
itf_num = 0;
|
||||
}
|
||||
|
||||
char * dap_cmd_string[] = {
|
||||
[ID_DAP_Info ] = "DAP_Info",
|
||||
[ID_DAP_HostStatus ] = "DAP_HostStatus",
|
||||
[ID_DAP_Connect ] = "DAP_Connect",
|
||||
[ID_DAP_Disconnect ] = "DAP_Disconnect",
|
||||
[ID_DAP_TransferConfigure ] = "DAP_TransferConfigure",
|
||||
[ID_DAP_Transfer ] = "DAP_Transfer",
|
||||
[ID_DAP_TransferBlock ] = "DAP_TransferBlock",
|
||||
[ID_DAP_TransferAbort ] = "DAP_TransferAbort",
|
||||
[ID_DAP_WriteABORT ] = "DAP_WriteABORT",
|
||||
[ID_DAP_Delay ] = "DAP_Delay",
|
||||
[ID_DAP_ResetTarget ] = "DAP_ResetTarget",
|
||||
[ID_DAP_SWJ_Pins ] = "DAP_SWJ_Pins",
|
||||
[ID_DAP_SWJ_Clock ] = "DAP_SWJ_Clock",
|
||||
[ID_DAP_SWJ_Sequence ] = "DAP_SWJ_Sequence",
|
||||
[ID_DAP_SWD_Configure ] = "DAP_SWD_Configure",
|
||||
[ID_DAP_SWD_Sequence ] = "DAP_SWD_Sequence",
|
||||
[ID_DAP_JTAG_Sequence ] = "DAP_JTAG_Sequence",
|
||||
[ID_DAP_JTAG_Configure ] = "DAP_JTAG_Configure",
|
||||
[ID_DAP_JTAG_IDCODE ] = "DAP_JTAG_IDCODE",
|
||||
[ID_DAP_SWO_Transport ] = "DAP_SWO_Transport",
|
||||
[ID_DAP_SWO_Mode ] = "DAP_SWO_Mode",
|
||||
[ID_DAP_SWO_Baudrate ] = "DAP_SWO_Baudrate",
|
||||
[ID_DAP_SWO_Control ] = "DAP_SWO_Control",
|
||||
[ID_DAP_SWO_Status ] = "DAP_SWO_Status",
|
||||
[ID_DAP_SWO_ExtendedStatus ] = "DAP_SWO_ExtendedStatus",
|
||||
[ID_DAP_SWO_Data ] = "DAP_SWO_Data",
|
||||
[ID_DAP_QueueCommands ] = "DAP_QueueCommands",
|
||||
[ID_DAP_ExecuteCommands ] = "DAP_ExecuteCommands",
|
||||
};
|
||||
|
||||
|
||||
uint16_t dap_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
|
||||
PICOPROBE_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||
PICOPROBE_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
|
||||
{
|
||||
|
||||
// Initialise circular buffer indices
|
||||
USBResponseBuffer.packet_wr_idx = 0;
|
||||
USBResponseBuffer.packet_rd_idx = 0;
|
||||
USBRequestBuffer.packet_wr_idx = 0;
|
||||
USBRequestBuffer.packet_rd_idx = 0;
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
|
||||
DAP_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||
DAP_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
|
||||
|
||||
// Initialse full/empty flags
|
||||
USBResponseBuffer.wasFull = false;
|
||||
USBResponseBuffer.wasEmpty = true;
|
||||
USBRequestBuffer.wasFull = false;
|
||||
USBRequestBuffer.wasEmpty = true;
|
||||
// Initialise circular buffer indices
|
||||
USBResponseBuffer.wptr = 0;
|
||||
USBResponseBuffer.rptr = 0;
|
||||
USBRequestBuffer.wptr = 0;
|
||||
USBRequestBuffer.rptr = 0;
|
||||
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + (itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_VERIFY(max_len >= drv_len, 0);
|
||||
itf_num = itf_desc->bInterfaceNumber;
|
||||
// Initialse full/empty flags
|
||||
USBResponseBuffer.wasFull = false;
|
||||
USBResponseBuffer.wasEmpty = true;
|
||||
USBRequestBuffer.wasFull = false;
|
||||
USBRequestBuffer.wasEmpty = true;
|
||||
|
||||
// Initialising the OUT endpoint
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + (itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_VERIFY(max_len >= drv_len, 0);
|
||||
itf_num = itf_desc->bInterfaceNumber;
|
||||
|
||||
tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1);
|
||||
uint8_t ep_addr = edpt_desc->bEndpointAddress;
|
||||
// Initialising the OUT endpoint
|
||||
|
||||
_out_ep_addr = ep_addr;
|
||||
tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1);
|
||||
uint8_t ep_addr = edpt_desc->bEndpointAddress;
|
||||
|
||||
// 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
|
||||
usbd_edpt_open(rhport, edpt_desc);
|
||||
usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
|
||||
_out_ep_addr = ep_addr;
|
||||
|
||||
// Initiliasing the IN endpoint
|
||||
// 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
|
||||
usbd_edpt_open(rhport, edpt_desc);
|
||||
usbd_edpt_xfer(rhport, ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
|
||||
|
||||
edpt_desc++;
|
||||
ep_addr = edpt_desc->bEndpointAddress;
|
||||
// Initiliasing the IN endpoint
|
||||
|
||||
_in_ep_addr = ep_addr;
|
||||
edpt_desc++;
|
||||
ep_addr = edpt_desc->bEndpointAddress;
|
||||
|
||||
// The IN endpoint doesn't need a transfer to initialise it, as this will be done by the main loop of dap_thread
|
||||
usbd_edpt_open(rhport, edpt_desc);
|
||||
_in_ep_addr = ep_addr;
|
||||
|
||||
return drv_len;
|
||||
// The IN endpoint doesn't need a transfer to initialise it, as this will be done by the main loop of dap_thread
|
||||
usbd_edpt_open(rhport, edpt_desc);
|
||||
|
||||
return drv_len;
|
||||
|
||||
}
|
||||
|
||||
bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Manage USBResponseBuffer (request) write and USBRequestBuffer (response) read indices
|
||||
bool dap_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
const uint8_t ep_dir = tu_edpt_dir(ep_addr);
|
||||
{
|
||||
const uint8_t ep_dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
if(ep_dir == TUSB_DIR_IN)
|
||||
{
|
||||
if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
|
||||
{
|
||||
USBResponseBuffer.packet_rd_idx = (USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT;
|
||||
if(ep_dir == TUSB_DIR_IN)
|
||||
{
|
||||
if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
|
||||
{
|
||||
USBResponseBuffer.rptr++;
|
||||
|
||||
// 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
|
||||
// So, queue up the buffer at the new read index, since we expect read to catch up to write at this point.
|
||||
// It is possible for the read index to be multiple spaces behind the write index (if the USB callbacks are lagging behind dap thread),
|
||||
// so we account for this by only setting wasEmpty to true if the next callback will empty the buffer
|
||||
if(!USBResponseBuffer.wasEmpty)
|
||||
{
|
||||
usbd_edpt_xfer(rhport, ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len);
|
||||
USBResponseBuffer.wasEmpty = ((USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT == USBResponseBuffer.packet_wr_idx);
|
||||
}
|
||||
// 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
|
||||
// So, queue up the buffer at the new read index, since we expect read to catch up to write at this point.
|
||||
// It is possible for the read index to be multiple spaces behind the write index (if the USB callbacks are lagging behind dap thread),
|
||||
// so we account for this by only setting wasEmpty to true if the next callback will empty the buffer
|
||||
if(!USBResponseBuffer.wasEmpty)
|
||||
{
|
||||
usbd_edpt_xfer(rhport, ep_addr, RD_SLOT_PTR(USBResponseBuffer), (uint16_t) _resp_len);
|
||||
USBResponseBuffer.wasEmpty = (USBResponseBuffer.rptr + 1) == USBResponseBuffer.wptr;
|
||||
}
|
||||
|
||||
// Wake up DAP thread after processing the callback
|
||||
vTaskResume(dap_taskhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Wake up DAP thread after processing the callback
|
||||
vTaskResume(dap_taskhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if(ep_dir == TUSB_DIR_OUT) {
|
||||
return false;
|
||||
|
||||
if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
|
||||
{
|
||||
// Only queue the next buffer in the out callback if the buffer is not full
|
||||
// If full, we set the wasFull flag, which will be checked by dap thread
|
||||
if(!buffer_full(&USBRequestBuffer))
|
||||
{
|
||||
USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.wasFull = false;
|
||||
}
|
||||
else {
|
||||
USBRequestBuffer.wasFull = true;
|
||||
}
|
||||
} else if(ep_dir == TUSB_DIR_OUT) {
|
||||
|
||||
// Wake up DAP thread after processing the callback
|
||||
vTaskResume(dap_taskhandle);
|
||||
return true;
|
||||
}
|
||||
if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
|
||||
{
|
||||
// Only queue the next buffer in the out callback if the buffer is not full
|
||||
// If full, we set the wasFull flag, which will be checked by dap thread
|
||||
if(!buffer_full(&USBRequestBuffer))
|
||||
{
|
||||
USBRequestBuffer.wptr++;
|
||||
usbd_edpt_xfer(rhport, ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.wasFull = false;
|
||||
}
|
||||
else {
|
||||
USBRequestBuffer.wasFull = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
// Wake up DAP thread after processing the callback
|
||||
vTaskResume(dap_taskhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
void dap_thread(void *ptr)
|
||||
{
|
||||
uint8_t DAPRequestBuffer[DAP_PACKET_SIZE];
|
||||
uint8_t DAPResponseBuffer[DAP_PACKET_SIZE];
|
||||
uint32_t n;
|
||||
do
|
||||
{
|
||||
while(USBRequestBuffer.rptr != USBRequestBuffer.wptr)
|
||||
{
|
||||
/*
|
||||
* Atomic command support - buffer QueueCommands, but don't process them
|
||||
* until a non-QueueCommands packet is seen.
|
||||
*/
|
||||
n = USBRequestBuffer.rptr;
|
||||
while (USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] == ID_DAP_QueueCommands) {
|
||||
probe_info("%lu %lu DAP queued cmd %s len %02x\n",
|
||||
USBRequestBuffer.wptr, USBRequestBuffer.rptr,
|
||||
dap_cmd_string[USBRequestBuffer.data[n % DAP_PACKET_COUNT][0]], USBRequestBuffer.data[n % DAP_PACKET_COUNT][1]);
|
||||
USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] = ID_DAP_ExecuteCommands;
|
||||
n++;
|
||||
while (n == USBRequestBuffer.wptr) {
|
||||
/* Need yield in a loop here, as IN callbacks will also wake the thread */
|
||||
probe_info("DAP wait\n");
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
}
|
||||
}
|
||||
// Read a single packet from the USB buffer into the DAP Request buffer
|
||||
memcpy(DAPRequestBuffer, RD_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
|
||||
probe_info("%lu %lu DAP cmd %s len %02x\n",
|
||||
USBRequestBuffer.wptr, USBRequestBuffer.rptr,
|
||||
dap_cmd_string[DAPRequestBuffer[0]], DAPRequestBuffer[1]);
|
||||
USBRequestBuffer.rptr++;
|
||||
|
||||
do
|
||||
{
|
||||
while(USBRequestBuffer.packet_rd_idx != USBRequestBuffer.packet_wr_idx)
|
||||
{
|
||||
// Read a single packet from the USB buffer into the DAP Request buffer
|
||||
memcpy(DAPRequestBuffer, &(USBRequestBuffer.data[USBRequestBuffer.packet_rd_idx]), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.packet_rd_idx = (USBRequestBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT;
|
||||
// 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.
|
||||
if(USBRequestBuffer.wasFull)
|
||||
{
|
||||
vTaskSuspendAll(); // Suspend the scheduler to safely update the write index
|
||||
USBRequestBuffer.wptr++;
|
||||
usbd_edpt_xfer(_rhport, _out_ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.wasFull = false;
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
// 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.
|
||||
if(USBRequestBuffer.wasFull)
|
||||
{
|
||||
vTaskSuspendAll(); // Suspend the scheduler to safely update the write index
|
||||
USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
usbd_edpt_xfer(_rhport, _out_ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.wasFull = false;
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
_resp_len = DAP_ProcessCommand(DAPRequestBuffer, DAPResponseBuffer);
|
||||
_resp_len = DAP_ExecuteCommand(DAPRequestBuffer, DAPResponseBuffer);
|
||||
probe_info("%lu %lu DAP resp %s\n",
|
||||
USBResponseBuffer.wptr, USBResponseBuffer.rptr,
|
||||
dap_cmd_string[DAPResponseBuffer[0]]);
|
||||
|
||||
|
||||
// Suspend the scheduler to avoid stale values/race conditions between threads
|
||||
vTaskSuspendAll();
|
||||
// Suspend the scheduler to avoid stale values/race conditions between threads
|
||||
vTaskSuspendAll();
|
||||
|
||||
if(buffer_empty(&USBResponseBuffer))
|
||||
{
|
||||
memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len);
|
||||
USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
if(buffer_empty(&USBResponseBuffer))
|
||||
{
|
||||
memcpy(WR_SLOT_PTR(USBResponseBuffer), DAPResponseBuffer, (uint16_t) _resp_len);
|
||||
USBResponseBuffer.wptr++;
|
||||
|
||||
usbd_edpt_xfer(_rhport, _in_ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len);
|
||||
} else {
|
||||
usbd_edpt_xfer(_rhport, _in_ep_addr, RD_SLOT_PTR(USBResponseBuffer), (uint16_t) _resp_len);
|
||||
} else {
|
||||
|
||||
memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len);
|
||||
USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
|
||||
// The In callback needs to check this flag to know when to queue up the next buffer.
|
||||
USBResponseBuffer.wasEmpty = false;
|
||||
}
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
// Suspend DAP thread until it is awoken by a USB thread callback
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
memcpy(WR_SLOT_PTR(USBResponseBuffer), DAPResponseBuffer, (uint16_t) _resp_len);
|
||||
USBResponseBuffer.wptr++;
|
||||
|
||||
// The In callback needs to check this flag to know when to queue up the next buffer.
|
||||
USBResponseBuffer.wasEmpty = false;
|
||||
}
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
// Suspend DAP thread until it is awoken by a USB thread callback
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
|
||||
} while (1);
|
||||
|
||||
} while (1);
|
||||
|
||||
}
|
||||
|
||||
usbd_class_driver_t const _dap_edpt_driver =
|
||||
{
|
||||
.init = dap_edpt_init,
|
||||
.reset = dap_edpt_reset,
|
||||
.open = dap_edpt_open,
|
||||
.control_xfer_cb = dap_edpt_control_xfer_cb,
|
||||
.xfer_cb = dap_edpt_xfer_cb,
|
||||
.sof = NULL,
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "PICOPROBE ENDPOINT"
|
||||
#endif
|
||||
.init = dap_edpt_init,
|
||||
.deinit = dap_edpt_deinit,
|
||||
.reset = dap_edpt_reset,
|
||||
.open = dap_edpt_open,
|
||||
.control_xfer_cb = dap_edpt_control_xfer_cb,
|
||||
.xfer_cb = dap_edpt_xfer_cb,
|
||||
.sof = NULL,
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "DAP ENDPOINT"
|
||||
#endif
|
||||
};
|
||||
|
||||
// Add the custom driver to the tinyUSB stack
|
||||
usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count)
|
||||
{
|
||||
*driver_count = 1;
|
||||
return &_dap_edpt_driver;
|
||||
*driver_count = 1;
|
||||
return &_dap_edpt_driver;
|
||||
}
|
||||
|
||||
bool buffer_full(buffer_t *buffer)
|
||||
{
|
||||
return ((buffer->packet_wr_idx + 1) % DAP_PACKET_COUNT == buffer->packet_rd_idx);
|
||||
}
|
||||
|
||||
bool buffer_empty(buffer_t *buffer)
|
||||
{
|
||||
return (buffer->packet_wr_idx == buffer->packet_rd_idx);
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "DAP_config.h"
|
||||
|
||||
#define PICOPROBE_INTERFACE_SUBCLASS 0x00
|
||||
#define PICOPROBE_INTERFACE_PROTOCOL 0x00
|
||||
#define DAP_INTERFACE_SUBCLASS 0x00
|
||||
#define DAP_INTERFACE_PROTOCOL 0x00
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[DAP_PACKET_COUNT][DAP_PACKET_SIZE];
|
||||
volatile uint32_t packet_wr_idx;
|
||||
volatile uint32_t packet_rd_idx;
|
||||
volatile bool wasEmpty;
|
||||
volatile bool wasFull;
|
||||
uint8_t data[DAP_PACKET_COUNT][DAP_PACKET_SIZE];
|
||||
volatile uint32_t wptr;
|
||||
volatile uint32_t rptr;
|
||||
volatile bool wasEmpty;
|
||||
volatile bool wasFull;
|
||||
} buffer_t;
|
||||
|
||||
extern TaskHandle_t dap_taskhandle, tud_taskhandle;
|
||||
@@ -29,13 +29,13 @@ extern TaskHandle_t dap_taskhandle, tud_taskhandle;
|
||||
void dap_thread(void *ptr);
|
||||
|
||||
/* Endpoint Handling */
|
||||
void picoprobe_edpt_init(void);
|
||||
uint16_t picoprobe_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool picoprobe_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
bool picoprobe_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void dap_edpt_init(void);
|
||||
uint16_t dap_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
bool dap_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
/* Helper Functions */
|
||||
bool buffer_full(buffer_t *buffer);
|
||||
bool buffer_empty(buffer_t *buffer);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "tusb.h"
|
||||
#include "get_serial.h"
|
||||
#include "picoprobe_config.h"
|
||||
#include "probe_config.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
@@ -36,7 +36,7 @@ tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
.bcdUSB = 0x0210, // USB Specification version 2.1 for BOS
|
||||
#else
|
||||
.bcdUSB = 0x0110,
|
||||
@@ -48,7 +48,7 @@ tusb_desc_device_t const desc_device =
|
||||
|
||||
.idVendor = 0x2E8A, // Pi
|
||||
.idProduct = 0x000c, // CMSIS-DAP Debug Probe
|
||||
.bcdDevice = 0x0103, // Version 01.03
|
||||
.bcdDevice = 0x0221, // Version 02.21
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
@@ -77,10 +77,10 @@ enum
|
||||
#define CDC_NOTIFICATION_EP_NUM 0x81
|
||||
#define CDC_DATA_OUT_EP_NUM 0x02
|
||||
#define CDC_DATA_IN_EP_NUM 0x83
|
||||
#define PROBE_OUT_EP_NUM 0x04
|
||||
#define PROBE_IN_EP_NUM 0x85
|
||||
#define DAP_OUT_EP_NUM 0x04
|
||||
#define DAP_IN_EP_NUM 0x85
|
||||
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
|
||||
#else
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN)
|
||||
@@ -97,19 +97,19 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
return desc_hid_report;
|
||||
}
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
uint8_t desc_configuration[] =
|
||||
{
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100),
|
||||
// Interface 0
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
// HID (named interface)
|
||||
TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_PROBE, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, CFG_TUD_HID_EP_BUFSIZE, 1),
|
||||
#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
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),
|
||||
#elif (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
// Bulk (named interface)
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 5, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64),
|
||||
#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 5, DAP_OUT_EP_NUM, DAP_IN_EP_NUM, 64),
|
||||
#elif (PROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
|
||||
// Bulk
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64),
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, DAP_OUT_EP_NUM, DAP_IN_EP_NUM, 64),
|
||||
#endif
|
||||
// Interface 1 + 2
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 6, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64),
|
||||
@@ -121,6 +121,8 @@ uint8_t const desc_configuration[] =
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
/* Hack in CAP_BREAK support */
|
||||
desc_configuration[CONFIG_TOTAL_LEN - TUD_CDC_DESC_LEN + 8 + 9 + 5 + 5 + 4 - 1] = 0x6;
|
||||
return desc_configuration;
|
||||
}
|
||||
|
||||
@@ -242,4 +244,4 @@ TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
|
||||
uint8_t const * tud_descriptor_bos_cb(void)
|
||||
{
|
||||
return desc_bos;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user