Compare commits

..

3 Commits

Author SHA1 Message Date
Jonathan Bell
2eeef92b89 submodules: delete CMSIS_5 from the repository
See https://github.com/raspberrypi/pico-setup/issues/57

Clones of debugprobe or invocations of pico-setup.sh frequently
encounter errors relating to bandwidth budgets. These sporadic errors
are due to the large size of the CMSIS_5 repo clone exhausting the
bandwidth quotas on Github, so drop this in favour of a downstream copy
of the relevant CMSIS-DAP firmware files.

Users are advised to delete and regenerate the build directory.

Optionally, reclaim filesystem space by removing .git/modules/CMSIS_5
and all subdirectories.
2025-07-07 11:52:26 +01:00
Jonathan Bell
8a0c487df3 CMakelists.txt: use downstream copies of CMSIS_5 files
This change will require re-running cmake in the build directory.
2025-07-07 11:52:26 +01:00
Jonathan Bell
2a421d1648 Add downstream copies of CMSIS_5 files relevant to CMSIS-DAP firmware
From tag v2.0.0. Not used in the project yet.
2025-07-07 11:52:08 +01:00
16 changed files with 34 additions and 1065 deletions

View File

@@ -22,8 +22,6 @@ add_executable(debugprobe
src/get_serial.c
src/sw_dp_pio.c
src/tusb_edpt_handler.c
src/autobaud.c
src/remote_gpio.c
)
target_sources(debugprobe PRIVATE
@@ -44,22 +42,9 @@ target_compile_options(debugprobe 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)
pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/autobaud.pio)
target_include_directories(debugprobe PRIVATE src)
# add version
add_custom_target(version
${CMAKE_SOURCE_DIR}/get-version.sh
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
target_include_directories(debugprobe PRIVATE
${CMAKE_BINARY_DIR}/generated
)
add_dependencies(debugprobe version)
target_compile_definitions (debugprobe PRIVATE
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
)
@@ -90,9 +75,6 @@ target_link_libraries(debugprobe PRIVATE
tinyusb_device
tinyusb_board
hardware_pio
hardware_dma
hardware_irq
hardware_clocks
FreeRTOS-Kernel
FreeRTOS-Kernel-Heap1
)

View File

@@ -1,12 +1,11 @@
# Debugprobe
Firmware source for the Raspberry Pi Debug Probe SWD/UART accessory. Can also be run on a Raspberry Pi Pico or Pico 2.
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/)
[Raspberry Pi Pico 2 product page](https://www.raspberrypi.com/products/raspberry-pi-pico-2/)
# Documentation
@@ -30,7 +29,7 @@ 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 `-DPICO_SDK_PATH=/path/to/sdk` to the arguments to CMake below.
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:
```
@@ -39,41 +38,31 @@ Run cmake and build the code:
```
Done! You should now have a `debugprobe.uf2` that you can upload to your Debug Probe via the UF2 bootloader.
## Building for the Pico 1
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.
## Building for the Pico 2
# Building for the Pico 2
If using an existing debugprobe clone:
- You must completely regenerate your build directory, or use a different one.
- You must also sync and update submodules.
- You must also sync and update submodules as rp2350 needs a downstream FreeRTOS port for now.
- `PICO_SDK_PATH` must point to a version 2.0.0 or greater install.
```
git submodule sync
git submodule update --init --recursive
git submodule update --init
mkdir build-pico2
cd build-pico2
cmake -DDEBUG_ON_PICO=1 -DPICO_BOARD=pico2 ../
cmake -DDEBUG_ON_PICO=1 -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ../
```
This will build with the configuration for the Pico 2 and call the output program `debugprobe_on_pico2.uf2`.
# AutoBaud
Mode which automatically detects and sets the UART baud rate as data arrives.
To enable AutoBaud, configure the USB CDC port to the following custom baud rate:
```
9728 (0x2600)
```
> **Note:** Some Linux serial tools cannot set custom baud values. PuTTY on Windows and any terminal that supports arbitrary baud rates works.
Changing the baud rate to any other value disables AutoBaud.
# TODO
- AutoBaud selection, as PIO is a capable frequency counter
- Possibly include RTT support

View File

@@ -1,21 +0,0 @@
#!/bin/sh
if command -v git &>/dev/null
then
VERSION=$(git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD)
else
VERSION="unknown"
fi
if [ ! -e generated/probe ]
then
mkdir -p generated/probe
fi
cat > generated/probe/version.h << EOF
#ifndef _PROBE_VERSION_H
#define _PROBE_VERSION_H
#define PROBE_VERSION "${VERSION}"
#endif
EOF

View File

@@ -1,8 +0,0 @@
TODO - add CMake package to build probe_gpio_api applet
In the meanwhile, this oneliner builds on the host platform:
gcc -static -Os probe_gpio_api.c -lusb-1.0 -ludev
requires libusb-1.0-dev and libudev-dev

View File

@@ -35,7 +35,9 @@
#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
// UART config
#define PROBE_UART_TX 4

View File

@@ -1,244 +0,0 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <libusb-1.0/libusb.h>
#include "remote_gpio.h"
#define VENDOR_ID 0x2E8A // Raspberry Pi
#define PRODUCT_ID 0x000c // Debugprobe
#define BCDD_VER 0x0103
#define NR_GPIOS 28 // Limit to RP2040/2350A for now
#define GPIO_ALL 0xff
#define GET_STATE BMREQUEST_GPIO_GET
#define SET_STATE BMREQUEST_GPIO_SET
#define GPIO_OUT 1
#define GPIO_IN 0
static const char usage[] = {
"Use: \n"
" probe-gpio <get> [GPIO]\n"
" probe-gpio <set> <GPIO> <OPTIONS>\n"
"\n"
" get: retrieve GPIO state\n"
" if 'get' is specified with no further arguments, then the state of all gpios is returned.\n"
"\n"
" set: GPIO must be specified. One at a time, unlike raspi-gpio.\n"
" OPTIONS: one of:\n"
" op - drive GPIO output\n"
" ip - drive GPIO input\n"
" dl - drive low\n"
" dh - drive high\n"
" pu - pull-up\n"
" pd - pull-down\n"
" pn - pull none\n"
};
const char* pull_state[] = {
"NONE",
"UP",
"DOWN",
"KEEPER"
};
const char* dir_state[] = {
"INPUT",
"OUTPUT",
};
static void print_gpio_state(int gpio, int level, int function, unsigned int dir, unsigned int pull)
{
fprintf(stdout, "GPIO %d: level=%d function=%d dir=%s pull=%s\n",
gpio, level, function,
dir <= 1 ? dir_state[dir] : "UNK",
pull <= 3 ? pull_state[pull] : "UNK"
);
};
int main(int argc, char **argv)
{
int rc, i = 0;
struct libusb_device_handle *devh = NULL;
struct libusb_device **dlist = NULL;
struct libusb_context *ctx = NULL;
struct libusb_device_descriptor desc = {0};
int op;
int gpio;
int set_fn = -1;
unsigned char dummy[4];
unsigned char data[4] = {};
uint32_t glevel, gfunction, gdir, gpull;
if (argc < 2 || argc > 4) {
fprintf(stderr, "Need an operation to do.\n");
fprintf(stderr, "%s", usage);
return -1;
}
if (strcmp(argv[1], "get") == 0) {
op = BMREQUEST_GPIO_GET;
if (argc == 2)
gpio = GPIO_ALL;
else {
gpio = strtol(argv[2], NULL, 10);
if (gpio < 0 || gpio > 28) {
fprintf(stderr, "gpio out of range: %d\n", gpio);
return errno;
}
}
} else if (strcmp(argv[1], "set") == 0) {
op = BMREQUEST_GPIO_SET;
if (argc != 4) {
fprintf(stderr, "need to specify gpio and operation\n");
fprintf(stderr, "%s", usage);
return -1;
}
gpio = strtol(argv[2], NULL, 10);
if (gpio < 0 || gpio > 29) {
fprintf(stderr, "gpio out of range: %d\n", gpio);
return errno;
}
if (strcmp(argv[3], "op") == 0) {
set_fn = GPIO_SET_DIR;
data[0] = GPIO_OUT;
} else if (strcmp(argv[3], "ip") == 0) {
set_fn = GPIO_SET_DIR;
data[0] = GPIO_IN;
} else if (strcmp(argv[3], "dl") == 0) {
set_fn = GPIO_PUT;
data[0] = 0;
} else if (strcmp(argv[3], "dh") == 0) {
set_fn = GPIO_PUT;
data[0] = 1;
} else if (strcmp(argv[3], "pu") == 0) {
set_fn = GPIO_SET_PULLS;
data[0] = 1;
} else if (strcmp(argv[3], "pd") == 0) {
set_fn = GPIO_SET_PULLS;
data[0] = 1 << 1;
} else if (strcmp(argv[3], "pn") == 0) {
set_fn = GPIO_SET_PULLS;
data[0] = 0;
} else {
fprintf(stderr, "invalid operation specified\n");
fprintf(stderr, "%s", usage);
return -1;
}
} else {
fprintf(stderr, "Operation must be <set> or <get>\n");
fprintf(stderr, "%s", usage);
return -1;
}
/* Initialize libusb
*/
rc = libusb_init(&ctx);
if (rc < 0) {
fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc));
exit(rc);
}
/* Set debugging output to max level.
*/
#if LIBUSB_API_VERSION >= 0x01000106
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, 3);
#else
libusb_set_debug(ctx, 3);
#endif
rc = libusb_get_device_list(ctx, &dlist);
if (rc < 0) {
fprintf(stderr, "Error retrieving device list: %s\n", libusb_error_name(i));
exit(rc);
}
/* Look for a specific device and open it.
*/
for (i = 0; i < rc; i++) {
libusb_device *device = dlist[i];
libusb_get_device_descriptor(device, &desc);
if (desc.idVendor == VENDOR_ID && desc.idProduct == PRODUCT_ID) {
//if(desc.bcdDevice == BCDD_VER) {
//printf(stderr, "Found a Debug Probe version %04x - using it\n", desc.bcdDevice);
break;
// } else {
//fprintf(stderr, "Found a Debug Probe version %04x - incompatible\n", desc.bcdDevice);
// }
}
}
if (i == rc) {
fprintf(stderr, "Error: no compatible Debug Probes found - wrong fw?\n");
goto out;
}
rc = libusb_open(dlist[i], &devh);
if (rc < 0) {
fprintf(stderr, "Error: can't open device at address %u: %s\n", libusb_get_device_address(dlist[i]), libusb_error_name(rc));
goto out;
}
libusb_free_device_list(dlist, 1);
/* Does the it do remote gpio? */
rc = libusb_control_transfer(devh, BMREQUEST_GPIO_GET, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
0, (unsigned char *)&dummy, sizeof(dummy), 0);
if (rc < 0) {
fprintf(stderr, "Error: probe doesn't understand REMOTE_GPIO access - wrong fw?\n");
goto out;
}
rc = 0;
if (op == BMREQUEST_GPIO_GET) {
if (gpio == GPIO_ALL) {
for (i = 0; i < NR_GPIOS; i++) {
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET,
i, (unsigned char *)&glevel, sizeof(glevel), 3000); if (rc < 0) goto out;
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
i, (unsigned char *)&gfunction, sizeof(gfunction), 3000); if (rc < 0) goto out;
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_DIR,
i, (unsigned char *)&gdir, sizeof(gdir), 3000); if (rc < 0) goto out;
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_PULLS,
i, (unsigned char *)&gpull, sizeof(gpull), 3000); if (rc < 0) goto out;
print_gpio_state(i, glevel, gfunction, gdir, gpull);
}
} else {
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET,
gpio, (unsigned char *)&glevel, sizeof(glevel), 3000); if (rc < 0) goto out;
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
gpio, (unsigned char *)&gfunction, sizeof(gfunction), 3000); if (rc < 0) goto out;
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_DIR,
gpio, (unsigned char *)&gdir, sizeof(gdir), 3000); if (rc < 0) goto out;
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_PULLS,
gpio, (unsigned char *)&gpull, sizeof(gpull), 3000); if (rc < 0) goto out;
print_gpio_state(gpio, glevel, gfunction, gdir, gpull);
}
} else if (op == BMREQUEST_GPIO_SET) {
// Need to init a gpio before SIO can do anything useful to it. This clobbers output enable, so avoid glitches
rc = libusb_control_transfer(devh, BMREQUEST_GPIO_GET, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
gpio, (unsigned char *)dummy, 4, 300); if (rc < 0) goto out;
if (dummy[0] != 5) {
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_INIT,
gpio, (unsigned char *)data, 4, 3000); if (rc < 0) goto out;
//printf("fn was %d, setting init\n", gfunction);
}
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, set_fn,
gpio, (unsigned char *)data, 4, 3000); if (rc < 0) goto out;
}
libusb_release_interface(devh, 0);
out:
if (rc < 0) {
fprintf(stderr, "libusb error: %s\n", libusb_error_name(rc));
}
if (devh)
libusb_close(devh);
libusb_exit(NULL);
return rc < 0 ? rc : 0;
}

