Compare commits

..

1 Commits

Author SHA1 Message Date
Jonathan Bell
1c36b52049 probe: handle pipelined OUT transfers properly
A consequence of tinyUSB's implementation of vendor interfaces is that
it treats pipe traffic as free-flowing, i.e. no distinction is made if a
short packet is received. CMSIS-DAP should ordinarily encapsulate a set
of commands in a single packet, but if multiple packets are receieved
before the FIFO is drained then all the commands need to be processed at
once.

Ensure there is always enough RX space, and TX space for the generated
response.
2022-11-22 17:06:02 +00:00
19 changed files with 316 additions and 491 deletions

View File

@@ -35,8 +35,14 @@ target_include_directories(picoprobe PRIVATE
target_compile_options(picoprobe PRIVATE -Wall)
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()
set(DBG_PIN_COUNT=4)
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)
target_include_directories(picoprobe PRIVATE src)

View File

@@ -3,9 +3,3 @@ Picoprobe allows a Pico / RP2040 to be used as USB -> SWD and UART bridge. This
# 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".
# 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

View File

@@ -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>.
@@ -493,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 PICOPROBE_DAP_CONNECTED_LED
gpio_put(PICOPROBE_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 PICOPROBE_DAP_RUNNING_LED
gpio_put(PICOPROBE_DAP_RUNNING_LED, bit);
#endif
}
__STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit) {}
///@}

View File

@@ -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_DEBUGPROBE_H_
#define BOARD_DEBUGPROBE_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 PICOPROBE_UART_TX 4
#define PICOPROBE_UART_RX 5
#define PICOPROBE_UART_INTERFACE uart1
#define PICOPROBE_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_PRODUCT_STRING "Debug Probe (CMSIS-DAP)"
#endif

View File

@@ -1,82 +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
/* Target reset GPIO (active-low). Omit if not used.*/
#define PROBE_PIN_RESET 0
#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 PICOPROBE_UART_TX 4
#define PICOPROBE_UART_RX 5
#define PICOPROBE_UART_INTERFACE uart1
#define PICOPROBE_UART_BAUDRATE 115200
#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_PRODUCT_STRING "Example Debug Probe"
#endif

View File

@@ -1,50 +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_PICO_H_
#define BOARD_PICO_H_
#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
#define PROBE_PIN_RESET 1
// UART config
#define PICOPROBE_UART_TX 4
#define PICOPROBE_UART_RX 5
#define PICOPROBE_UART_INTERFACE uart1
#define PICOPROBE_UART_BAUDRATE 115200
#define PICOPROBE_USB_CONNECTED_LED 25
#define PROBE_PRODUCT_STRING "Picoprobe (CMSIS-DAP)"
#endif

View File

@@ -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})

View 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. */

View File

