Skip to content

RP2040Nexus/yapicoprobe

Repository files navigation

Yet Another Picoprobe

Overview

YAPicoprobe allows a Pico / RP2040 to be used as USB → SWD and UART bridge. This means it can be used as a debugger and serial console for another Pico or any other SWD compatible controller.

YAPicoprobe is a fork of the original Picoprobe and was created due to my lazyness to follow the PR discussions and delays with unknown outcome.

Another reason for this fork is that I wanted to play around with SWD, RTT, PIO etc pp, so the established development process was a little bit hindering.

Finally there is Yet Another Picoprobe around, the YAPicoprobe.

Features

Plus

  • FAST, at least faster than most other picoprobes

  • standard debug tool connectivity

    • CMSIS-DAPv2 WinUSB (driver-less vendor-specific bulk) - CMSIS compliant debug channel

    • CMSIS-DAPv1 HID - CMSIS compliant debug channel as a fallback

  • MSC - drag-n-drop support of UF2 firmware image à la DAPLink for RP2040 Pico / PicoW and nRF52832/833/840 targets

  • CDC - virtual com port for logging of the target

    • UART connection between target and probe is redirected

    • RTT terminal channel is automatically redirected into this CDC (if there is no CMSIS-DAPv2/MSC connection)

  • CDC - virtual com port for logging of the probe

  • sigrok probe - data collection on eight digital and three analog channels (logic analyzer and oscilloscope) with auto-trigger capability

  • LED for state indication

Other Benefits

  • on Windows no more Zadig fiddling because the underlying protocols of CMSIS-DAPv1 and v2 are driver-less

  • easy drag-n-drop (or copy) upload of a firmware image to the target via the probe

  • no more reset push button (or disconnect/connect cycle) to put the target into BOOTSEL mode

  • auto detection of RP2040/nRF52/Generic targets

Minus

  • custom Picoprobe protocol has been dropped

Details

Known Devices

  • RP2040 (and its flash "win w25q16jv"), size of the targets flash is detected but it’s not tested, if MSC flashing does work

  • nRF52832/833/840

  • other SWD compatible (Arm) devices with limitations in MSC access

Code Inherited

Picoprobe

the original

pico_debug

another probe which gave ideas for PIO code

sigrok-pico

original RP2040 based sigrok logic analyzer / oscilloscope

DAPLink

The SWD probe software for a lot of targets and boards

Wiring

Picoprobe documentation can be found in the Pico Getting Started Guide. See "Appendix A: Using Picoprobe".

Wiring

Wires between probe and target board are the same as before, but the target UART wires can be omitted if RTT is used for logging on the target device.

Additionally there is an additional /RESET line and the sigrok digital and analog inputs.

I recommend to use a simple Raspberry Pi Pico board as the probe. The Pico W board does not add any features, instead the LED indicator does not work there.

Table 1. Pins Assignments Rasberry Pi Pico
Pin Description

GP1

SWDIR

GP2

target SWCLK

GP3

target SWDIO

GP4

target UART-RX

GP5

target UART-TX

GP6

target /RESET (RUN)

GP10..17

sigrok digital inputs

GP26/ADC0

sigrok ADC0

GP27/ADC1

sigrok ADC1

GP28/ADC2

sigrok ADC2

Table 2. Other Pin Assigments RP2040
Pin Description Pico W

GPIO0

spare

GPIO7..9

spare

GPIO18

spare

GPIO19..21

debug pins (for probe debugging)

GPIO22

spare

GPIO23

power supply control

WL_ON

GPIO24

USB sense

WL_D

GPIO25

LED

WL_CS

GPIO29/ADC3

VSYS/3

WL_CLK

Tool Compatibility

Table 3. Tool Compatibility
Tool Linux Windows (10) Command line

openocd
0.11 & 0.12

yes

yes

openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 25000" -c "program {firmware.elf} verify reset; shutdown;"

pyocd
0.34.x

yes

no

pyocd flash -f 2500000 -t rp2040 firmware.elf

cp / copy

yes

yes

cp firmware.uf2 /media/picoprobe

Parameter Optimization

YaPicoprobe tries to identify the connecting tool and sets some internal parameters for best performance. Those settings are:

Table 4. Parameter Optimization
Tool Parameter

pyocd / CMSIS-DAPv2

DAP_PACKET_COUNT=1
DAP_PACKET_SIZE=1024

openocd / CMSIS-DAPv2

DAP_PACKET_COUNT=2
DAP_PACKET_SIZE=1024

CMSIS-DAPv1 HID

DAP_PACKET_COUNT=1
DAP_PACKET_SIZE=64

SWD Adapter Speed