View File

@@ -1,64 +0,0 @@
#ifndef _REMOTE_GPIO_H_
#define _REMOTE_GPIO_H_
#ifdef PICO_SDK_VERSION_MAJOR
#include "tusb.h"
bool gpio_remote_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
#endif
/* Control interface definitions for exposing pico-sdk's GPIO API.
* bmRequestType.direction - SET calls are Host to Device (0), GET calls are Device to Host (1)
* bmRequestType.type = Vendor (2)
* bmRequestType.recipient = Device (0)
* (i.e. bmRequestType = 0x40 or 0xc0.)
* bRequest = CTRL_REMOTE_GPIO_REQ (0x02)
* wValue - GPIO API function indexed by enum here. Note that there are fewer get_ calls defined than set_ calls.
* wIndex - for functions operating on a single GPIO, the GPIO number. For functions operating on all GPIOs in bulk (e.g gpio_get_all or gpio_dir_out_masked), set to zero.
* wLength = 4. For SET calls, provide a le32 word of either the 2nd argument to a function call for a specific GPIO, or a mask of GPIOs to operate on in bulk.
* Note: for GPIO_INIT and GPIO_DEINIT, the data stage still happens but is ignored.
* For GET calls, returns a le32 of either the function call result, or the results of a bulk operation as a bitmask.
**/
#define CTRL_REMOTE_GPIO_REQ 0x2
#define BMREQUEST_GPIO_SET 0x40
#define BMREQUEST_GPIO_GET 0xc0
typedef enum {
GPIO_GET_FUNCTION,
GPIO_GET_PULLS,
GPIO_GET_INPUT_ENABLED,
GPIO_GET_INPUT_HYST_ENABLED,
GPIO_GET_SLEW_RATE,
GPIO_GET_DRIVE_STRENGTH,
GPIO_GET,
GPIO_GET_ALL,
GPIO_GET_OUT_LEVEL,
GPIO_GET_DIR,
GPIO_GET_MAX,
} gpio_get_fns;
typedef enum {
GPIO_SET_FUNCTION,
GPIO_SET_PULLS,
GPIO_SET_INPUT_ENABLED,
GPIO_SET_INPUT_HYST_ENABLED,
GPIO_SET_SLEW_RATE,
GPIO_SET_DRIVE_STRENGTH,
GPIO_PUT, // No idea why this isn't called gpio_set in the sdk
GPIO_PUT_ALL,
GPIO_SET_MASK,
GPIO_CLR_MASK,
GPIO_XOR_MASK,
GPIO_SET_DIR_OUT_MASKED,
GPIO_SET_DIR_IN_MASKED,
GPIO_SET_DIR_ALL_BITS,
GPIO_SET_DIR,
GPIO_INIT,
GPIO_INIT_MASK,
GPIO_DEINIT,
GPIO_SET_MAX,
} gpio_set_fns;
#endif /* _REMOTE_GPIO_H_ */

