Compare commits

..

6 Commits

Author SHA1 Message Date
Jonathan Bell
59ced31058 picoprobe: rename to Debug Adapter 2022-12-06 17:11:59 +00:00
Jonathan Bell
4a3d23a85a probe: if we have a separate SWDIO input, use it
For boards with a level-shifter on SWDIO for compatibility with 1.8V VDDIO.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2022-12-06 17:11:59 +00:00
Jonathan Bell
b10589915c picoprobe: add a USB Connected LED and signal it appropriately
Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2022-12-06 17:10:19 +00:00
Jonathan Bell
6cf7e08291 picoprobe: use GPIO definitions for the new debug adapter, PCB R2
Limitations:
- Power LED doesn't yet do anything sensible
- The level-shifted SWDIO input isn't utilised by the PIO SM, so only 3.3v I/O

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2022-12-06 17:10:19 +00:00
Jonathan Bell
7adec12fa9 RTOS: reduce DAP polling delay - improves throughput.
Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2022-12-06 17:10:19 +00:00
Jonathan Bell
abc32c8ada Add DAP and UART LED options. Use a debounce for UART LEDs.
Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2022-12-06 17:10:19 +00:00
19 changed files with 331 additions and 469 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

@@ -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 0
// 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

@@ -39,14 +39,8 @@ 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);

View File

@@ -28,7 +28,16 @@
#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) {
led_count = 0;
gpio_init(PICOPROBE_LED);
gpio_set_dir(PICOPROBE_LED, GPIO_OUT);
gpio_put(PICOPROBE_LED, 1);
#ifdef PICOPROBE_USB_CONNECTED_LED
gpio_init(PICOPROBE_USB_CONNECTED_LED);
gpio_set_dir(PICOPROBE_USB_CONNECTED_LED, GPIO_OUT);
@@ -50,3 +59,20 @@ void led_init(void) {
gpio_set_dir(PICOPROBE_UART_TX_LED, GPIO_OUT);
#endif
}
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

@@ -70,11 +70,6 @@ void usb_thread(void *ptr)
} 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;
@@ -83,7 +78,6 @@ void dap_thread(void *ptr)
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);
@@ -92,14 +86,18 @@ void dap_thread(void *ptr)
}
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();
#endif
led_init();
picoprobe_info("Welcome to Picoprobe!\n");
@@ -116,10 +114,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

@@ -45,13 +45,41 @@
#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 12
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) // 2
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 2) // 3
#define PROBE_PIN_SWDI (PROBE_PIN_OFFSET + 1) // 1 - for level-shifted input
// Target reset config
#define PROBE_PIN_RESET 0
// 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
// 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
@@ -61,3 +89,5 @@
#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,56 +68,54 @@ 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);
uint32_t divider = clk_sys_freq_khz / freq_khz / 4;
if (divider == 0)
divider = 1;
// 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);
#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);
}
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);
DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE_WAIT);
picoprobe_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
// 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) {
@@ -120,39 +127,69 @@ uint32_t probe_read_bits(uint bit_count) {
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);
#ifdef PROBE_PIN_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);
// 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)
@@ -164,3 +201,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,24 +26,18 @@
#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);

View File

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

View File

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

View File

@@ -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;
@@ -152,10 +154,12 @@ uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
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;
@@ -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);
}

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
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
.idProduct = 0x0004, // Picoprobe
#else
.idProduct = 0x000c, // CMSIS-DAP Debug Probe
#endif
.bcdDevice = 0x0101, // Version 01.01
.iManufacturer = 0x01,
.iProduct = 0x02,
@@ -133,7 +137,7 @@ 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
"Debug Probe (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