The tools above allow specification of the adapter speed. This is the clock frequency between probe and target device. Unfortunately DAP converts internally the frequency into delays which are always even multiples of clock cycles. That means that actual clock speeds are 125MHz / (2*n), n>=3 → 20833kHz, 12500kHz, 10417kHz, …​

Normally the requested frequency is rounded down according to the possible values from above. But if the specified frequency is completely out of range, the allowed maximum SWD frequency of the RP2040 is used, which is 24MHz.

Actually usable frequency depends on cabling and the DAP speed. If the DAP cannot access memory with speed determined by the host, it responds with WAIT and the host needs to retry.

Effects of cabling should be clear: the longer the cables plus some more effects, the worse the signals. Which effectively means slowing down clock frequency is required to get the data transported.

Tip

SWCLK speed for MSC and RTT (below) is set according to the latest used tool setup. E.g. pyocd reset -f 5000000 -t rp2040 sets SWCLK to 5MHz.

Note

SWD clock frequency is also limited by the target controller. For nRF52 targets default clock is set to 8MHz, for unknown SWD targets 2MHz are used.

MSC - Mass Storage Device Class

Via MSC the so called "drag-n-drop" supported is implemented. Actually this also helps in copying a UF2 image directly into the target via command line.

MSC write access, i.e. flashing of the target, is device dependent and thus works only for a few selected devices which are in my range of interest. Those devices are the RP2040 (and its flash "win w25q16jv") and the Nordic nRF52 family (namely nRF52832/833/840).
For the RP2040 some special flash routines has been implemented. For nRF52 flashing regular DAPLink modules have been taken. Which also implies, that extending the probes capabilities shouln’t be too hard.

Note
  • RP2040: flash erase takes place on a 64KByte base: on the first write to a 64 KByte page, the corresponding page is erased. That means, that multiple UF2 images can be flashed into the target as long as there is no overlapping within 64 KByte boundaries

  • nRF52: whole chip is erased on first write operation of an UF2 image which means that only one UF2 image can be flashed

Because CMSIS-DAP access should be generic, flashing of other SWD compatible devices is tool dependant (openocd/pyocd).

RTT - Real Time Transfer

RTT allows transfer from the target to the host in "realtime". YAPicoprobe currently reads channel 0 of the targets RTT and sends it into the CDC of the target. Effectively this allows RTT debug output into a terminal.

Note
  • only the devices RAM is scanned for an RTT control block, for unknown devices RAM in the range 0x20000000-0x2003ffff is assumed

  • don’t be too overwhelmed about Seggers numbers in the above mentioned document. The data must still be transferred which is not taken into account in the diagram (of course the target processor has finished after writing the data)

  • only one of CMSIS-DAP / MSC / RTT can access the target at the same time. RTT is disconnected in case CMSIS-DAP or MSC are claiming access

sigrok - Data Collection

The probe allows data collection for a sigrok compatible environment. Meaning the probe can act also as a logic analyzer / oscilloscope backend. The module is based on work taken from sigrok-pico. This also means, that at the moment libsigrok has to be adopted accordingly, see here. Benefit is, that this allows the Pico as a mixed-signal device and RLE compression of the collected data.

Specification of the module is:

  • 8 digital channels at GP10..GP17

  • 3 analog channels at GP26..GP28 with 8bit resolution

  • internal buffer of 100KByte which allows depending on setup between 25000 and two hundred thousand samples with highest sample speed

  • digital sampling rate can be up to 100MHz for a short period of time, see here

  • analog sampling rate can be up to 500kHz with one channel

  • continuous digital sampling can be up to 10MHz depending on data stream and USB connection/load

  • auto-trigger for sampling rates ⇐ 24MHz

Drawbacks:

  • digital channel numbering in sigrok is confusing, because D2 corresponds to GP10…​

  • for best performance digital channels must be assigned from GP10 consecutively

  • currently no hardware triggering supported

LED Indications

Table 5. LED Indications
state indication

no target found

5Hz blinking

DAPv1 connected

LED on, off for 100ms once per second

DAPv2 connected

LED on, off for 100ms twice per second

MSC active

LED on, off for 100ms thrice per second

UART data from target

slow flashing: 300ms on, 700ms off

target found

LED off, flashes once per second for 20ms

RTT control block found

LED off, flashes twice per second for 20ms

RTT data received

LED off, flashes thrice per second for 20ms

sigrok running

10Hz flashing

sigrok waiting for auto trigger

10Hz negative flashing (flicker)

Note

pyocd does not disconnect correctly at an end of a gdb debug session so the LED still shows a connection. To get out of this situation issue pyocd reset -t rp2040 or similar.

Configuration

udev rules for MSC and CMSIS-DAP

/etc/udev/rules.d/90-picoprobes.rules:

# set mode to allow access for regular user
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="000c", MODE:="0666"