View File

@@ -102,11 +102,9 @@
*/
/* SMP port only */
#define configNUMBER_OF_CORES 2
#define configTICK_CORE 0
#define configUSE_CORE_AFFINITY 1
#define configNUM_CORES 1
#define configTICK_CORE 1
#define configRUN_MULTIPLE_PRIORITIES 1
#define configUSE_PASSIVE_IDLE_HOOK 0
/* RP2040 specific */
#define configSUPPORT_PICO_SYNC_INTEROP 1

View File

@@ -1,387 +0,0 @@
#include <pico/stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <hardware/pio.h>
#include <hardware/dma.h>
#include <hardware/irq.h>
#include <hardware/clocks.h>
#include "autobaud.h"
#include "probe_config.h"
#include "autobaud.pio.h"
// DMA buffer size
#define BUF_SIZE 1024
// Size of hash table for sample occurrence counts
#define HASH_TBL_SIZE 500
// Minimum sample occurrence ratio to consider a baud rate value valid
#define MIN_FREQUENCY 0.05f
// PIO clock frequency in Hz
#define PIO_CLOCK_FREQUENCY 125000000
// DMA IRQ for autobaud
#define DMA_AUTOBAUD_IRQ 0
// Priority for DMA IRQ handler
#define DMA_AUTOBAUD_IRQ_PRIORITY PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY
typedef struct {
int key;
int count;
} Entry;
typedef struct {
Entry *entries;
size_t size;
} HashTable;
// PIO instance
static PIO pio;
// PIO state machine
static int sm = -1;
// PIO program offset
static int offset = -1;
// UART RX GPIO 1 pin
static const uint rx_pin = PROBE_UART_RX;
// Frequency hash table to store samples occurance
static HashTable *freq_table;
// Estimated baud rate and validity
static float baud;
static float validity;
// shortest bit duration in PIO cycles
static uint32_t min_cycles_count = UINT32_MAX;
// longest bit duration in PIO cycles
static uint32_t max_cycles_count = 0;
static uint32_t total_samples; // total samples seen
static uint32_t bit_time_sum; // sum of 1-bit times
static uint32_t bit_time_count; // total 1-bit times
static uint32_t outlier_count; // total of 1-bit times outliers
// DMA channels to read RX PIO line
static int ctrl_chan = -1;
static int data_chan = -1;
// DMA ring buffer storing PIO RX FIFO data
static uint32_t rx_buffer[BUF_SIZE] __attribute__((aligned(4096)));
// DMA control channel reads this value to reload transfer count
static const uint32_t dma_reload_count = BUF_SIZE;
static uintptr_t last_write_addr = (uintptr_t)rx_buffer;
static uintptr_t curr_write_addr = (uintptr_t)rx_buffer;
volatile bool autobaud_running = false;
volatile bool autobaud_stopped = true;
// Queue to hold new baud rate estimates
QueueHandle_t baudQueue;
TaskHandle_t autobaud_taskhandle;
uint32_t hash(uint32_t x, size_t size) {
x = ((x >> 16) ^ x) * 0x45d9f3bu;
x = ((x >> 16) ^ x) * 0x45d9f3bu;
x = (x >> 16) ^ x;
return x % size;
}
HashTable *create_table(size_t size) {
HashTable *table = malloc(sizeof(HashTable));
if (!table) return NULL;
table->size = size;
table->entries = calloc(size, sizeof(Entry));
if (!table->entries) {
free(table);
return NULL;
}
return table;
}
void insert(HashTable *table, int key) {
uint32_t idx = hash(key, table->size);
while (table->entries[idx].key != 0) {
if (table->entries[idx].key == key) {
table->entries[idx].count++;
return;
}
idx = (idx + 1) % table->size;
}
table->entries[idx].key = key;
table->entries[idx].count = 1;
}
int get_count(HashTable *table, int key) {
uint32_t idx = hash(key, table->size);
while (table->entries[idx].key != 0) {
if (table->entries[idx].key == key) {
return table->entries[idx].count;
}
idx = (idx + 1) % table->size;
}
return 0;
}
void free_table(HashTable *table) {
free(table->entries);
free(table);
}
void __isr dma_handler() {
// Clear DMA interrupt
if ((data_chan >= 0) && dma_irqn_get_channel_status(DMA_AUTOBAUD_IRQ, data_chan)) {
dma_irqn_acknowledge_channel(DMA_AUTOBAUD_IRQ, data_chan);
}
}
bool dma_configure(PIO pio, uint sm) {
// Configure two DMA channels for continuous RX FIFO monitoring:
// - data_chan: Continuously reads from PIO RX FIFO into circular buffer
// - ctrl_chan: Triggers data_chan to restart when it completes a transfer
ctrl_chan = dma_claim_unused_channel(true);
if (ctrl_chan < 0) return false;
data_chan = dma_claim_unused_channel(true);
if (data_chan < 0) return false;
dma_channel_config data_cfg = dma_channel_get_default_config(data_chan);
channel_config_set_transfer_data_size(&data_cfg, DMA_SIZE_32);
channel_config_set_read_increment(&data_cfg, false);
channel_config_set_write_increment(&data_cfg, true);
channel_config_set_dreq(&data_cfg, pio_get_dreq(pio, sm, false)); // Trigger when PIO RX FIFO has data to read
channel_config_set_chain_to(&data_cfg, ctrl_chan); // Chain to control channel when transfer completes
channel_config_set_ring(&data_cfg, true, 12); // Ring buffer size 2^12 = 4096 bytes (1024 uint32_t)
dma_channel_configure(
data_chan,
&data_cfg,
rx_buffer,
&pio->rxf[sm],
BUF_SIZE,
false
);
dma_channel_config ctrl_cfg = dma_channel_get_default_config(ctrl_chan);
channel_config_set_transfer_data_size(&ctrl_cfg, DMA_SIZE_32);
channel_config_set_read_increment(&ctrl_cfg, false);
channel_config_set_write_increment(&ctrl_cfg, false);
dma_channel_configure(
ctrl_chan,
&ctrl_cfg,
&(dma_hw->ch[data_chan].al1_transfer_count_trig), // Destination: data channel's transfer count register
&dma_reload_count, // Source: reload transfer count value (BUF_SIZE)
1,
false
);
// Enable DMA interrupt on data channel completion
irq_add_shared_handler(dma_get_irq_num(DMA_AUTOBAUD_IRQ), dma_handler, DMA_AUTOBAUD_IRQ_PRIORITY);
irq_set_enabled(dma_get_irq_num(DMA_AUTOBAUD_IRQ), true);
dma_irqn_set_channel_enabled(DMA_AUTOBAUD_IRQ, data_chan, true);
dma_channel_start(data_chan);
return true;
}
// Compare rounded integer parts of baud rates
// Tolerance of 0.5% in detected versus set
inline bool baud_changed(float new_baud, float baud) {
uint32_t hi = (uint32_t)(baud * 1.005f);
uint32_t lo = (uint32_t)(baud * 0.995f);
uint32_t new = (uint32_t)new_baud;
return (new > hi || new < lo);
}
// Processes new DMA samples read from the PIO RX FIFO. Each DMA sample
// represents a timestamp (or cycle count) between edges on the input signal.
// Accumulates these durations, filters noise, and estimates baud rate.
uint estimate_baud_rate() {
uint old_progress = total_samples;
// Get current DMA write address
curr_write_addr = dma_hw->ch[data_chan].write_addr;
// Convert absolute addresses to buffer indices
size_t curr_index = ((curr_write_addr) - (uintptr_t)rx_buffer) / sizeof(rx_buffer[0]);
size_t last_index = (last_write_addr - (uintptr_t)rx_buffer) / sizeof(rx_buffer[0]);
for (size_t i = last_index; i != curr_index; i = (i + 1) % BUF_SIZE) {
uint32_t raw = rx_buffer[i];
uint32_t curr_cycles_count = (UINT32_MAX - raw) * 2;
insert(freq_table, curr_cycles_count);
total_samples++;
if (curr_cycles_count > max_cycles_count)
max_cycles_count = curr_cycles_count;
float freq = (float) get_count(freq_table, curr_cycles_count) / (float) total_samples;
// if sample is seen at least 5% of all samples,
// it is assumed it's not a noisy value
if (freq < MIN_FREQUENCY) continue;
if (curr_cycles_count < min_cycles_count) {
min_cycles_count = curr_cycles_count;
bit_time_sum = 0;
bit_time_count = 0;
outlier_count = 0;
continue;
}
// If current duration is within +10% of min_cycles, treat it as a "1-bit period"
if ((curr_cycles_count - min_cycles_count) < ((float)min_cycles_count * 0.1f)) {
bit_time_sum += curr_cycles_count;
bit_time_count++;
// 1-bit period should not be less than 1/9th of the longest period
if (curr_cycles_count < (max_cycles_count / 9))
outlier_count++;
// Calculate baud from average of 1-bit times
float avg_bit_time = (float) bit_time_sum / (float) bit_time_count;
float new_baud = PIO_CLOCK_FREQUENCY / avg_bit_time;
// If baud has changed, send updated baud information to cdc_thread
if (baud_changed(new_baud, baud)) {
float completeness = 1.0f - expf(-(float) total_samples / 40.0f);
float noise_ratio = (float) outlier_count / (float) bit_time_count;
float consistency = 1.0f - fminf(noise_ratio * 2.0f, 1.0f);
float validity = completeness * consistency;
if (validity > 0.6f) {
baud = new_baud;
BaudInfo_t new_baud_info;
new_baud_info.baud = (uint32_t)roundf(baud);
new_baud_info.validity = validity;
xQueueOverwrite(baudQueue, &new_baud_info);
}
}
}
}
last_write_addr = curr_write_addr;
return total_samples - old_progress;
}
void autobaud_deinit() {
// Disable DMA IRQ and channels
if (data_chan >= 0) {
dma_irqn_set_channel_enabled(DMA_AUTOBAUD_IRQ, data_chan, false);
dma_irqn_acknowledge_channel(DMA_AUTOBAUD_IRQ, data_chan);
dma_channel_unclaim(data_chan);
data_chan = -1;
}
if (ctrl_chan >= 0) {
dma_channel_unclaim(ctrl_chan);
ctrl_chan = -1;
}
irq_remove_handler(dma_get_irq_num(DMA_AUTOBAUD_IRQ), dma_handler);
if (!irq_has_shared_handler(dma_get_irq_num(DMA_AUTOBAUD_IRQ))) {
irq_set_enabled(dma_get_irq_num(DMA_AUTOBAUD_IRQ), false);
}
// Remove PIO program
if (pio && (sm >= 0)) {
pio_sm_set_enabled(pio, sm, false);
pio_sm_unclaim(pio, sm);
}
if (pio && (offset >= 0)) {
pio_remove_program(pio, &autobaud_program, offset);
}
if (freq_table) {
free_table(freq_table);
freq_table = NULL;
}
if (baudQueue) {
vQueueDelete(baudQueue);
baudQueue = NULL;
}
// Reset state
pio = NULL;
sm = offset = -1;
baud = validity = 0.0f;
min_cycles_count = UINT32_MAX;
max_cycles_count = 0;
total_samples = bit_time_sum = bit_time_count = outlier_count = 0;
last_write_addr = (uintptr_t)rx_buffer;
curr_write_addr = (uintptr_t)rx_buffer;
}
bool autobaud_init() {
pio = pio0;
// Claim a free PIO state machine
sm = pio_claim_unused_sm(pio, true);
if (sm < 0)
return false;
// Add PIO program
offset = pio_add_program(pio, &autobaud_program);
if (offset < 0) {
autobaud_deinit();
return false;
}
float div = (float)clock_get_hz(clk_sys) / PIO_CLOCK_FREQUENCY;
autobaud_program_init(pio, sm, offset, rx_pin, div);
pio_sm_set_enabled(pio, sm, true);
// Create hash table to keep count of sample occurance
freq_table = create_table(HASH_TBL_SIZE);
if (!freq_table) {
autobaud_deinit();
return false;
}
// Create queue to send baud information to cdc_thread
baudQueue = xQueueCreate(1, sizeof(BaudInfo_t));
if (!baudQueue) {
autobaud_deinit();
return false;
}
// Set up DMA to continuously write PIO RX data into RAM
if (!dma_configure(pio, sm)) {
autobaud_deinit();
return false;
}
return true;
}
void autobaud_start() {
xTaskNotify(autobaud_taskhandle, AUTOBAUD_CMD_START, eSetValueWithOverwrite);
}
void autobaud_wait_stop() {
while (!autobaud_stopped)
xTaskNotify(autobaud_taskhandle, AUTOBAUD_CMD_STOP, eSetValueWithOverwrite);
}
// FreeRTOS thread running if MAGIC_BAUD was set by host
void autobaud_thread(void * param) {
TickType_t wake = xTaskGetTickCount();
uint32_t cmd = AUTOBAUD_CMD_NONE;
uint processed;
while (true) {
if (!autobaud_running) {
// Idle state: thread is blocked here until start command is received
xTaskNotifyWait(0, 0xFFFFFFFFu, &cmd, portMAX_DELAY);
if (cmd == AUTOBAUD_CMD_START) {
if (autobaud_init()) {
autobaud_running = true;
autobaud_stopped = false;
}
}
} else {
// Check if host requested autobaud termination
if (xTaskNotifyWait(0, 0xFFFFFFFFu, &cmd, 0) == pdTRUE) {
if (cmd == AUTOBAUD_CMD_STOP) {
autobaud_running = false;
autobaud_deinit();
autobaud_stopped = true;
continue;
}
}
processed = estimate_baud_rate();
if (!processed)
xTaskDelayUntil(&wake, 1);
}
}
}

