Compare commits
1 Commits
acm_break
...
pipeline-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c36b52049 |
@@ -5,11 +5,11 @@ include(pico_sdk_import.cmake)
|
||||
set(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/freertos)
|
||||
include(FreeRTOS_Kernel_import.cmake)
|
||||
|
||||
project(debugprobe)
|
||||
project(picoprobe)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(debugprobe
|
||||
add_executable(picoprobe
|
||||
src/led.c
|
||||
src/main.c
|
||||
src/usb_descriptors.c
|
||||
@@ -17,10 +17,9 @@ add_executable(debugprobe
|
||||
src/cdc_uart.c
|
||||
src/get_serial.c
|
||||
src/sw_dp_pio.c
|
||||
src/tusb_edpt_handler.c
|
||||
)
|
||||
|
||||
target_sources(debugprobe PRIVATE
|
||||
target_sources(picoprobe 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 +27,30 @@ target_sources(debugprobe PRIVATE
|
||||
#CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c
|
||||
)
|
||||
|
||||
target_include_directories(debugprobe PRIVATE
|
||||
target_include_directories(picoprobe PRIVATE
|
||||
CMSIS_5/CMSIS/DAP/Firmware/Include/
|
||||
CMSIS_5/CMSIS/Core/Include/
|
||||
include/
|
||||
)
|
||||
|
||||
target_compile_options(debugprobe PRIVATE -Wall)
|
||||
target_compile_options(picoprobe PRIVATE -Wall)
|
||||
|
||||
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)
|
||||
if (DEFINED ENV{PICOPROBE_LED})
|
||||
message("PICOPROBE_LED is defined as " $ENV{PICOPROBE_LED})
|
||||
target_compile_definitions(picoprobe PRIVATE PICOPROBE_LED=$ENV{PICOPROBE_LED})
|
||||
endif()
|
||||
|
||||
target_include_directories(debugprobe PRIVATE src)
|
||||
set(DBG_PIN_COUNT=4)
|
||||
|
||||
target_compile_definitions (debugprobe PRIVATE
|
||||
pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio)
|
||||
|
||||
target_include_directories(picoprobe PRIVATE src)
|
||||
|
||||
target_compile_definitions (picoprobe PRIVATE
|
||||
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
set_target_properties(debugprobe PROPERTIES
|
||||
OUTPUT_NAME "debugprobe_on_pico"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
target_link_libraries(debugprobe PRIVATE
|
||||
target_link_libraries(picoprobe PRIVATE
|
||||
pico_multicore
|
||||
pico_stdlib
|
||||
pico_unique_id
|
||||
@@ -67,6 +61,6 @@ target_link_libraries(debugprobe PRIVATE
|
||||
FreeRTOS-Kernel-Heap1
|
||||
)
|
||||
|
||||
pico_set_binary_type(debugprobe copy_to_ram)
|
||||
pico_set_binary_type(picoprobe copy_to_ram)
|
||||
|
||||
pico_add_extra_outputs(debugprobe)
|
||||
pico_add_extra_outputs(picoprobe)
|
||||
|
||||
54
README.md
54
README.md
@@ -1,53 +1,5 @@
|
||||
# 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/)
|
||||
|
||||
# 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.
|
||||
|
||||
# Documentation
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
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 `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 Pico, then you need to invoke `cmake` in the sequence above with the `DEBUG_ON_PICO=ON` option:
|
||||
```
|
||||
cmake -DDEBUG_ON_PICO=ON ..
|
||||
```
|
||||
|
||||
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 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.
|
||||
|
||||
|
||||
# TODO
|
||||
- AutoBaud selection, as PIO is a capable frequency counter
|
||||
- Possibly include RTT support
|
||||
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".
|
||||
|
||||
@@ -47,12 +47,12 @@ This information includes:
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "cmsis_compiler.h"
|
||||
#include "probe_config.h"
|
||||
#include "picoprobe_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.
|
||||
/* Debugprobe actually uses kHz rather than Hz, so just lie about it here */
|
||||
/* Picoprobe 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.
|
||||
@@ -62,7 +62,6 @@ This information includes:
|
||||
/// a Cortex-M0+ processor with high-speed peripheral I/O only 1 processor cycle might be
|
||||
/// required.
|
||||
#define IO_PORT_WRITE_CYCLES 1U ///< I/O Cycles: 2=default, 1=Cortex-M0+ fast I/0.
|
||||
#define DELAY_SLOW_CYCLES 1U // We don't differentiate between fast/slow, we've got a 16-bit divisor for that
|
||||
|
||||
/// Indicate that Serial Wire Debug (SWD) communication mode is available at the Debug Access Port.
|
||||
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
|
||||
@@ -95,7 +94,7 @@ This information includes:
|
||||
/// This configuration settings is used to optimize the communication performance with the
|
||||
/// debugger and depends on the USB peripheral. For devices with limited RAM or USB buffer the
|
||||
/// setting can be reduced (valid range is 1 .. 255).
|
||||
#define DAP_PACKET_COUNT 2U ///< Specifies number of packets buffered.
|
||||
#define DAP_PACKET_COUNT 3U ///< Specifies number of packets buffered.
|
||||
|
||||
/// Indicate that UART Serial Wire Output (SWO) trace is available.
|
||||
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
|
||||
@@ -460,11 +459,7 @@ __STATIC_FORCEINLINE void PIN_nTRST_OUT (uint32_t bit) {
|
||||
\return Current status of the nRESET DAP hardware I/O pin.
|
||||
*/
|
||||
__STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void) {
|
||||
#ifdef PROBE_PIN_RESET
|
||||
return probe_reset_level();
|
||||
#else
|
||||
return (0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** nRESET I/O pin: Set Output.
|
||||
@@ -473,11 +468,7 @@ __STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void) {
|
||||
- 1: release device hardware reset.
|
||||
*/
|
||||
__STATIC_FORCEINLINE void PIN_nRESET_OUT (uint32_t bit) {
|
||||
#ifdef PROBE_PIN_RESET
|
||||
probe_assert_reset(!!bit);
|
||||
#else
|
||||
(void) bit;
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
///@}
|
||||
@@ -501,22 +492,14 @@ It is recommended to provide the following LEDs for status indication:
|
||||
- 1: Connect LED ON: debugger is connected to CMSIS-DAP Debug Unit.
|
||||
- 0: Connect LED OFF: debugger is not connected to CMSIS-DAP Debug Unit.
|
||||
*/
|
||||
__STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit) {
|
||||
#ifdef DEBUGPROBE_DAP_CONNECTED_LED
|
||||
gpio_put(DEBUGPROBE_DAP_CONNECTED_LED, bit);
|
||||
#endif
|
||||
}
|
||||
__STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit) {}
|
||||
|
||||
/** Debug Unit: Set status Target Running LED.
|
||||
\param bit status of the Target Running LED.
|
||||
- 1: Target Running LED ON: program execution in target started.
|
||||
- 0: Target Running LED OFF: program execution in target stopped.
|
||||
*/
|
||||
__STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit) {
|
||||
#ifdef DEBUGPROBE_DAP_RUNNING_LED
|
||||
gpio_put(DEBUGPROBE_DAP_RUNNING_LED, bit);
|
||||
#endif
|
||||
}
|
||||
__STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit) {}
|
||||
|
||||
///@}
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BOARD_DEBUG_PROBE_H_
|
||||
#define BOARD_DEBUG_PROBE_H_
|
||||
|
||||
#define PROBE_IO_SWDI
|
||||
#define PROBE_CDC_UART
|
||||
// No reset pin
|
||||
|
||||
// PIO config
|
||||
#define PROBE_SM 0
|
||||
#define PROBE_PIN_OFFSET 12
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0)
|
||||
// For level-shifted input.
|
||||
#define PROBE_PIN_SWDI (PROBE_PIN_OFFSET + 1)
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 2)
|
||||
|
||||
// UART config
|
||||
#define PROBE_UART_TX 4
|
||||
#define PROBE_UART_RX 5
|
||||
#define PROBE_UART_INTERFACE uart1
|
||||
#define PROBE_UART_BAUDRATE 115200
|
||||
|
||||
#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)"
|
||||
|
||||
#endif
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BOARD_EXAMPLE_H_
|
||||
#define BOARD_EXAMPLE_H_
|
||||
#error "Example board configuration requested - specify PICO_BOARD and re-run CMake."
|
||||
|
||||
/* Select one of these. */
|
||||
/* Direct connection - SWCLK/SWDIO on two GPIOs */
|
||||
#define PROBE_IO_RAW
|
||||
/* SWCLK connected to a GPIO, SWDO driven from a GPIO, SWDI sampled via a level shifter */
|
||||
#define PROBE_IO_SWDI
|
||||
/* Level-shifted SWCLK, SWDIO with separate SWDO, SWDI and OE_N pin */
|
||||
#define PROBE_IO_OEN
|
||||
|
||||
/* 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
|
||||
|
||||
#define PROBE_SM 0
|
||||
#define PROBE_PIN_OFFSET 12
|
||||
/* PIO config for PROBE_IO_RAW */
|
||||
#if defined(PROBE_IO_RAW)
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0)
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1)
|
||||
#endif
|
||||
|
||||
/* PIO config for PROBE_IO_SWDI */
|
||||
#if defined(PROBE_IO_SWDI)
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0)
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1)
|
||||
#define PROBE_PIN_SWDI (PROBE_PIN_OFFSET + 2)
|
||||
#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)
|
||||
#define PROBE_PIN_SWDIOEN (PROBE_PIN_OFFSET + 0)
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 1)
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 2)
|
||||
#define PROBE_PIN_SWDI (PROBE_PIN_OFFSET + 3)
|
||||
#endif
|
||||
|
||||
#if defined(PROBE_CDC_UART)
|
||||
#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 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"
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,8 @@
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
# todo document
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
@@ -18,8 +20,8 @@ if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_P
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
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")
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
@@ -29,31 +31,20 @@ if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
GIT_SUBMODULES_RECURSE FALSE
|
||||
)
|
||||
else ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
endif ()
|
||||
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
message("Downloading PICO SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
"PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
@@ -65,9 +56,9 @@ endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
/* Memory allocation related definitions. */
|
||||
#define configSUPPORT_STATIC_ALLOCATION 0
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
#define configTOTAL_HEAP_SIZE (64*1024)
|
||||
#define configTOTAL_HEAP_SIZE (128*1024)
|
||||
#define configAPPLICATION_ALLOCATED_HEAP 0
|
||||
|
||||
/* Hook function related definitions. */
|
||||
|
||||
210
src/cdc_uart.c
210
src/cdc_uart.c
@@ -29,69 +29,30 @@
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "probe_config.h"
|
||||
#include "picoprobe_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];
|
||||
static uint8_t rx_buf[32];
|
||||
// Actually s^-1 so 25ms
|
||||
#define DEBOUNCE_MS 40
|
||||
static uint debounce_ticks = 5;
|
||||
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
static volatile uint tx_led_debounce;
|
||||
#endif
|
||||
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
static uint rx_led_debounce;
|
||||
#endif
|
||||
static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE];
|
||||
static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE];
|
||||
|
||||
void cdc_uart_init(void) {
|
||||
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_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
|
||||
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);
|
||||
}
|
||||
|
||||
bool cdc_task(void)
|
||||
void 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(PROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) {
|
||||
rx_buf[rx_len++] = uart_getc(PROBE_UART_INTERFACE);
|
||||
while(uart_is_readable(PICOPROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) {
|
||||
rx_buf[rx_len++] = uart_getc(PICOPROBE_UART_INTERFACE);
|
||||
}
|
||||
|
||||
if (tud_cdc_connected()) {
|
||||
@@ -100,193 +61,66 @@ bool 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 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);
|
||||
if (rx_len > written)
|
||||
cdc_tx_oe++;
|
||||
|
||||
if (written > 0) {
|
||||
tud_cdc_write(rx_buf, written);
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
} else {
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
if (rx_led_debounce)
|
||||
rx_led_debounce--;
|
||||
else
|
||||
gpio_put(PROBE_UART_RX_LED, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Reading from a firehose and writing to a FIFO. */
|
||||
size_t watermark = MIN(tud_cdc_available(), sizeof(tx_buf));
|
||||
if (watermark > 0) {
|
||||
size_t tx_len;
|
||||
#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(PROBE_UART_INTERFACE, tx_buf, tx_len);
|
||||
} else {
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
if (tx_led_debounce)
|
||||
tx_led_debounce--;
|
||||
else
|
||||
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);
|
||||
tx_led_debounce = 0;
|
||||
} else {
|
||||
keep_alive = true;
|
||||
}
|
||||
uart_write_blocking(PICOPROBE_UART_INTERFACE, tx_buf, tx_len);
|
||||
}
|
||||
} else if (was_connected) {
|
||||
tud_cdc_write_clear();
|
||||
uart_set_break(PROBE_UART_INTERFACE, false);
|
||||
timed_break = false;
|
||||
was_connected = 0;
|
||||
tx_led_debounce = 0;
|
||||
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) {
|
||||
keep_alive = cdc_task();
|
||||
if (!keep_alive) {
|
||||
delayed = xTaskDelayUntil(&last_wake, interval);
|
||||
if (delayed == pdFALSE)
|
||||
last_wake = xTaskGetTickCount();
|
||||
}
|
||||
cdc_task();
|
||||
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);
|
||||
interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ));
|
||||
debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS));
|
||||
probe_info("New baud rate %ld micros %ld interval %lu\n",
|
||||
picoprobe_info("New baud rate %d micros %d interval %u\n",
|
||||
line_coding->bit_rate, micros, interval);
|
||||
uart_deinit(PROBE_UART_INTERFACE);
|
||||
uart_deinit(PICOPROBE_UART_INTERFACE);
|
||||
tud_cdc_write_clear();
|
||||
tud_cdc_read_flush();
|
||||
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);
|
||||
uart_init(PICOPROBE_UART_INTERFACE, line_coding->bit_rate);
|
||||
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 && !rts)
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
gpio_put(PROBE_UART_RX_LED, 0);
|
||||
rx_led_debounce = 0;
|
||||
#endif
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
gpio_put(PROBE_UART_TX_LED, 0);
|
||||
tx_led_debounce = 0;
|
||||
#endif
|
||||
} else
|
||||
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;
|
||||
break;
|
||||
case 0xffff:
|
||||
uart_set_break(PROBE_UART_INTERFACE, true);
|
||||
timed_break = false;
|
||||
gpio_put(PROBE_UART_TX_LED, 1);
|
||||
tx_led_debounce = 1 << 30;
|
||||
break;
|
||||
default:
|
||||
uart_set_break(PROBE_UART_INTERFACE, true);
|
||||
timed_break = true;
|
||||
gpio_put(PROBE_UART_TX_LED, 1);
|
||||
break_expiry = xTaskGetTickCount() + (wValue * (configTICK_RATE_HZ / 1000));
|
||||
tx_led_debounce = 1 << 30;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
void cdc_thread(void *ptr);
|
||||
void cdc_uart_init(void);
|
||||
bool cdc_task(void);
|
||||
void cdc_task(void);
|
||||
|
||||
extern TaskHandle_t uart_taskhandle;
|
||||
|
||||
|
||||
51
src/led.c
51
src/led.c
@@ -26,27 +26,36 @@
|
||||
#include <pico/stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "probe_config.h"
|
||||
#include "picoprobe_config.h"
|
||||
|
||||
#define LED_COUNT_SHIFT 14
|
||||
#define LED_COUNT_MAX 5 * (1 << LED_COUNT_SHIFT)
|
||||
|
||||
static uint32_t led_count;
|
||||
|
||||
void led_init(void) {
|
||||
#ifdef PROBE_USB_CONNECTED_LED
|
||||
gpio_init(PROBE_USB_CONNECTED_LED);
|
||||
gpio_set_dir(PROBE_USB_CONNECTED_LED, GPIO_OUT);
|
||||
#endif
|
||||
#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
|
||||
#ifdef PROBE_UART_RX_LED
|
||||
gpio_init(PROBE_UART_RX_LED);
|
||||
gpio_set_dir(PROBE_UART_RX_LED, GPIO_OUT);
|
||||
#endif
|
||||
#ifdef PROBE_UART_TX_LED
|
||||
gpio_init(PROBE_UART_TX_LED);
|
||||
gpio_set_dir(PROBE_UART_TX_LED, GPIO_OUT);
|
||||
#endif
|
||||
led_count = 0;
|
||||
|
||||
gpio_init(PICOPROBE_LED);
|
||||
gpio_set_dir(PICOPROBE_LED, GPIO_OUT);
|
||||
gpio_put(PICOPROBE_LED, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void led_task(void) {
|
||||
if (led_count != 0) {
|
||||
--led_count;
|
||||
gpio_put(PICOPROBE_LED, !((led_count >> LED_COUNT_SHIFT) & 1));
|
||||
}
|
||||
}
|
||||
|
||||
void led_signal_activity(uint total_bits) {
|
||||
if (led_count == 0) {
|
||||
gpio_put(PICOPROBE_LED, 0);
|
||||
}
|
||||
|
||||
if (led_count < LED_COUNT_MAX) {
|
||||
led_count += total_bits;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,5 +27,7 @@
|
||||
#define LED_H
|
||||
|
||||
void led_init(void);
|
||||
void led_task(void);
|
||||
void led_signal_activity(uint total_bits);
|
||||
|
||||
#endif
|
||||
|
||||
81
src/main.c
81
src/main.c
@@ -32,21 +32,20 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "probe_config.h"
|
||||
#include "probe.h"
|
||||
#include "tusb.h"
|
||||
#include "time.h"
|
||||
#include "cdc_uart.h"
|
||||
#include "get_serial.h"
|
||||
#include "led.h"
|
||||
#include "tusb_edpt_handler.h"
|
||||
#include "DAP.h"
|
||||
#include "DAP_config.h"
|
||||
|
||||
// UART0 for debugprobe debug
|
||||
// UART1 for debugprobe to target device
|
||||
// UART0 for Picoprobe debug
|
||||
// UART1 for picoprobe to target device
|
||||
|
||||
static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
|
||||
static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
|
||||
static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE * DAP_PACKET_COUNT];
|
||||
static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE * DAP_PACKET_COUNT];
|
||||
|
||||
#define THREADED 1
|
||||
|
||||
@@ -54,44 +53,61 @@ 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;
|
||||
static TaskHandle_t dap_taskhandle, tud_taskhandle;
|
||||
|
||||
void usb_thread(void *ptr)
|
||||
{
|
||||
TickType_t wake;
|
||||
wake = xTaskGetTickCount();
|
||||
do {
|
||||
tud_task();
|
||||
#ifdef PROBE_USB_CONNECTED_LED
|
||||
if (!gpio_get(PROBE_USB_CONNECTED_LED) && tud_ready())
|
||||
gpio_put(PROBE_USB_CONNECTED_LED, 1);
|
||||
else
|
||||
gpio_put(PROBE_USB_CONNECTED_LED, 0);
|
||||
#endif
|
||||
// Go to sleep for up to a tick if nothing to do
|
||||
if (!tud_task_event_ready())
|
||||
xTaskDelayUntil(&wake, 1);
|
||||
// Trivial delay to save power
|
||||
vTaskDelay(1);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
// Workaround API change in 0.13
|
||||
#if (TUSB_VERSION_MAJOR == 0) && (TUSB_VERSION_MINOR <= 12)
|
||||
#define tud_vendor_flush(x) ((void)0)
|
||||
#endif
|
||||
void dap_thread(void *ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
int32_t packet_len;
|
||||
uint16_t req_len, resp_len;
|
||||
uint8_t *rx_ptr;
|
||||
|
||||
do {
|
||||
while (tud_vendor_available()) {
|
||||
rx_ptr = &RxDataBuffer[0];
|
||||
packet_len = tud_vendor_read(rx_ptr, sizeof(RxDataBuffer));
|
||||
picoprobe_debug("Got chunk %u\n", packet_len);
|
||||
do {
|
||||
ret = DAP_ProcessCommand(rx_ptr, TxDataBuffer);
|
||||
resp_len = ret & 0xffff;
|
||||
req_len = ret >> 16;
|
||||
tud_vendor_write(TxDataBuffer, resp_len);
|
||||
rx_ptr += req_len;
|
||||
packet_len -= req_len;
|
||||
picoprobe_debug("Packet decode remaining %u req %u resp %u\n",
|
||||
packet_len, req_len, resp_len);
|
||||
} while (packet_len > 0);
|
||||
}
|
||||
// Trivial delay to save power
|
||||
vTaskDelay(1);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
uint32_t resp_len;
|
||||
|
||||
board_init();
|
||||
usb_serial_init();
|
||||
cdc_uart_init();
|
||||
tusb_init();
|
||||
stdio_uart_init();
|
||||
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
|
||||
probe_gpio_init();
|
||||
probe_init();
|
||||
#else
|
||||
DAP_Setup();
|
||||
|
||||
#endif
|
||||
led_init();
|
||||
|
||||
probe_info("Welcome to debugprobe!\n");
|
||||
picoprobe_info("Welcome to Picoprobe!\n");
|
||||
|
||||
if (THREADED) {
|
||||
/* UART needs to preempt USB as if we don't, characters get lost */
|
||||
@@ -105,10 +121,11 @@ int main(void) {
|
||||
while (!THREADED) {
|
||||
tud_task();
|
||||
cdc_task();
|
||||
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
|
||||
probe_task();
|
||||
led_task();
|
||||
#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
if (tud_vendor_available()) {
|
||||
uint32_t resp_len;
|
||||
tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
|
||||
resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
|
||||
tud_vendor_write(TxDataBuffer, resp_len);
|
||||
@@ -145,7 +162,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 (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
#if (PICOPROBE_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)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) 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
|
||||
@@ -23,30 +23,64 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BOARD_PICO_H_
|
||||
#define BOARD_PICO_H_
|
||||
#ifndef PICOPROBE_H_
|
||||
#define PICOPROBE_H_
|
||||
|
||||
#if false
|
||||
#define picoprobe_info(format,args...) printf(format, ## args)
|
||||
#else
|
||||
#define picoprobe_info(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
#if false
|
||||
#define picoprobe_debug(format,args...) printf(format, ## args)
|
||||
#else
|
||||
#define picoprobe_debug(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if false
|
||||
#define picoprobe_dump(format,args...) printf(format, ## args)
|
||||
#else
|
||||
#define picoprobe_dump(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define PROBE_IO_RAW
|
||||
#define PROBE_CDC_UART
|
||||
|
||||
// PIO config
|
||||
#define PROBE_SM 0
|
||||
#define PROBE_PIN_OFFSET 2
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) // 2
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1) // 3
|
||||
|
||||
// Target reset config
|
||||
#if false
|
||||
#define PROBE_PIN_RESET 1
|
||||
#endif
|
||||
#define PROBE_PIN_RESET 6
|
||||
|
||||
// UART config
|
||||
#define PROBE_UART_TX 4
|
||||
#define PROBE_UART_RX 5
|
||||
#define PROBE_UART_INTERFACE uart1
|
||||
#define PROBE_UART_BAUDRATE 115200
|
||||
#define PICOPROBE_UART_TX 4
|
||||
#define PICOPROBE_UART_RX 5
|
||||
#define PICOPROBE_UART_INTERFACE uart1
|
||||
#define PICOPROBE_UART_BAUDRATE 115200
|
||||
|
||||
#define PROBE_USB_CONNECTED_LED 25
|
||||
// LED config
|
||||
#ifndef PICOPROBE_LED
|
||||
|
||||
#define PROBE_PRODUCT_STRING "Debugprobe on Pico (CMSIS-DAP)"
|
||||
#ifndef PICO_DEFAULT_LED_PIN
|
||||
#error PICO_DEFAULT_LED_PIN is not defined, run PICOPROBE_LED=<led_pin> cmake
|
||||
#elif PICO_DEFAULT_LED_PIN == -1
|
||||
#error PICO_DEFAULT_LED_PIN is defined as -1, run PICOPROBE_LED=<led_pin> cmake
|
||||
#else
|
||||
#define PICOPROBE_LED PICO_DEFAULT_LED_PIN
|
||||
#endif
|
||||
|
||||
#define PROTO_OPENOCD_CUSTOM 0
|
||||
#define PROTO_DAP_V1 1
|
||||
#define PROTO_DAP_V2 2
|
||||
|
||||
// Interface config
|
||||
#ifndef PICOPROBE_DEBUG_PROTOCOL
|
||||
#define PICOPROBE_DEBUG_PROTOCOL PROTO_DAP_V2
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
257
src/probe.c
257
src/probe.c
@@ -31,7 +31,7 @@
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "led.h"
|
||||
#include "probe_config.h"
|
||||
#include "picoprobe_config.h"
|
||||
#include "probe.pio.h"
|
||||
#include "tusb.h"
|
||||
|
||||
@@ -52,6 +52,15 @@ CU_REGISTER_DEBUG_PINS(probe_timing)
|
||||
|
||||
#define PROBE_BUF_SIZE 8192
|
||||
struct _probe {
|
||||
// Total length
|
||||
uint tx_len;
|
||||
// Data back to host
|
||||
uint8_t tx_buf[PROBE_BUF_SIZE];
|
||||
|
||||
// CMD / Data RX'd from
|
||||
uint rx_len;
|
||||
uint8_t rx_buf[PROBE_BUF_SIZE];
|
||||
|
||||
// PIO offset
|
||||
uint offset;
|
||||
uint initted;
|
||||
@@ -59,120 +68,248 @@ struct _probe {
|
||||
|
||||
static struct _probe probe;
|
||||
|
||||
enum PROBE_CMDS {
|
||||
PROBE_INVALID = 0, // Invalid command
|
||||
PROBE_WRITE_BITS = 1, // Host wants us to write bits
|
||||
PROBE_READ_BITS = 2, // Host wants us to read bits
|
||||
PROBE_SET_FREQ = 3, // Set TCK
|
||||
PROBE_RESET = 4, // Reset all state
|
||||
PROBE_TARGET_RESET = 5, // Reset target
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) probe_cmd_hdr {
|
||||
uint8_t id;
|
||||
uint8_t cmd;
|
||||
uint32_t bits;
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) probe_pkt_hdr {
|
||||
uint32_t total_packet_length;
|
||||
};
|
||||
|
||||
void probe_set_swclk_freq(uint freq_khz) {
|
||||
uint clk_sys_freq_khz = clock_get_hz(clk_sys) / 1000;
|
||||
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;
|
||||
picoprobe_info("Set swclk freq %dKHz sysclk %dkHz\n", freq_khz, clk_sys_freq_khz);
|
||||
// Worked out with saleae
|
||||
uint32_t divider = clk_sys_freq_khz / freq_khz / 2;
|
||||
pio_sm_set_clkdiv_int_frac(pio0, PROBE_SM, divider, 0);
|
||||
}
|
||||
|
||||
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 == 0 ? GPIO_OUT : GPIO_IN);
|
||||
#endif
|
||||
}
|
||||
|
||||
int probe_reset_level(void)
|
||||
{
|
||||
#if defined(PROBE_PIN_RESET)
|
||||
return gpio_get(PROBE_PIN_RESET);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef enum probe_pio_command {
|
||||
CMD_WRITE = 0,
|
||||
CMD_SKIP,
|
||||
CMD_TURNAROUND,
|
||||
CMD_READ
|
||||
} probe_pio_command_t;
|
||||
|
||||
static inline uint32_t fmt_probe_command(uint bit_count, bool out_en, probe_pio_command_t cmd) {
|
||||
uint cmd_addr =
|
||||
cmd == CMD_WRITE ? probe.offset + probe_offset_write_cmd :
|
||||
cmd == CMD_SKIP ? probe.offset + probe_offset_get_next_cmd :
|
||||
cmd == CMD_TURNAROUND ? probe.offset + probe_offset_turnaround_cmd :
|
||||
probe.offset + probe_offset_read_cmd;
|
||||
return ((bit_count - 1) & 0xff) | ((uint)out_en << 8) | (cmd_addr << 9);
|
||||
gpio_set_dir(PROBE_PIN_RESET, state);
|
||||
}
|
||||
|
||||
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, bit_count - 1);
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, 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_SET(probe_timing, DBG_PIN_WRITE_WAIT);
|
||||
picoprobe_dump("Write %d bits 0x%x\n", bit_count, data_byte);
|
||||
// Wait for pio to push garbage to rx fifo so we know it has finished sending
|
||||
pio_sm_get_blocking(pio0, PROBE_SM);
|
||||
DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE_WAIT);
|
||||
DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE);
|
||||
}
|
||||
|
||||
void probe_hiz_clocks(uint bit_count) {
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, false, CMD_TURNAROUND));
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, 0);
|
||||
}
|
||||
|
||||
uint32_t probe_read_bits(uint bit_count) {
|
||||
DEBUG_PINS_SET(probe_timing, DBG_PIN_READ);
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, false, CMD_READ));
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1);
|
||||
uint32_t data = pio_sm_get_blocking(pio0, PROBE_SM);
|
||||
uint32_t data_shifted = data;
|
||||
if (bit_count < 32) {
|
||||
data_shifted = data >> (32 - bit_count);
|
||||
}
|
||||
|
||||
probe_dump("Read %d bits 0x%x (shifted 0x%x)\n", bit_count, data, data_shifted);
|
||||
picoprobe_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;
|
||||
}
|
||||
|
||||
static void probe_wait_idle() {
|
||||
pio0->fdebug = 1u << (PIO_FDEBUG_TXSTALL_LSB + PROBE_SM);
|
||||
while (!(pio0->fdebug & (1u << (PIO_FDEBUG_TXSTALL_LSB + PROBE_SM))))
|
||||
;
|
||||
}
|
||||
|
||||
void probe_read_mode(void) {
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(0, false, CMD_SKIP));
|
||||
probe_wait_idle();
|
||||
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_in_posedge));
|
||||
while(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO));
|
||||
}
|
||||
|
||||
void probe_write_mode(void) {
|
||||
pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(0, true, CMD_SKIP));
|
||||
probe_wait_idle();
|
||||
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_out_negedge));
|
||||
while(!(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO)));
|
||||
}
|
||||
|
||||
void probe_gpio_init()
|
||||
{
|
||||
// Funcsel pins
|
||||
pio_gpio_init(pio0, PROBE_PIN_SWCLK);
|
||||
pio_gpio_init(pio0, PROBE_PIN_SWDIO);
|
||||
// Make sure SWDIO has a pullup on it. Idle state is high
|
||||
gpio_pull_up(PROBE_PIN_SWDIO);
|
||||
}
|
||||
|
||||
void probe_init() {
|
||||
// Target reset pin: pull up, input to emulate open drain pin
|
||||
gpio_pull_up(PROBE_PIN_RESET);
|
||||
// gpio_init will leave the pin cleared and set as input
|
||||
gpio_init(PROBE_PIN_RESET);
|
||||
if (!probe.initted) {
|
||||
uint offset = pio_add_program(pio0, &probe_program);
|
||||
probe.offset = offset;
|
||||
|
||||
pio_sm_config sm_config = probe_program_get_default_config(offset);
|
||||
probe_sm_init(&sm_config);
|
||||
|
||||
// Set SWCLK as a sideset pin
|
||||
sm_config_set_sideset_pins(&sm_config, PROBE_PIN_SWCLK);
|
||||
|
||||
// Set SWDIO offset
|
||||
sm_config_set_out_pins(&sm_config, PROBE_PIN_SWDIO, 1);
|
||||
sm_config_set_set_pins(&sm_config, PROBE_PIN_SWDIO, 1);
|
||||
sm_config_set_in_pins(&sm_config, PROBE_PIN_SWDIO);
|
||||
|
||||
// Set SWD and SWDIO pins as output to start. This will be set in the sm
|
||||
pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 2, true);
|
||||
|
||||
// shift output right, autopull off, autopull threshold
|
||||
sm_config_set_out_shift(&sm_config, true, false, 0);
|
||||
// shift input right as swd data is lsb first, autopush off
|
||||
sm_config_set_in_shift(&sm_config, true, false, 0);
|
||||
|
||||
// Init SM with config
|
||||
pio_sm_init(pio0, PROBE_SM, offset, &sm_config);
|
||||
|
||||
// Set up divisor
|
||||
probe_set_swclk_freq(1000);
|
||||
|
||||
// Jump SM to command dispatch routine, and enable it
|
||||
pio_sm_exec(pio0, PROBE_SM, offset + probe_offset_get_next_cmd);
|
||||
// Enable SM
|
||||
pio_sm_set_enabled(pio0, PROBE_SM, 1);
|
||||
probe.initted = 1;
|
||||
}
|
||||
|
||||
// Jump to write program
|
||||
probe_write_mode();
|
||||
}
|
||||
|
||||
void probe_deinit(void)
|
||||
{
|
||||
probe_read_mode();
|
||||
if (probe.initted) {
|
||||
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.initted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void probe_handle_read(uint total_bits) {
|
||||
picoprobe_debug("Read %d bits\n", total_bits);
|
||||
probe_read_mode();
|
||||
|
||||
uint chunk;
|
||||
uint bits = total_bits;
|
||||
while (bits > 0) {
|
||||
if (bits > 8) {
|
||||
chunk = 8;
|
||||
} else {
|
||||
chunk = bits;
|
||||
}
|
||||
probe.tx_buf[probe.tx_len] = (uint8_t)probe_read_bits(chunk);
|
||||
probe.tx_len++;
|
||||
// Decrement remaining bits
|
||||
bits -= chunk;
|
||||
}
|
||||
}
|
||||
|
||||
void probe_handle_write(uint8_t *data, uint total_bits) {
|
||||
picoprobe_debug("Write %d bits\n", total_bits);
|
||||
|
||||
led_signal_activity(total_bits);
|
||||
|
||||
probe_write_mode();
|
||||
|
||||
uint chunk;
|
||||
uint bits = total_bits;
|
||||
while (bits > 0) {
|
||||
if (bits > 8) {
|
||||
chunk = 8;
|
||||
} else {
|
||||
chunk = bits;
|
||||
}
|
||||
|
||||
probe_write_bits(chunk, (uint32_t)*data++);
|
||||
bits -= chunk;
|
||||
}
|
||||
}
|
||||
|
||||
void probe_prepare_read_header(struct probe_cmd_hdr *hdr) {
|
||||
// We have a read so need to prefix the data with the cmd header
|
||||
if (probe.tx_len == 0) {
|
||||
// Reserve some space for probe_pkt_hdr
|
||||
probe.tx_len += sizeof(struct probe_pkt_hdr);
|
||||
}
|
||||
|
||||
memcpy((void*)&probe.tx_buf[probe.tx_len], hdr, sizeof(struct probe_cmd_hdr));
|
||||
probe.tx_len += sizeof(struct probe_cmd_hdr);
|
||||
}
|
||||
|
||||
void probe_handle_pkt(void) {
|
||||
uint8_t *pkt = &probe.rx_buf[0] + sizeof(struct probe_pkt_hdr);
|
||||
uint remaining = probe.rx_len - sizeof(struct probe_pkt_hdr);
|
||||
|
||||
DEBUG_PINS_SET(probe_timing, DBG_PIN_PKT);
|
||||
|
||||
picoprobe_debug("Processing packet of length %d\n", probe.rx_len);
|
||||
|
||||
probe.tx_len = 0;
|
||||
while (remaining) {
|
||||
struct probe_cmd_hdr *hdr = (struct probe_cmd_hdr*)pkt;
|
||||
uint data_bytes = DIV_ROUND_UP(hdr->bits, 8);
|
||||
pkt += sizeof(struct probe_cmd_hdr);
|
||||
remaining -= sizeof(struct probe_cmd_hdr);
|
||||
|
||||
if (hdr->cmd == PROBE_WRITE_BITS) {
|
||||
uint8_t *data = pkt;
|
||||
probe_handle_write(data, hdr->bits);
|
||||
pkt += data_bytes;
|
||||
remaining -= data_bytes;
|
||||
} else if (hdr->cmd == PROBE_READ_BITS) {
|
||||
probe_prepare_read_header(hdr);
|
||||
probe_handle_read(hdr->bits);
|
||||
} else if (hdr->cmd == PROBE_SET_FREQ) {
|
||||
probe_set_swclk_freq(hdr->bits);
|
||||
} else if (hdr->cmd == PROBE_RESET) {
|
||||
// TODO: Is there anything to do after a reset?
|
||||
// tx len and rx len should already be 0
|
||||
;
|
||||
} else if (hdr->cmd == PROBE_TARGET_RESET) {
|
||||
probe_assert_reset(hdr->bits);
|
||||
}
|
||||
}
|
||||
probe.rx_len = 0;
|
||||
|
||||
if (probe.tx_len) {
|
||||
// Fill in total packet length before sending
|
||||
struct probe_pkt_hdr *tx_hdr = (struct probe_pkt_hdr*)&probe.tx_buf[0];
|
||||
tx_hdr->total_packet_length = probe.tx_len;
|
||||
tud_vendor_write(&probe.tx_buf[0], probe.tx_len);
|
||||
picoprobe_debug("Picoprobe wrote %d response bytes\n", probe.tx_len);
|
||||
}
|
||||
probe.tx_len = 0;
|
||||
|
||||
DEBUG_PINS_CLR(probe_timing, DBG_PIN_PKT);
|
||||
}
|
||||
|
||||
// USB bits
|
||||
void probe_task(void) {
|
||||
if ( tud_vendor_available() ) {
|
||||
uint count = tud_vendor_read(&probe.rx_buf[probe.rx_len], 64);
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
probe.rx_len += count;
|
||||
}
|
||||
|
||||
if (probe.rx_len >= sizeof(struct probe_pkt_hdr)) {
|
||||
struct probe_pkt_hdr *pkt_hdr = (struct probe_pkt_hdr*)&probe.rx_buf[0];
|
||||
if (pkt_hdr->total_packet_length == probe.rx_len) {
|
||||
probe_handle_pkt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
src/probe.h
18
src/probe.h
@@ -26,27 +26,19 @@
|
||||
#ifndef PROBE_H_
|
||||
#define PROBE_H_
|
||||
|
||||
#if defined(PROBE_IO_RAW) || defined(PROBE_IO_SWDI)
|
||||
#include "probe.pio.h"
|
||||
#endif
|
||||
|
||||
#if defined(PROBE_IO_OEN)
|
||||
#include "probe_oen.pio.h"
|
||||
#endif
|
||||
|
||||
void probe_set_swclk_freq(uint freq_khz);
|
||||
|
||||
// Bit counts in the range 1..256
|
||||
void probe_write_bits(uint bit_count, uint32_t data_byte);
|
||||
uint32_t probe_read_bits(uint bit_count);
|
||||
void probe_hiz_clocks(uint bit_count);
|
||||
|
||||
void probe_read_mode(void);
|
||||
void probe_write_mode(void);
|
||||
|
||||
void probe_handle_read(uint total_bits);
|
||||
void probe_handle_write(uint8_t *data, uint total_bits);
|
||||
|
||||
void probe_task(void);
|
||||
void probe_gpio_init(void);
|
||||
void probe_init(void);
|
||||
void probe_deinit(void);
|
||||
void probe_assert_reset(bool state);
|
||||
int probe_reset_level(void);
|
||||
|
||||
#endif
|
||||
|
||||
106
src/probe.pio
106
src/probe.pio
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021-2023 Raspberry Pi (Trading) Ltd.
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) 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
|
||||
@@ -23,91 +23,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
// Every TX FIFO entry is either a command, or up to 32 bits of data.
|
||||
// Command format:
|
||||
//
|
||||
// | 13:9 | 8 | 7:0 |
|
||||
// | Cmd | Dir | Count |
|
||||
//
|
||||
// Count is the number of bits to be transferred by this command, minus 1.
|
||||
// Dir is the output enable for the SWDIO pin.
|
||||
// Cmd is the address of the write_cmd, read_cmd or get_next_cmd label.
|
||||
//
|
||||
// write_cmd expects a FIFO data entry, but read_cmd does not.
|
||||
//
|
||||
// read_cmd pushes data to the FIFO, but write_cmd does not. (The lack of RX
|
||||
// garbage on writes allows the interface code to return early after pushing a
|
||||
// write command, as there is no need in general to poll for a command's
|
||||
// completion as long as all commands are executed in order.)
|
||||
//
|
||||
// The SWCLK period is 4 PIO SM execution cycles.
|
||||
|
||||
.program probe
|
||||
.side_set 1 opt
|
||||
|
||||
public write_cmd:
|
||||
public turnaround_cmd: ; Alias of write, used for probe_oen.pio
|
||||
pull
|
||||
write_bitloop:
|
||||
out pins, 1 [1] side 0x0 ; Data is output by host on negedge
|
||||
jmp x-- write_bitloop [1] side 0x1 ; ...and captured by target on posedge
|
||||
; Fall through to next command
|
||||
.wrap_target
|
||||
public get_next_cmd:
|
||||
pull side 0x0 ; SWCLK is initially low
|
||||
out x, 8 ; Get bit count
|
||||
out pindirs, 1 ; Set SWDIO direction
|
||||
out pc, 5 ; Go to command routine
|
||||
public out_negedge:
|
||||
set pindirs, 1 side 0x0 ; Init OE clock 0
|
||||
pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register
|
||||
mov x, osr ; mov bits to shift -1 from output shift register into x
|
||||
pull ; Pull data to shift out
|
||||
out_negedge_bitloop:
|
||||
out pins, 1 side 0x0 ; clock data out on falling edge
|
||||
jmp x-- out_negedge_bitloop side 0x1 ; data is present for posedge
|
||||
set pins, 1 side 0x0 ; Drive data high (idle bus state)
|
||||
push ; Push to rx fifo just so processor knows when done
|
||||
jmp out_negedge ; Wait for next transaction
|
||||
|
||||
read_bitloop:
|
||||
nop ; Additional delay on taken loop branch
|
||||
public read_cmd:
|
||||
in pins, 1 [1] side 0x1 ; Data is captured by host on posedge
|
||||
jmp x-- read_bitloop side 0x0
|
||||
push
|
||||
.wrap ; Wrap to next command
|
||||
|
||||
|
||||
; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc
|
||||
% c-sdk {
|
||||
|
||||
static inline void probe_gpio_init()
|
||||
{
|
||||
#if defined(PROBE_PIN_RESET)
|
||||
// Target reset pin: pull up, input to emulate open drain pin
|
||||
gpio_pull_up(PROBE_PIN_RESET);
|
||||
// gpio_init will leave the pin cleared and set as input
|
||||
gpio_init(PROBE_PIN_RESET);
|
||||
#endif
|
||||
// Funcsel pins
|
||||
pio_gpio_init(pio0, PROBE_PIN_SWCLK);
|
||||
pio_gpio_init(pio0, PROBE_PIN_SWDIO);
|
||||
// Make sure SWDIO has a pullup on it. Idle state is high
|
||||
gpio_pull_up(PROBE_PIN_SWDIO);
|
||||
}
|
||||
|
||||
static inline void probe_sm_init(pio_sm_config* sm_config) {
|
||||
|
||||
// Set SWCLK as a sideset pin
|
||||
sm_config_set_sideset_pins(sm_config, PROBE_PIN_SWCLK);
|
||||
|
||||
// Set SWDIO offset
|
||||
sm_config_set_out_pins(sm_config, PROBE_PIN_SWDIO, 1);
|
||||
sm_config_set_set_pins(sm_config, PROBE_PIN_SWDIO, 1);
|
||||
#ifdef PROBE_IO_SWDI
|
||||
sm_config_set_in_pins(sm_config, PROBE_PIN_SWDI);
|
||||
#else
|
||||
sm_config_set_in_pins(sm_config, PROBE_PIN_SWDIO);
|
||||
#endif
|
||||
|
||||
|
||||
// Set SWD and SWDIO pins as output to start. This will be set in the sm
|
||||
pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 2, true);
|
||||
|
||||
// shift output right, autopull off, autopull threshold
|
||||
sm_config_set_out_shift(sm_config, true, false, 0);
|
||||
// shift input right as swd data is lsb first, autopush off
|
||||
sm_config_set_in_shift(sm_config, true, false, 0);
|
||||
}
|
||||
|
||||
%}
|
||||
public in_posedge:
|
||||
set pindirs, 0 side 0x0 ; INIT IE clock 0
|
||||
pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register
|
||||
mov x, osr ; mov bits to shift -1 from output shift register into x into x
|
||||
in_posedge_bitloop:
|
||||
in pins, 1 side 0x1 ; Generate posedge and read data
|
||||
jmp x-- in_posedge_bitloop side 0x0 ;
|
||||
push ; Push to rx fifo when done
|
||||
jmp in_posedge ; Jump back to start
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROBE_CONFIG_H_
|
||||
#define PROBE_CONFIG_H_
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#if false
|
||||
#define probe_info(format,args...) \
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define probe_info(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
#if false
|
||||
#define probe_debug(format,args...) \
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define probe_debug(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if false
|
||||
#define probe_dump(format,args...)\
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define probe_dump(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
// TODO tie this up with PICO_BOARD defines in the main SDK
|
||||
|
||||
#ifdef DEBUG_ON_PICO
|
||||
#include "board_pico_config.h"
|
||||
#else
|
||||
#include "board_debug_probe_config.h"
|
||||
#endif
|
||||
//#include "board_example_config.h"
|
||||
|
||||
|
||||
#define PROTO_DAP_V1 1
|
||||
#define PROTO_DAP_V2 2
|
||||
|
||||
// Interface config
|
||||
#ifndef PROBE_DEBUG_PROTOCOL
|
||||
#define PROBE_DEBUG_PROTOCOL PROTO_DAP_V2
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,82 +0,0 @@
|
||||
; Output-enable active-low variant of the SWD probe
|
||||
|
||||
; This program is very similar to the one in probe.pio. The only difference is
|
||||
; that here write_cmd and turnaround_cmd are split into two separate routines,
|
||||
; whose difference is OEn being high/low.
|
||||
|
||||
; SWDIO_OEn is pin 0, SWCLK pin 1, SWDIO (out) pin 2, SWDI (in) pin 3.
|
||||
; Pin 0 and 1 are sideset pins
|
||||
|
||||
.program probe
|
||||
.side_set 2 opt
|
||||
|
||||
public turnaround_cmd:
|
||||
pull
|
||||
turnaround_bitloop:
|
||||
nop [1] side 0x1
|
||||
jmp x-- turnaround_bitloop [1] side 0x3
|
||||
jmp get_next_cmd
|
||||
|
||||
public write_cmd:
|
||||
pull
|
||||
write_bitloop:
|
||||
out pins, 1 [1] side 0x0 ; Data is output by host on negedge
|
||||
jmp x-- write_bitloop [1] side 0x2 ; ...and captured by target on posedge
|
||||
; Fall through to next command
|
||||
.wrap_target
|
||||
public get_next_cmd:
|
||||
pull side 0x1 ; SWCLK initially low, OEn disabled
|
||||
out x, 8 ; Get bit count
|
||||
out pindirs, 1 ; Set SWDIO direction
|
||||
out pc, 5 ; Go to command routine
|
||||
|
||||
read_bitloop:
|
||||
nop ; Additional delay on taken loop branch
|
||||
public read_cmd:
|
||||
in pins, 1 [1] side 0x3 ; Data is captured by host on posedge
|
||||
jmp x-- read_bitloop side 0x1
|
||||
push
|
||||
.wrap ; Wrap to next command
|
||||
|
||||
|
||||
; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc
|
||||
% c-sdk {
|
||||
|
||||
static inline void probe_gpio_init()
|
||||
{
|
||||
#if defined(PROBE_PIN_RESET)
|
||||
// Target reset pin: pull up, input to emulate open drain pin
|
||||
gpio_pull_up(PROBE_PIN_RESET);
|
||||
// gpio_init will leave the pin cleared and set as input
|
||||
gpio_init(PROBE_PIN_RESET);
|
||||
#endif
|
||||
// Funcsel pins
|
||||
pio_gpio_init(pio0, PROBE_PIN_SWDIOEN);
|
||||
pio_gpio_init(pio0, PROBE_PIN_SWCLK);
|
||||
pio_gpio_init(pio0, PROBE_PIN_SWDIO);
|
||||
|
||||
// Make sure SWDIO has a pullup on it. Idle state is high
|
||||
gpio_pull_up(PROBE_PIN_SWDIO);
|
||||
gpio_pull_up(PROBE_PIN_SWDIOEN);
|
||||
}
|
||||
|
||||
static inline void probe_sm_init(pio_sm_config* sm_config) {
|
||||
|
||||
// Set SWDIOEN and SWCLK as sideset pins
|
||||
sm_config_set_sideset_pins(sm_config, PROBE_PIN_SWDIOEN);
|
||||
|
||||
// Set SWDIO offset
|
||||
sm_config_set_out_pins(sm_config, PROBE_PIN_SWDIO, 1);
|
||||
sm_config_set_set_pins(sm_config, PROBE_PIN_SWDIO, 1);
|
||||
sm_config_set_in_pins(sm_config, PROBE_PIN_SWDI);
|
||||
|
||||
// Set SWDIOEN, SWD and SWDIO pins as output to start. This will be set in the sm
|
||||
pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 3, true);
|
||||
|
||||
// shift output right, autopull off, autopull threshold
|
||||
sm_config_set_out_shift(sm_config, true, false, 0);
|
||||
// shift input right as swd data is lsb first, autopush off
|
||||
sm_config_set_in_shift(sm_config, true, false, 0);
|
||||
}
|
||||
|
||||
%}
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
/*
|
||||
* This is a shim between the SW_DP functions and the PIO
|
||||
* implementation used for Debugprobe. Instead of calling bitbash functions,
|
||||
* implementation used for Picoprobe. 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;
|
||||
}
|
||||
probe_debug("SWJ sequence count = %d FDB=0x%2x\n", count, data[0]);
|
||||
picoprobe_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;
|
||||
}
|
||||
probe_debug("SWD sequence\n");
|
||||
picoprobe_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;
|
||||
}
|
||||
probe_debug("SWD_transfer\n");
|
||||
picoprobe_debug("SWD_transfer\n");
|
||||
/* Generate the request packet */
|
||||
prq |= (1 << 0); /* Start Bit */
|
||||
for (n = 1; n < 5; n++) {
|
||||
@@ -133,6 +133,8 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
probe_write_bits(8, prq);
|
||||
|
||||
/* Turnaround (ignore read bits) */
|
||||
probe_read_mode();
|
||||
|
||||
ack = probe_read_bits(DAP_Data.swd_conf.turnaround + 3);
|
||||
ack >>= DAP_Data.swd_conf.turnaround;
|
||||
|
||||
@@ -149,13 +151,15 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
}
|
||||
if (data)
|
||||
*data = val;
|
||||
probe_debug("Read %02x ack %02x 0x%08x parity %01x\n",
|
||||
picoprobe_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);
|
||||
probe_read_bits(DAP_Data.swd_conf.turnaround);
|
||||
probe_write_mode();
|
||||
} else {
|
||||
/* Turnaround for write */
|
||||
probe_hiz_clocks(DAP_Data.swd_conf.turnaround);
|
||||
probe_read_bits(DAP_Data.swd_conf.turnaround);
|
||||
probe_write_mode();
|
||||
|
||||
/* Write WDATA[0:31] */
|
||||
val = *data;
|
||||
@@ -163,7 +167,7 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
parity = __builtin_popcount(val);
|
||||
/* Write Parity Bit */
|
||||
probe_write_bits(1, parity & 0x1);
|
||||
probe_debug("write %02x ack %02x 0x%08x parity %01x\n",
|
||||
picoprobe_debug("write %02x ack %02x 0x%08x parity %01x\n",
|
||||
prq, ack, val, parity);
|
||||
}
|
||||
/* Capture Timestamp */
|
||||
@@ -174,9 +178,9 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
/* Idle cycles - drive 0 for N clocks */
|
||||
if (DAP_Data.transfer.idle_cycles) {
|
||||
for (n = DAP_Data.transfer.idle_cycles; n; ) {
|
||||
if (n > 256) {
|
||||
probe_write_bits(256, 0);
|
||||
n -= 256;
|
||||
if (n > 32) {
|
||||
probe_write_bits(32, 0);
|
||||
n -= 32;
|
||||
} else {
|
||||
probe_write_bits(n, 0);
|
||||
n -= n;
|
||||
@@ -191,7 +195,8 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
/* Dummy Read RDATA[0:31] + Parity */
|
||||
probe_read_bits(33);
|
||||
}
|
||||
probe_hiz_clocks(DAP_Data.swd_conf.turnaround);
|
||||
probe_read_bits(DAP_Data.swd_conf.turnaround);
|
||||
probe_write_mode();
|
||||
if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) {
|
||||
/* Dummy Write WDATA[0:31] + Parity */
|
||||
probe_write_bits(32, 0);
|
||||
@@ -204,6 +209,7 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
|
||||
n = DAP_Data.swd_conf.turnaround + 32U + 1U;
|
||||
/* Back off data phase */
|
||||
probe_read_bits(n);
|
||||
probe_write_mode();
|
||||
return ((uint8_t)ack);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,23 +68,12 @@
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 1
|
||||
|
||||
/*
|
||||
* TX bufsize (actually UART RX) is oversized because the Windows CDC-ACM
|
||||
* driver submits a grand total of _one_ URB at any one time.
|
||||
* This means the application must consume the data before the next IN token
|
||||
* is issued. At high datarates this leads to huge variation in instantaneous
|
||||
* throughput on USB, so a large runway is needed.
|
||||
*/
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 128
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 4096
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 64
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 64
|
||||
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE 8192
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE 8192
|
||||
|
||||
#ifndef TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
#define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "tusb_edpt_handler.h"
|
||||
#include "DAP.h"
|
||||
|
||||
static uint8_t itf_num;
|
||||
static uint8_t _rhport;
|
||||
|
||||
volatile uint32_t _resp_len;
|
||||
|
||||
static uint8_t _out_ep_addr;
|
||||
static uint8_t _in_ep_addr;
|
||||
|
||||
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)
|
||||
{
|
||||
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 &&
|
||||
DAP_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||
DAP_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
|
||||
|
||||
// Initialise circular buffer indices
|
||||
USBResponseBuffer.wptr = 0;
|
||||
USBResponseBuffer.rptr = 0;
|
||||
USBRequestBuffer.wptr = 0;
|
||||
USBRequestBuffer.rptr = 0;
|
||||
|
||||
// Initialse full/empty flags
|
||||
USBResponseBuffer.wasFull = false;
|
||||
USBResponseBuffer.wasEmpty = true;
|
||||
USBRequestBuffer.wasFull = false;
|
||||
USBRequestBuffer.wasEmpty = true;
|
||||
|
||||
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;
|
||||
|
||||
// Initialising the OUT endpoint
|
||||
|
||||
tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1);
|
||||
uint8_t ep_addr = edpt_desc->bEndpointAddress;
|
||||
|
||||
_out_ep_addr = ep_addr;
|
||||
|
||||
// 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);
|
||||
|
||||
// Initiliasing the IN endpoint
|
||||
|
||||
edpt_desc++;
|
||||
ep_addr = edpt_desc->bEndpointAddress;
|
||||
|
||||
_in_ep_addr = ep_addr;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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, 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;
|
||||
|
||||
} else if(ep_dir == TUSB_DIR_OUT) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Wake up DAP thread after processing the callback
|
||||
vTaskResume(dap_taskhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
void dap_thread(void *ptr)
|
||||
{
|
||||
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("%u %u 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("%u %u DAP cmd %s len %02x\n",
|
||||
USBRequestBuffer.wptr, USBRequestBuffer.rptr,
|
||||
dap_cmd_string[DAPRequestBuffer[0]], DAPRequestBuffer[1]);
|
||||
USBRequestBuffer.rptr++;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
_resp_len = DAP_ExecuteCommand(DAPRequestBuffer, DAPResponseBuffer);
|
||||
probe_info("%u %u 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();
|
||||
|
||||
if(buffer_empty(&USBResponseBuffer))
|
||||
{
|
||||
memcpy(WR_SLOT_PTR(USBResponseBuffer), DAPResponseBuffer, (uint16_t) _resp_len);
|
||||
USBResponseBuffer.wptr++;
|
||||
|
||||
usbd_edpt_xfer(_rhport, _in_ep_addr, RD_SLOT_PTR(USBResponseBuffer), (uint16_t) _resp_len);
|
||||
} else {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
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 = "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;
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef TUSB_EDPT_HANDLER_H
|
||||
#define TUSB_EDPT_HANDLER_H
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "DAP_config.h"
|
||||
|
||||
#define DAP_INTERFACE_SUBCLASS 0x00
|
||||
#define DAP_INTERFACE_PROTOCOL 0x00
|
||||
|
||||
typedef struct {
|
||||
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;
|
||||
|
||||
/* Main DAP loop */
|
||||
void dap_thread(void *ptr);
|
||||
|
||||
/* Endpoint Handling */
|
||||
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
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "tusb.h"
|
||||
#include "get_serial.h"
|
||||
#include "probe_config.h"
|
||||
#include "picoprobe_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 (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
|
||||
.bcdUSB = 0x0210, // USB Specification version 2.1 for BOS
|
||||
#else
|
||||
.bcdUSB = 0x0110,
|
||||
@@ -47,8 +47,12 @@ tusb_desc_device_t const desc_device =
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0x2E8A, // Pi
|
||||
.idProduct = 0x000c, // CMSIS-DAP Debug Probe
|
||||
.bcdDevice = 0x0103, // Version 01.03
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
|
||||
.idProduct = 0x0004, // Picoprobe
|
||||
#else
|
||||
.idProduct = 0x000c, // CMSIS-DAP adapter
|
||||
#endif
|
||||
.bcdDevice = 0x0101, // Version 01.01
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
@@ -77,10 +81,10 @@ enum
|
||||
#define CDC_NOTIFICATION_EP_NUM 0x81
|
||||
#define CDC_DATA_OUT_EP_NUM 0x02
|
||||
#define CDC_DATA_IN_EP_NUM 0x83
|
||||
#define DAP_OUT_EP_NUM 0x04
|
||||
#define DAP_IN_EP_NUM 0x85
|
||||
#define PROBE_OUT_EP_NUM 0x04
|
||||
#define PROBE_IN_EP_NUM 0x85
|
||||
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
#if (PICOPROBE_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 +101,19 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
return desc_hid_report;
|
||||
}
|
||||
|
||||
uint8_t desc_configuration[] =
|
||||
uint8_t const desc_configuration[] =
|
||||
{
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100),
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
// Interface 0
|
||||
#if (PROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
// HID (named interface)
|
||||
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)
|
||||
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)
|
||||
// Bulk (named interface)
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 5, DAP_OUT_EP_NUM, DAP_IN_EP_NUM, 64),
|
||||
#elif (PROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 5, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64),
|
||||
#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
|
||||
// Bulk
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, DAP_OUT_EP_NUM, DAP_IN_EP_NUM, 64),
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_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,8 +125,6 @@ uint8_t 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;
|
||||
}
|
||||
|
||||
@@ -135,11 +137,11 @@ char const* string_desc_arr [] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"Raspberry Pi", // 1: Manufacturer
|
||||
PROBE_PRODUCT_STRING, // 2: Product
|
||||
"Picoprobe CMSIS-DAP", // 2: Product
|
||||
usb_serial, // 3: Serial, uses flash unique ID
|
||||
"CMSIS-DAP v1 Interface", // 4: Interface descriptor for HID transport
|
||||
"CMSIS-DAP v2 Interface", // 5: Interface descriptor for Bulk transport
|
||||
"CDC-ACM UART Interface", // 6: Interface descriptor for CDC
|
||||
"Picoprobe CMSIS-DAP v1", // 4: Interface descriptor for HID transport
|
||||
"Picoprobe CMSIS-DAP v2", // 5: Interface descriptor for Bulk transport
|
||||
"Picoprobe CDC-ACM UART", // 6: Interface descriptor for CDC
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32];
|
||||
@@ -244,4 +246,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