@@ -36,17 +36,6 @@ TickType_t last_wake, interval = 100;
static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE];
static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE];
// Actually s^-1 so 25ms
#define DEBOUNCE_MS 40
static uint debounce_ticks = 5;
#ifdef PICOPROBE_UART_TX_LED
static uint tx_led_debounce;
#endif
#ifdef PICOPROBE_UART_RX_LED
static uint rx_led_debounce;
#endif
void cdc_uart_init(void) {
gpio_set_function(PICOPROBE_UART_TX, GPIO_FUNC_UART);
@@ -72,43 +61,21 @@ 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);
rx_led_debounce = debounce_ticks;
#endif
written = MIN(tud_cdc_write_available(), rx_len);
if (written > 0) {
tud_cdc_write(rx_buf, written);
tud_cdc_write_flush();
}
} else {
#ifdef PICOPROBE_UART_RX_LED
if (rx_led_debounce)
rx_led_debounce--;
else
gpio_put(PICOPROBE_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 PICOPROBE_UART_TX_LED
gpio_put(PICOPROBE_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);
} else {
#ifdef PICOPROBE_UART_TX_LED
if (tx_led_debounce)
tx_led_debounce--;
else
gpio_put(PICOPROBE_UART_TX_LED, 0);
#endif
}
} else if (was_connected) {
tud_cdc_write_clear();
@@ -135,11 +102,11 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
* 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));
picoprobe_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(PICOPROBE_UART_INTERFACE);
tud_cdc_write_clear();
@@ -152,16 +119,8 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
{
/* 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 PICOPROBE_UART_RX_LED
gpio_put(PICOPROBE_UART_RX_LED, 0);
rx_led_debounce = 0;
#endif
#ifdef PICOPROBE_UART_RX_LED
gpio_put(PICOPROBE_UART_TX_LED, 0);
tx_led_debounce = 0;
#endif
} else
else
vTaskResume(uart_taskhandle);
}

View File

@@ -28,25 +28,34 @@
#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 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
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;
}
}

View File

@@ -27,5 +27,7 @@
#define LED_H
void led_init(void);
void led_task(void);
void led_signal_activity(uint total_bits);
#endif

View File

@@ -32,20 +32,20 @@
#include <string.h>
#include "bsp/board.h"
#include "tusb.h"
#include "picoprobe_config.h"
#include "probe.h"
#include "tusb.h"
#include "time.h"
#include "cdc_uart.h"
#include "get_serial.h"
#include "led.h"
#include "DAP.h"
#include "DAP_config.h"
// 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
@@ -59,48 +59,52 @@ void usb_thread(void *ptr)
{
do {
tud_task();
#ifdef PICOPROBE_USB_CONNECTED_LED
if (!gpio_get(PICOPROBE_USB_CONNECTED_LED) && tud_ready())
gpio_put(PICOPROBE_USB_CONNECTED_LED, 1);
else
gpio_put(PICOPROBE_USB_CONNECTED_LED, 0);
#endif
// 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 resp_len;
uint32_t ret;
int32_t packet_len;
uint16_t req_len, resp_len;
uint8_t *rx_ptr;
do {
if (tud_vendor_available()) {
tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
tud_vendor_write(TxDataBuffer, resp_len);
tud_vendor_flush();
} else {
// Trivial delay to save power
vTaskDelay(1);
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();
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
probe_gpio_init();
probe_init();
#else
DAP_Setup();
stdio_uart_init();
#endif
led_init();
picoprobe_info("Welcome to Picoprobe!\n");
@@ -117,10 +121,11 @@ int main(void) {
while (!THREADED) {
tud_task();
cdc_task();
#if (PICOPROBE_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);

View File

@@ -26,50 +26,53 @@
#ifndef PICOPROBE_H_
#define PICOPROBE_H_
#include "FreeRTOS.h"
#include "task.h"
#if false
#define picoprobe_info(format,args...) \
do { \
vTaskSuspendAll(); \
printf(format, ## args); \
xTaskResumeAll(); \
} while (0)
#define picoprobe_info(format,args...) printf(format, ## args)
#else
#define picoprobe_info(format,...) ((void)0)
#endif
#if false
#define picoprobe_debug(format,args...) \
do { \
vTaskSuspendAll(); \
printf(format, ## args); \
xTaskResumeAll(); \
} while (0)
#define picoprobe_debug(format,args...) printf(format, ## args)
#else
#define picoprobe_debug(format,...) ((void)0)
#endif
#if false
#define picoprobe_dump(format,args...)\
do { \
vTaskSuspendAll(); \
printf(format, ## args); \
xTaskResumeAll(); \
} while (0)
#define picoprobe_dump(format,args...) printf(format, ## args)
#else
#define picoprobe_dump(format,...) ((void)0)
#endif
// TODO tie this up with PICO_BOARD defines in the main SDK
#include "board_pico_config.h"
//#include "board_debugprobe_config.h"
//#include "board_example_config.h"
// 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
#define PROBE_PIN_RESET 6
// UART config
#define PICOPROBE_UART_TX 4
#define PICOPROBE_UART_RX 5
#define PICOPROBE_UART_INTERFACE uart1
#define PICOPROBE_UART_BAUDRATE 115200
// LED config
#ifndef PICOPROBE_LED
#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
@@ -79,3 +82,5 @@ do { \
#endif
#endif
#endif

View File

@@ -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,6 +68,25 @@ 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;
picoprobe_info("Set swclk freq %dKHz sysclk %dkHz\n", freq_khz, clk_sys_freq_khz);
@@ -69,10 +97,8 @@ void probe_set_swclk_freq(uint freq_khz) {
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);
#endif
}
void probe_write_bits(uint bit_count, uint32_t data_byte) {
@@ -103,21 +129,51 @@ uint32_t probe_read_bits(uint bit_count) {
void probe_read_mode(void) {
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_in_posedge));
while(pio_sm_get_pc(pio0, PROBE_SM) != probe.offset + probe_offset_in_idle);
while(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO));
}
void probe_write_mode(void) {
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_out_negedge));
while(pio_sm_get_pc(pio0, PROBE_SM) != probe.offset + probe_offset_out_idle);
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
@@ -141,3 +197,119 @@ void probe_deinit(void)
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();
}
}
}

View File

@@ -26,14 +26,6 @@
#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);
void probe_write_bits(uint bit_count, uint32_t data_byte);
uint32_t probe_read_bits(uint bit_count);
@@ -41,6 +33,11 @@ uint32_t probe_read_bits(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);

View File

@@ -28,7 +28,6 @@
public out_negedge:
set pindirs, 1 side 0x0 ; Init OE clock 0
public out_idle:
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
@@ -41,7 +40,6 @@ out_negedge_bitloop:
public in_posedge:
set pindirs, 0 side 0x0 ; INIT IE clock 0
public in_idle:
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:
@@ -49,47 +47,3 @@ in_posedge_bitloop:
jmp x-- in_posedge_bitloop side 0x0 ;
push ; Push to rx fifo when done
jmp in_posedge ; Jump back to start
; 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);
}
%}

View File

@@ -1,73 +0,0 @@
; Output-enable active-low variant of the SWD probe
.program probe
.side_set 2 opt
; SWDIO_OEN is pin 0, SWCLK pin 1, SWDIO (out) pin 2, SWDI (in) pin 3.
; Pin 0 and 1 are sideset pins
public out_negedge:
set pindirs, 0x1 side 0x0 ; OE_N 0, data high, clock 0
public out_idle:
pull ; pull nbits - 1
mov x, osr
pull ; pull data
public out_negedge_bitloop:
out pins, 1 side 0x0
jmp x-- out_negedge_bitloop side 0x2 ; OE_N 0, clock high
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
public in_posedge:
set pindirs, 0x0 side 0x1 ; OE_N 1, data high, clock 0
public in_idle:
pull
mov x, osr
in_posedge_bitloop:
in pins, 1 side 0x1 ; OE_N 1, clock 0
jmp x-- in_posedge_bitloop side 0x3 ; OE_N 1, clock 1
push
jmp in_posedge
; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc
% c-sdk {
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);
}
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);
}
%}

View File

@@ -74,10 +74,6 @@
#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

View File

@@ -47,7 +47,11 @@ tusb_desc_device_t const desc_device =
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x2E8A, // Pi
.idProduct = 0x000c, // CMSIS-DAP Debug Probe
#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,
@@ -133,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];