View File

@@ -1,30 +0,0 @@
#ifndef AUTOBAUD_H
#define AUTOBAUD_H
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#define MAGIC_BAUD 9728 // 0x2600
typedef enum {
AUTOBAUD_CMD_NONE = 0,
AUTOBAUD_CMD_START = 1,
AUTOBAUD_CMD_STOP = 2,
} autobaud_cmd_t;
typedef struct {
uint32_t baud; // Estimated baud rate
float validity; // Validity of the estimated baud rate
} BaudInfo_t;
extern volatile bool autobaud_running;
extern volatile bool autobaud_stopped;
extern QueueHandle_t baudQueue;
extern TaskHandle_t autobaud_taskhandle;
void autobaud_start(void);
void autobaud_wait_stop(void);
void autobaud_thread(void * param);
#endif

View File

@@ -1,42 +0,0 @@
.program autobaud
.wrap_target
falling_edge:
wait 0 pin 0 ; falling edge detected
set x, 0 ; set cycle counter to 0
mov x, ~x ; x = 0xFFFFFFFF, to decrement x
count_cycles:
jmp pin rising_edge ; if line went high, save current cycle count
jmp x-- count_cycles ; otherwise, decrement and enter loop again
rising_edge:
mov isr, x ; save elapsed cycle count in ISR
push noblock ; nonblocking push of the ISR content
jmp falling_edge ; repeat sampling process
.wrap
% c-sdk {
// Helper function (for use in C program) to initialize this PIO program
void autobaud_program_init(PIO pio, uint sm, uint offset, uint rx_pin, float div) {
// Sets up state machine and wrap target. This function is automatically
// generated in autobaud.pio.h.
pio_sm_config c = autobaud_program_get_default_config(offset);
// No need to set PIO funcsel, as this program is receive-only
sm_config_set_in_pins(&c, rx_pin);
sm_config_set_jmp_pin(&c, rx_pin);
pio_sm_set_consecutive_pindirs(pio, sm, rx_pin, 1, false);
// Set the clock divider for the state machine
sm_config_set_clkdiv(&c, div);
// Load configuration and jump to start of the program
pio_sm_init(pio, sm, offset, &c);
}
%}