# create COM port for target CDC
ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ATTRS{interface}=="YAPicoprobe CDC-UART",    MODE:="0666", SYMLINK+="ttyPicoTarget"
ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ATTRS{interface}=="YAPicoprobe CDC-DEBUG",   MODE:="0666", SYMLINK+="ttyPicoProbe"
ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ATTRS{interface}=="YAPicoprobe CDC-SIGROK",  MODE:="0666", SYMLINK+="ttyPicoSigRok

# mount Picoprobe to /media/picoprobe
ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", RUN+="/usr/bin/logger --tag picoprobe-mount Mounting what seems to be a Raspberry Pi Picoprobe", RUN+="/usr/bin/systemd-mount --no-block --collect --fsck=0 -o uid=hardy,gid=hardy,flush $devnode /media/picoprobe"
ACTION=="remove", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", RUN+="/usr/bin/logger --tag picoprobe-mount Unmounting what seems to be a Raspberry Pi Picoprobe", RUN+="/usr/bin/systemd-umount /media/picoprobe"

# mount RPi bootloader to /media/pico
ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", RUN+="/usr/bin/logger --tag rpi-pico-mount Mounting what seems to be a Raspberry Pi Pico", RUN+="/usr/bin/systemd-mount --no-block --collect --fsck=0 -o uid=hardy,gid=hardy,flush $devnode /media/pico"
ACTION=="remove", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", RUN+="/usr/bin/logger --tag rpi-pico-mount Unmounting what seems to be a Raspberry Pi Pico", RUN+="/usr/bin/systemd-umount /media/pico"

PlatformIO

PlatformIO configuration in platformio.ini is pretty straight forward:

[env:pico]
framework = arduino
platform = https://github.com/maxgerhardt/platform-raspberrypi
board = rpipicow
board_build.core = earlephilhower
upload_protocol = cmsis-dap
debug_tool = cmsis-dap
monitor_speed = 115200
monitor_port  = /dev/ttyPicoTarget

The firmware image can alternativly copied directly (and faster) via MSC with custom upload:

[env:pico_cp]
...
upload_protocol = custom
upload_command = cp .pio/build/pico_cp/firmware.uf2 /media/picoprobe
...

I’m sure there are smarter ways to specify the image path directly.

There is also a special PlatformIO handling in the probe: it ignores the defensive 1MHz clock setting which is used by the above contained openocd. Standard clock is thus 15MHz. If this is too fast, set the frequency with pyocd reset -f 1100000 -t rp2040 or similar. If this is too slow, use pyocd reset -f 50000000 -t rp2040.

RTT

To use RTT for debug/console output the following has to be done:

  • in platformio.ini:

[env:pico]
...
lib_deps =
    ...
    koendv/RTT Stream
  • in main.cpp:

...
#include <RTTStream.h>
...
RTTStream rtt;
...
rtt.println("main module");
  • in other modules:

...
#include <RTTStream.h>
...
extern RTTStream rtt;
...
rtt.println("sub module");

Optimizations

SWD / Benchmarking

Benchmarking is done with an image with a size around 400KByte. Command lines are as follows:

  • cp: time cp firmware.uf2 /media/picoprobe/

  • openocd 0.12.0-rc2 (CMSIS-DAP)v2: time openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 25000" -c "program {firmware.elf} verify reset; shutdown;"

  • openocd 0.12.0-rc2 (CMSIS-DAP)v1: time openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "cmsis_dap_backend hid; adapter speed 25000" -c "program {firmware.elf} verify reset; shutdown;"

  • pyocd 0.34.3: time pyocd flash -f 25000000 -t rp2040 firmware.elf, pyocd ignores silently "-O cmsis_dap.prefer_v1=true", except for the "list" option

Note that benchmarking takes place under Linux. Surprisingly openocd and pyocd behave differently under Windows. DAPv2 is always used, because DAPv1 does not run under Linux(?).

Table 6. CMSIS-DAP Benchmarks
command / version cp openocd DAPv1 openocd DAPv2 pyocd DAPv2 comment

very early version

-

-

10.4s

-

v1.00

6.4s

-

8.1s

16.5s

git-3120a90

5.7s

-

7.8s

15.4s

- same but NDEBUG -

7.3s

-

9.5s

16.6s

a bad miracle…​ to make things worse, pyocd is very instable

git-bd8c41f

5.7s

28.6s

7.7s

19.9s

there was a python update :-/

git-0d6c6a8

5.7s

28.5s

6.8s

20.2s

- same but optimized for openocd

5.7s

28.5s

6.1s

-

pyocd crashes

git-0eba8bf

4.9s

28.6s

6.5s

13.8s

cp shows sometimes 5.4s

- same but optimized for openocd