View File

@@ -27,7 +27,6 @@
#include "FreeRTOS.h"
#include "task.h"
#include "tusb.h"
#include "autobaud.h"
#include "probe_config.h"
@@ -51,8 +50,6 @@ static volatile uint tx_led_debounce;
static uint rx_led_debounce;
#endif
static BaudInfo_t baud_info;
void cdc_uart_init(void) {
gpio_set_function(PROBE_UART_TX, GPIO_FUNC_UART);
gpio_set_function(PROBE_UART_RX, GPIO_FUNC_UART);
@@ -179,22 +176,6 @@ bool cdc_task(void)
return keep_alive;
}
void cdc_uart_set_baudrate(uint32_t baudrate) {
/* 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(baudrate, 1);
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",
baudrate, micros, interval);
uart_deinit(PROBE_UART_INTERFACE);
tud_cdc_write_clear();
tud_cdc_read_flush();
uart_init(PROBE_UART_INTERFACE, baudrate);
}
void cdc_thread(void *ptr)
{
BaseType_t delayed;
@@ -205,38 +186,31 @@ void cdc_thread(void *ptr)
keep_alive = cdc_task();
if (!keep_alive) {
delayed = xTaskDelayUntil(&last_wake, interval);
if (delayed == pdFALSE)
last_wake = xTaskGetTickCount();
if (autobaud_running) {
// Receive baud information from autobaud thread
if (xQueueReceive(baudQueue, &baud_info, 0) == pdTRUE) {
cdc_uart_set_baudrate(baud_info.baud);
// Assume 8N1
uart_set_format(PROBE_UART_INTERFACE, 8, 1, UART_PARITY_NONE);
}
}
if (delayed == pdFALSE)
last_wake = xTaskGetTickCount();
}
}
}
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
{
if (line_coding->bit_rate == MAGIC_BAUD) {
if (!autobaud_running)
autobaud_start();
return;
}
else if (autobaud_running) {
autobaud_wait_stop();
}
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. */
if (tud_cdc_connected())
vTaskSuspend(uart_taskhandle);
cdc_uart_set_baudrate(line_coding->bit_rate);
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",
line_coding->bit_rate, micros, interval);
uart_deinit(PROBE_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:

View File

@@ -41,12 +41,10 @@
#include "probe_config.h"
#include "probe.h"
#include "cdc_uart.h"
#include "autobaud.h"
#include "get_serial.h"
#include "tusb_edpt_handler.h"
#include "DAP.h"
#include "hardware/structs/usb.h"
#include "remote_gpio.h"
// UART0 for debugprobe debug
// UART1 for debugprobe to target device
@@ -60,8 +58,6 @@ static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2)
#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1)
#define AUTOBAUD_TASK_PRIO (tskIDLE_PRIORITY + 1)
TaskHandle_t dap_taskhandle, tud_taskhandle, mon_taskhandle;
static int was_configured;
@@ -195,9 +191,8 @@ 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)
{
// nothing to with Status stage
if (stage == CONTROL_STAGE_ACK)
return true;
// nothing to with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP) return true;
switch (request->bmRequestType_bit.type)
{
@@ -205,8 +200,6 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
switch (request->bRequest)
{
case 1:
if (stage != CONTROL_STAGE_SETUP)
return true;
if ( request->wIndex == 7 )
{
// Get Microsoft OS 2.0 compatible descriptor
@@ -218,8 +211,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
{
return false;
}
case CTRL_REMOTE_GPIO_REQ:
return gpio_remote_req(rhport, stage, request);
default: break;
}
break;
@@ -238,9 +230,6 @@ void tud_suspend_cb(bool remote_wakeup_en)
if (was_configured) {
vTaskSuspend(uart_taskhandle);
vTaskSuspend(dap_taskhandle);
if (autobaud_running)
autobaud_wait_stop();
vTaskSuspend(autobaud_taskhandle);
}
/* slow down clk_sys for power saving ? */
}
@@ -251,7 +240,6 @@ void tud_resume_cb(void)
if (was_configured) {
vTaskResume(uart_taskhandle);
vTaskResume(dap_taskhandle);
vTaskResume(autobaud_taskhandle);
}
}
@@ -262,10 +250,6 @@ void tud_unmount_cb(void)
vTaskSuspend(dap_taskhandle);
vTaskDelete(uart_taskhandle);
vTaskDelete(dap_taskhandle);
if (autobaud_running)
autobaud_wait_stop();
vTaskSuspend(autobaud_taskhandle);
vTaskDelete(autobaud_taskhandle);
was_configured = 0;
}
@@ -277,9 +261,6 @@ void tud_mount_cb(void)
xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle);
/* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */
xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle);
/* Autobaud detection using PIO as a frequency counter */
xTaskCreate(autobaud_thread, "ABR", configMINIMAL_STACK_SIZE, NULL, AUTOBAUD_TASK_PRIO, &autobaud_taskhandle);
vTaskCoreAffinitySet(autobaud_taskhandle, (1 << 1));
was_configured = 1;
}
}

View File

@@ -1,6 +1,6 @@
#include "probe_config.h"
#include "pico/binary_info.h"
#include "probe/version.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
@@ -8,7 +8,6 @@
void bi_decl_config()
{
bi_decl(bi_program_version_string(PROBE_VERSION));
#ifdef PROBE_PIN_RESET
bi_decl(bi_1pin_with_name(PROBE_PIN_RESET, "PROBE RESET"));
#endif

View File

@@ -1,160 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2023 Raspberry Pi Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <pico/stdlib.h>
#include <stdio.h>
#include <string.h>
#include "hardware/gpio.h"
#include "probe_config.h"
#include "remote_gpio.h"
/* tinyUSB gives you multiple callbacks, one per stage */
static uint32_t data;
bool gpio_remote_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
/* Is the host paying attention ? */
if (request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_DEVICE)
return false;
probe_info("remote_req wValue=0x%02x wIndex=0x%02x wLength=0x%02x dir=%d\n", request->wValue, request->wIndex, request->wLength, request->bmRequestType_bit.direction);
if (request->wIndex >= NUM_BANK0_GPIOS)
return false;
switch (request->bmRequestType_bit.direction)
{
case TUSB_DIR_IN:
{
if(stage != CONTROL_STAGE_SETUP)
return true;
if (request->wLength != 4)
return false;
switch (request->wValue) {
case GPIO_GET_FUNCTION:
data = gpio_get_function(request->wIndex);
break;
case GPIO_GET_PULLS:
data = (gpio_is_pulled_up(request->wIndex) ? 0x1 : 0);
data |= (gpio_is_pulled_down(request->wIndex) ? 0x2 : 0);
break;
case GPIO_GET_INPUT_HYST_ENABLED:
data = gpio_is_input_hysteresis_enabled(request->wIndex);
break;
case GPIO_GET_SLEW_RATE:
data = gpio_get_slew_rate(request->wIndex);
break;
case GPIO_GET_DRIVE_STRENGTH:
data = gpio_get_drive_strength(request->wIndex);
break;
case GPIO_GET:
data = gpio_get(request->wIndex);
break;
case GPIO_GET_ALL:
data = gpio_get_all();
break;
case GPIO_GET_OUT_LEVEL:
data = gpio_get_out_level(request->wIndex);
break;
case GPIO_GET_DIR:
data = gpio_get_dir(request->wIndex);
break;
default:
return false;
break;
}
probe_info("ctrl IN data 0x%08lx\n", data);
return tud_control_xfer(rhport, request, &data, sizeof(data));
}
case TUSB_DIR_OUT:
{
/* Data phase should "always" happen */
if (stage == CONTROL_STAGE_SETUP) {
return tud_control_xfer(rhport, request, &data, sizeof(data));
} else if (stage == CONTROL_STAGE_DATA) {
probe_info("data stage2 data= %08lx\n", data);
switch (request->wValue) {
case GPIO_SET_FUNCTION:
gpio_set_function(request->wIndex, data);
break;
case GPIO_SET_PULLS:
gpio_set_pulls(request->wIndex, !!(data & 0x1), !!(data & 0x2));
break;
case GPIO_SET_INPUT_ENABLED:
gpio_set_input_enabled(request->wIndex, data);
break;
case GPIO_SET_INPUT_HYST_ENABLED:
gpio_set_input_hysteresis_enabled(request->wIndex, data);
break;
case GPIO_SET_SLEW_RATE:
gpio_set_slew_rate(request->wIndex, data);
break;
case GPIO_SET_DRIVE_STRENGTH:
gpio_set_drive_strength(request->wIndex, data);
break;
case GPIO_PUT:
gpio_put(request->wIndex, !!data);
break;
case GPIO_PUT_ALL:
gpio_put_all(data);
break;
case GPIO_SET_MASK:
gpio_set_mask(data);
break;
case GPIO_CLR_MASK:
gpio_clr_mask(data);
break;
case GPIO_XOR_MASK:
gpio_xor_mask(data);
break;
case GPIO_SET_DIR_OUT_MASKED:
gpio_set_dir_out_masked(data);
break;
case GPIO_SET_DIR_IN_MASKED:
gpio_set_dir_in_masked(data);
break;
case GPIO_SET_DIR_ALL_BITS:
gpio_set_dir_all_bits(data);
break;
case GPIO_SET_DIR:
gpio_set_dir(request->wIndex, !!data);
break;
case GPIO_INIT:
gpio_init(request->wIndex);
break;
case GPIO_INIT_MASK:
gpio_init_mask(data);
break;
case GPIO_DEINIT:
gpio_deinit(request->wIndex);
break;
default:
return false;
}
return true;
} else {
return true;
}
}
}
return false;
}

View File

@@ -48,7 +48,7 @@ tusb_desc_device_t const desc_device =
.idVendor = 0x2E8A, // Pi
.idProduct = 0x000c, // CMSIS-DAP Debug Probe
.bcdDevice = 0x0223, // Version 02.23
.bcdDevice = 0x0222, // Version 02.22
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,