4.9s

28.6s

5.8s

-

pyocd crashes

git-e38fa52

4.8s

28.6s

6.6s

14.0s

cp shows sometimes 5.4s

- same but optimized for openocd

4.8s

28.6s

5.9s

-

pyocd crashes

git-28fd8db

4.1s

28.6s

6.2s

13.9s

cp shows sometimes 4.6s, SWCLK tuned to 25MHz

- same but optimized for openocd

4.1s

28.6s

5.7s

-

pyocd crashes

Comparison with picoprobe and automated_test.py of pyocd

There is a good benchmark (actually the functional tests) in pyocd: automated_test.py. For configuration check https://github.com/pyocd/pyOCD/blob/main/docs/automated_tests.md

Comparing the original picoprobe firmware (running on the official Raspi Pico Debug Probe) and YAPicoprobe shows the following picture:

Table 7. Benchmarking with automated_test.py
Probe HW Probe FW Target 1.1MHz 2MHz 4MHz 6MHz 8MHz

Debug Probe

Original Picoprobe (download)

nRF52840 dongle

586s

583s

580s

581s

(-)

Debug Probe

YAPicoprobe

nRF52840 dongle

327s

236s

192s

171s (*)

(-)

Pi Pico W

YAPicoprobe

nRF52840 dongle

332s

234s

192s

177s

(-)

Debug Probe

Original Picoprobe (download)

PCA10056

586s

582s

581s

580s

580s

Debug Probe

YAPicoprobe

PCA10056

322s

233s

189s

183s

244s

Pi Pico W

YAPicoprobe

PCA10056

237s

193s

178s

173s

(*) unexpected error only observed in conjuntion with the nRF52840 dongle.

Tests were performed with the following configuration:

probes:
  <probe-unid>:
    frequency:       [12468]000000
    target_override: nrf52840
    test_binary:     l1_nrf52840-dk.bin

Findings are not completely clear:

  • the original probe firmware is very slow, no clue about the actual reason. Weird is that runtime with the original firmware is not frequency dependant

  • there seems to be a frequency limit of the debug probe HW at around 6MHz due to its in-series resistors (runtime increase from 6 to 8MHz)

  • the nRF52840 dongle has problems with >=6MHz as well. Either a PCB or a nRF52840 part problem. Max frequency of 4MHz should be used

  • good setups are

    • YAPicoprobe + PCA10056 @ 6MHz

    • YAPicoprobe + nRF52840 dongle @ 4MHz

SWD / PIO

Several PIO optimizations has been implemented. Main idea of PIO control has been taken from pico_debug.

To monitor the progress between the several versions, PulseView has been used. LA probe was sigrok-pico.

First Version (03.01.2023 - e2b4a67)

First Version

(Currently) Final Version (06.01.2023 - 28fd8db)

06.01.2023

Explanation / Conclusion

The plots above were taken at SWCLK=15MHz. Absolute time of the four command sequences shrunk from ~25us to 18us. Not bad.

Nevertheless there are still gaps which may give more optimization opportunities. Switching times between read / write and the gap between two commands are candidates. Note that moving code into RAM did not really help (and optimization is still a non/slow-working mystery).

SWDIR

Level shifter must be used to allow different voltage levels on probe and target. There are different switching circuits out there, e.g.

  • TXS0108E (or TXS0102/4E) which allows 3.3V on probe side and up to 5V on target side for up to 8 signals

  • 74LXC1T45 which allows the same voltage levels for a single signal (depending of type)

Because SWDIO is a bidirectional signal, the level shifter must switch between input and output. The TXS010xx does this automatically while the 74LXCxT45 requires an SWDIR signal to control direction.

Drawback of the automatic switching are much lower frequencies (<=24MHz) which may pass the component and the condition Vcca<=Vccb. So the TXS0108E is actually not recommended for this purpose.

For a clean implementation SWDIR has been provided to allow support of the 74LXCxT45. The following image shows the timing of SWDIR, SWCLK and SWDIO.

SWDIR
Note

For the sigrok input signals it’s also good practice to use level shifter if the target uses other voltage levels than the probe.

Notes

  • Frequencies

    • the CPU is overclocked to 168MHz (=7*24MHz)

    • SWD frequency limit is 25MHz, actually allowed are 24MHz

  • sigrok

    • PIO is running 7x faster in auto trigger mode than the specified sample rate

Misc

Hardware

  • use 2x 74LXC1T45 for the SWD IF, largest package: 6 pin SOT-23

  • 7803 for power supply of target

  • 74LVC8T245 level shifter for sigrok input, 24 pin SOIC / SOP packages are visible for soldering

About

Yet Another Picoprobe

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C 96.0%
  • CMake 1.8%
  • Other 2.2%