diff --git a/CMakeLists.txt b/CMakeLists.txt index 964663641..b6e6ddbc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,8 +194,9 @@ target_link_libraries(${PROJECT} PRIVATE if(PICO_BOARD STREQUAL "pico_w") target_link_libraries(${PROJECT} PRIVATE - pico_cyw43_arch_lwip_sys_freertos - pico_lwip_iperf + #pico_cyw43_arch_lwip_sys_freertos + #pico_lwip_iperf + pico_cyw43_arch_none ) endif() diff --git a/Makefile b/Makefile index 75f546af4..9989955e2 100755 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # VERSION_MAJOR := 1 -VERSION_MINOR := 12 +VERSION_MINOR := 13 BUILD_DIR := build PROJECT := picoprobe @@ -20,7 +20,7 @@ CMAKE_FLAGS += -DCMAKE_EXPORT_COMPILE_COMMANDS=True ifeq ($(PICO_BOARD),) # pico|pico_w|pico_debug_probe - PICO_BOARD := pico_w + PICO_BOARD := pico_debug_probe endif diff --git a/README.adoc b/README.adoc index 3527606f9..8d9f08fc6 100644 --- a/README.adoc +++ b/README.adoc @@ -21,127 +21,85 @@ Finally there is **Y**et **A**nother **Picoprobe** around, the YAPicoprobe. ## Features -### Plus -* **FAST**, at least faster than most other picoprobes +* **FAST**, at least faster than most other picoprobes, see link:doc/benchmarks.adoc[benchmarks] * 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 https://github.com/ARMmbed/DAPLink[DAPLink] for RP2040 Pico / PicoW and nRF52832/833/840 targets -* CDC - virtual com port for logging of the target +* CDC - virtual com port for bidirectional communication with 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 +* CDC 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 +* 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 ## 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 +* RP2040 (and its flash "win w25q16jv"), size of the targets flash is automatically detected * nRF52832/833/840 * other SWD compatible (Arm) devices with limitations in MSC access -### Code Inherited -[%header] -|=== -| | - -| https://github.com/raspberrypi/picoprobe[Picoprobe] | the original +### Supported Probe Hardware +* https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html[Raspberry Pi Pico and Pico W] + + If you have to choose between a Pico and a Pico W, I'ld recommend the simple Pico, because Pico W does not + add any features +* https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html[Pico Debug Probe] + + + -| https://github.com/essele/pico_debug[pico_debug] -| another probe which gave ideas for PIO code +Other RP2040 hardware will work as well, e.g. https://mcuoneclipse.com/2023/04/08/open-source-picolink-raspberry-pi-rp2040-cmsis-dap-debug-probe/[picoLink] +is happy with the firmware image for the Pico. -| https://github.com/pico-coder/sigrok-pico[sigrok-pico] -| original RP2040 based sigrok logic analyzer / oscilloscope +Firmware images can be downloaded from https://github.com/rgrr/yapicoprobe/releases[github]. -| https://github.com/ARMmbed/DAPLink[DAPLink] -| The SWD probe software for a lot of targets and boards -|=== +### Probe / Target Wiring +Wires between Pico probe and target board are shown below. At least three wires (plus supply) are required: +SWCLK, SWDIO and Gnd. The Reset line is optional. If target UART RX/TX are used, additional two wires are needed. +Sigrok inputs go extra. +[.text-center] +image::board_schematic_bb.png[Wiring, 60%] -### Wiring -Picoprobe documentation can be found in the https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf[Pico Getting Started Guide]. +More information about setup can be found in the +https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf[Pico Getting Started Guide]. See "Appendix A: Using Picoprobe". -image::board_schematic_bb.png[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. +For information about cabling between Pico Debug Probe and target refer to the corresponding +https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html[documentation]. -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. +For details about probe pin assignments see the link:doc/hardware.adoc[hardware section]. -.Pins Assignments Rasberry Pi Pico -[%header] -|=== -| 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 - -|=== - -.Other Pin Assigments RP2040 -[%header] -|=== -| 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 .Tool Compatibility +[%autowidth] [%header] |=== -|Tool | Linux | Windows (10) | Command line +|Tool | Linux | Windows (10) | Example command line -|openocd + -0.11 & 0.12 +|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 +|pyOCD 0.34.x |yes |no -|`pyocd flash -f 2500000 -t rp2040 firmware.elf` +|`pyocd flash -f 400000 -t nrf52840 firmware.elf` |cp / copy |yes @@ -149,31 +107,37 @@ does not add any features, instead the LED indicator does not work there. |`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: .Parameter Optimization +[%autowidth] [%header] |=== |Tool | Parameter -|pyocd / CMSIS-DAPv2 +|pyOCD / CMSIS-DAPv2 |DAP_PACKET_COUNT=1 + DAP_PACKET_SIZE=1024 -|openocd / CMSIS-DAPv2 +|OpenOCD / CMSIS-DAPv2 |DAP_PACKET_COUNT=2 + DAP_PACKET_SIZE=1024 +|unknown / CMSIS-DAPv2 +|DAP_PACKET_COUNT=1 + +DAP_PACKET_SIZE=64 + |CMSIS-DAPv1 HID |DAP_PACKET_COUNT=1 + DAP_PACKET_SIZE=64 |=== -### SWD Adapter Speed +#### 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, ... @@ -195,7 +159,7 @@ 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, +SWD clock frequency is also limited by the target controller. For nRF52 targets default clock is set to 6MHz, for unknown SWD targets 2MHz are used. ==== @@ -220,7 +184,7 @@ too hard. ==== Because CMSIS-DAP access should be generic, flashing of other SWD compatible devices is tool dependant -(openocd/pyocd). +(OpenOCD/pyOCD). ### RTT - Real Time Transfer @@ -229,6 +193,9 @@ allows transfer from the target to the host in "realtime". YAPicoprobe currentl channel 0 of the targets RTT and sends it into the CDC of the target. Effectively this allows RTT debug output into a terminal. +If an RTT control block has been detected, characters received via the target CDC are transmitted +with RTT to the target. + [NOTE] ==== * only the devices RAM is scanned for an RTT control block, for unknown devices @@ -244,7 +211,13 @@ allows RTT debug output into a terminal. ==== -### sigrok - Data Collection +### Sigrok - Data Collection + +[IMPORTANT] +==== +Sigrok is disabled on the Pico Debug Probe. +==== + The probe allows data collection for a https://sigrok.org/[sigrok] compatible environment. Meaning the probe can act also as a logic analyzer / oscilloscope backend. The module is based on work taken from https://github.com/pico-coder/sigrok-pico[sigrok-pico]. @@ -277,9 +250,10 @@ Drawbacks: ### LED Indications .LED Indications +[%autowidth] [%header] |=== -| state | indication +| State | Indication | no target found | 5Hz blinking @@ -314,17 +288,54 @@ Drawbacks: [NOTE] ==== -pyocd does not disconnect correctly at an end of a gdb debug session so the LED still shows a connection. +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. ==== +#### Pico Debug Probe + +The Pico Debug Probe has four additional LEDs. Assignment is as follows: + +.Pico Debug Probe Additional LEDs +[%autowidth] +[%header] +|=== +| LED | Color | Indication + +| UART_TX +| yellow +| 5ms flash, if target sends data + +| UART_RX +| green +| 20ms flash, if target gets data + +| DAP_TARGET_RUNNING +| yellow +| set by host tool + +| DAP_CONNECTED +| green +| set by host tool + +|=== + +[NOTE] +==== +* currently OpenOCD sets both DAP_* LEDs on CMSIS-DAP connection +* pyOCD currently does not set the DAP_* LEDs at all +==== + ### Configuration #### udev rules for MSC and CMSIS-DAP -/etc/udev/rules.d/90-picoprobes.rules: -``` +Under Linux one wants to use the following udev rules for convenience. + +./etc/udev/rules.d/90-picoprobes.rules +[source] +---- # set mode to allow access for regular user SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="000c", MODE:="0666" @@ -340,12 +351,14 @@ ACTION=="remove", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="file # 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 https://platformio.org/[PlatformIO] configuration in `platformio.ini` is pretty straight forward: -``` +.PlatformIO configuration +[source,yaml] +---- [env:pico] framework = arduino platform = https://github.com/maxgerhardt/platform-raspberrypi @@ -355,22 +368,24 @@ 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: -``` +.PlatformIO copy configuration +[source,yaml] +---- [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. +I'm sure there are smarter ways to specify the image path. 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 +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`. @@ -409,219 +424,42 @@ 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(?). +## Internals -.CMSIS-DAP Benchmarks -[%header] -|=== -|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 +### Notes +* Frequencies +** the CPU is overclocked to 168MHz (=7*24MHz) +** SWD frequency limits +*** RP2040: 25MHz, actually allowed are 24MHz; default is 15MHz +*** nRF528xx: 10MHz, actually allowed are 8MHz; default is 6MHz +* sigrok +** PIO is running 7x faster in auto trigger mode than the specified sample rate -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: +### Code Inherited -.Benchmarking with automated_test.py [%header] |=== -| Probe HW | Probe FW | Target | 1.1MHz | 2MHz | 4MHz | 6MHz | 8MHz - -| Debug Probe -| Original Picoprobe (https://github.com/raspberrypi/picoprobe/releases/latest/download/debugprobe.uf2[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 (https://github.com/raspberrypi/picoprobe/releases/latest/download/debugprobe.uf2[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: - -```yaml -probes: - : - 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 https://github.com/essele/pico_debug/blob/main/swd.pio[pico_debug]. - -To monitor the progress between the several versions, -https://sigrok.org/wiki/PulseView[PulseView] has been used. LA probe was -https://github.com/pico-coder/sigrok-pico[sigrok-pico]. - -#### First Version (03.01.2023 - e2b4a67) -image::Screenshot_20230103_074404.png[First Version] - -#### (Currently) Final Version (06.01.2023 - 28fd8db) -image::Screenshot_20230106_153629.png[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. - -* https://www.ti.com/product/TXS0108E[TXS0108E] (or TXS0102/4E) which - allows 3.3V on probe side and up to 5V on target side for up to 8 signals -* https://www.ti.com/product/SN74LXC1T45[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. +| https://github.com/raspberrypi/picoprobe[Picoprobe] | the original -image::Screenshot_20230124_140906.png[SWDIR] +| https://github.com/essele/pico_debug[pico_debug] +| another probe which gave ideas for PIO code -[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. -==== +| https://github.com/pico-coder/sigrok-pico[sigrok-pico] +| original RP2040 based sigrok logic analyzer / oscilloscope +| https://github.com/ARMmbed/DAPLink[DAPLink] +| The SWD probe software for a lot of targets and boards +|=== -## 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 https://www.ti.com/product/SN74LXC1T45[74LXC1T45] for the SWD IF, - largest package: 6 pin SOT-23 -* 7803 for power supply of target -* https://www.ti.com/product/SN74LVC8T245[74LVC8T245] level shifter for sigrok input, - 24 pin SOIC / _SOP_ packages are visible for soldering +## More Text +* more link:doc/hardware.adoc[hardware information] and some ideas concerning a probe hardware +* an incomplete list of link:doc/TODO.adoc[TODOs], more on https://github.com/rgrr/yapicoprobe/issues[github] +* some link:doc/optimizations.adoc[optimizations] +* some link:doc/benchmarks.adoc[benchmarks] diff --git a/TODO.adoc b/doc/TODO.adoc similarity index 64% rename from TODO.adoc rename to doc/TODO.adoc index 73ea91316..0d33b236c 100755 --- a/TODO.adoc +++ b/doc/TODO.adoc @@ -6,17 +6,19 @@ -### TODO / Known Bugs - -* Features -** Wireless with PicoW -*** USBIP -**** https://usbip.sourceforge.net/ -**** https://github.com/thevoidnn/esp8266-wifi-cmsis-dap -**** https://github.com/windowsair/wireless-esp8266-dap -*** https://arm-software.github.io/CMSIS_5/Driver/html/group__wifi__interface__gr.html[CMSIS WiFi interface] -** semihosting? -* Bugs +## TODOs + +### Features +* Wireless with PicoW +** USBIP +*** https://usbip.sourceforge.net/ +*** https://github.com/thevoidnn/esp8266-wifi-cmsis-dap +*** https://github.com/windowsair/wireless-esp8266-dap +** https://arm-software.github.io/CMSIS_5/Driver/html/group__wifi__interface__gr.html[CMSIS WiFi interface] +* semihosting? But actually what can the probe do in this case? + + +### Bugs ** check the benchmark "miracle" with the NDEBUG version ** if `configTICK_RATE_HZ` is around 100, SWD IF no longer works * TODO diff --git a/doc/benchmarks.adoc b/doc/benchmarks.adoc new file mode 100755 index 000000000..770ac9bd7 --- /dev/null +++ b/doc/benchmarks.adoc @@ -0,0 +1,154 @@ +:imagesdir: png +:source-highlighter: rouge +:toc: +:toclevels: 5 + + + + +## Benchmarks + +### 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(?). + +.CMSIS-DAP Benchmarks +[%header] +|=== +|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: + +.Benchmarking with automated_test.py +[%header] +|=== +| Probe HW | Probe FW | Target | 1.1MHz | 2MHz | 4MHz | 6MHz | 8MHz + +| Debug Probe +| Original Picoprobe (https://github.com/raspberrypi/picoprobe/releases/latest/download/debugprobe.uf2[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 (https://github.com/raspberrypi/picoprobe/releases/latest/download/debugprobe.uf2[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: + +```yaml +probes: + : + 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 + diff --git a/doc/hardware.adoc b/doc/hardware.adoc new file mode 100755 index 000000000..44f8df083 --- /dev/null +++ b/doc/hardware.adoc @@ -0,0 +1,92 @@ +:imagesdir: png +:source-highlighter: rouge +:toc: +:toclevels: 5 + + + + +## Hardware + +### Pin Assignments + +.Pins Assignments Rasberry Pi Pico +[%header] +|=== +| 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 + +|=== + +.Other Pin Assigments Raspberry Pi Pico +[%header] +|=== +| 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 +|=== + + + +### SWDIR / SWDIO + +Level shifter must be used to allow different voltage levels on probe and target. +There are different switching circuits out there, e.g. + +* https://www.ti.com/product/TXS0108E[TXS0108E] (or TXS0102/4E) which + allows 3.3V on probe side and up to 5V on target side for up to 8 signals +* https://www.ti.com/product/SN74LXC1T45[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. + +image::Screenshot_20230124_140906.png[SWDIR] + + +### sigrok + +For the sigrok input signals it's also good practice to use level shifter if the target +uses other voltage levels than the probe. + + + +### Ideas + +The firmware calls for a new hardware. To get a really nice probe, some +level shifters , LEDs and power regulator for target supply would be required. + +Following parts could be used: + +* use 2x https://www.ti.com/product/SN74LXC1T45[74LXC1T45] for the SWD IF, + largest package: 6 pin SOT-23 +* 7803 for power supply of target +* https://www.ti.com/product/SN74LVC8T245[74LVC8T245] level shifter for sigrok input, + 24 pin SOIC / _SOP_ packages are visible for soldering + diff --git a/doc/optimizations.adoc b/doc/optimizations.adoc new file mode 100755 index 000000000..e6eb14dd3 --- /dev/null +++ b/doc/optimizations.adoc @@ -0,0 +1,33 @@ +:imagesdir: png +:source-highlighter: rouge +:toc: +:toclevels: 5 + + + + +## Optimizations + +### SWD / PIO +Several PIO optimizations has been implemented. Main idea of PIO control has +been taken from https://github.com/essele/pico_debug/blob/main/swd.pio[pico_debug]. + +To monitor the progress between the several versions, +https://sigrok.org/wiki/PulseView[PulseView] has been used. LA probe was +https://github.com/pico-coder/sigrok-pico[sigrok-pico]. + +#### First Version (03.01.2023 - e2b4a67) +image::Screenshot_20230103_074404.png[First Version] + +#### (Currently) Final Version (06.01.2023 - 28fd8db) +image::Screenshot_20230106_153629.png[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). + diff --git a/include/boards/pico.h b/include/boards/pico.h index 81b0a3a37..cbfd410b6 100755 --- a/include/boards/pico.h +++ b/include/boards/pico.h @@ -9,7 +9,8 @@ // SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES // ----------------------------------------------------- -// This header may be included by other board headers as "boards/pico.h" +// This header may be included by other board headers as "boards/pico.h". +// But normally this is included via "#include " if PICO_BOARD is set accordingly. #ifndef _BOARDS_PICO_H #define _BOARDS_PICO_H diff --git a/include/boards/pico_debug_probe.h b/include/boards/pico_debug_probe.h index 91f72daaf..abb806dcb 100755 --- a/include/boards/pico_debug_probe.h +++ b/include/boards/pico_debug_probe.h @@ -9,7 +9,8 @@ // SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES // ----------------------------------------------------- -// This header may be included by other board headers as "boards/pico_debug_probe.h" +// This header may be included by other board headers as "boards/pico_debug_probe.h". +// But normally this is included via "#include " if PICO_BOARD is set accordingly. // Schematic: https://datasheets.raspberrypi.com/debug/raspberry-pi-debug-probe-schematics.pdf #ifndef _BOARDS_PICO_DEBUG_PROBE_H @@ -47,6 +48,8 @@ #define PICOPROBE_LED PICO_DEFAULT_LED_PIN #define PICOPROBE_LED_CONNECTED 15 #define PICOPROBE_LED_RUNNING 16 +#define PICOPROBE_LED_TARGET_RX 7 // host -> probe -> target UART / RTT data, i.e. target is receiving +#define PICOPROBE_LED_TARGET_TX 8 // target -> probe -> host UART / RTT data, i.e. target is transmitting // PIO config #define PROBE_PIO pio0 diff --git a/include/boards/pico_w.h b/include/boards/pico_w.h index 6d0ad7a69..790b9cb9a 100755 --- a/include/boards/pico_w.h +++ b/include/boards/pico_w.h @@ -9,7 +9,8 @@ // SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES // ----------------------------------------------------- -// This header may be included by other board headers as "boards/pico.h" +// This header may be included by other board headers as "boards/pico.h". +// But normally this is included via "#include " if PICO_BOARD is set accordingly. #ifndef _BOARDS_PICO_W_H #define _BOARDS_PICO_W_H diff --git a/include/tusb_config.h b/include/tusb_config.h index f016ef5c7..2006e51aa 100644 --- a/include/tusb_config.h +++ b/include/tusb_config.h @@ -71,11 +71,15 @@ //********************************************** #define CFG_TUD_CDC_UART 1 // CDC for target UART IO -#define CFG_TUD_CDC_SIGROK 1 // CDC for sigrok IO +#if !defined(TARGET_BOARD_PICO_DEBUG_PROBE) + #define CFG_TUD_CDC_SIGROK 1 // CDC for sigrok IO +#else + #define CFG_TUD_CDC_SIGROK 0 // no sigrok for debug probe +#endif #if !defined(NDEBUG) - #define CFG_TUD_CDC_DEBUG 1 // CDC for debug output of the probe + #define CFG_TUD_CDC_DEBUG 1 // CDC for debug output of the probe #else - #define CFG_TUD_CDC_DEBUG 0 + #define CFG_TUD_CDC_DEBUG 0 #endif #define CFG_TUD_HID 1 // CMSIS-DAPv1 diff --git a/src/cdc_debug.c b/src/cdc_debug.c index 442bc87c6..af60cf5cf 100644 --- a/src/cdc_debug.c +++ b/src/cdc_debug.c @@ -23,6 +23,10 @@ * */ +/** + * Send probe debug output via CDC to host. + */ + #include #include #include "pico/stdlib.h" @@ -54,7 +58,7 @@ static EventGroupHandle_t events; static uint8_t cdc_debug_buf[CFG_TUD_CDC_TX_BUFSIZE]; -static bool m_connected = false; +static volatile bool m_connected = false; @@ -66,13 +70,15 @@ void cdc_debug_thread(void *ptr) for (;;) { #if CFG_TUD_CDC_DEBUG if ( !m_connected) { - // wait here some time (until my terminal program is ready) - m_connected = true; + // wait here until connected (and until my terminal program is ready) + while ( !m_connected) { + vTaskDelay(pdMS_TO_TICKS(100)); + } vTaskDelay(pdMS_TO_TICKS(100)); } if (xStreamBufferIsEmpty(stream_printf)) { - // -> end of transmission: flush and sleep for a long time + // -> end of transmission: flush and sleep for a long time (or until new data is available) tud_cdc_n_write_flush(CDC_DEBUG_N); xEventGroupWaitBits(events, EV_TX_COMPLETE | EV_STREAM, pdTRUE, pdFALSE, pdMS_TO_TICKS(10000)); } @@ -103,17 +109,12 @@ void cdc_debug_thread(void *ptr) void cdc_debug_line_state_cb(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) { - vTaskSuspend(task_printf); -#if CFG_TUD_CDC_DEBUG - tud_cdc_n_write_clear(CDC_DEBUG_N); -#endif + // CDC drivers use linestate as a bodge to activate/deactivate the interface. + if ( !dtr && !rts) { m_connected = false; } else { - vTaskResume(task_printf); + m_connected = true; } } // cdc_debug_line_state_cb diff --git a/src/cdc_uart.c b/src/cdc_uart.c index 9c8430e18..193d10026 100644 --- a/src/cdc_uart.c +++ b/src/cdc_uart.c @@ -23,6 +23,28 @@ * */ +/** + * Target (debug) input/output via CDC to host. + * + * Target -> Probe -> Host + * ----------------------- + * * target -> probe + * * UART: interrupt handler on_uart_rx() to cdc_uart_put_into_stream() + * * RTT: (rtt_console) to cdc_uart_write() + * * UART/RTT data is written into \a stream_uart via + * * probe -> host: cdc_thread() + * * data is fetched from \a stream_uart and then put into a CDC + * in cdc_thread() + * + * Host -> Probe -> Target + * ----------------------- + * * host -> probe + * * data is received from CDC in cdc_thread() + * * probe -> target + * * data is first tried to be transmitted via RTT + * * if that was not successful (no RTT_CB), data is transmitted via UART to the target + */ + #include #include "FreeRTOS.h" #include "stream_buffer.h" @@ -33,6 +55,7 @@ #include "picoprobe_config.h" #include "led.h" +#include "rtt_console.h" #define STREAM_UART_SIZE 4096 @@ -41,7 +64,7 @@ static TaskHandle_t task_uart = NULL; static StreamBufferHandle_t stream_uart; -static bool m_connected = false; +static volatile bool m_connected = false; #define EV_TX_COMPLETE 0x01 @@ -59,11 +82,13 @@ void cdc_thread(void *ptr) for (;;) { #if CFG_TUD_CDC_UART - size_t cdc_rx_chars; + uint32_t cdc_rx_chars; if ( !m_connected) { - // wait here some time (until my terminal program is ready) - m_connected = true; + // wait here until connected (and until my terminal program is ready) + while ( !m_connected) { + vTaskDelay(pdMS_TO_TICKS(100)); + } vTaskDelay(pdMS_TO_TICKS(100)); } @@ -75,13 +100,16 @@ void cdc_thread(void *ptr) } else if (cdc_rx_chars != 0) { // wait a short period if there are characters host -> probe -> target - xEventGroupWaitBits(events, EV_TX_COMPLETE | EV_STREAM | EV_RX, pdTRUE, pdFALSE, pdMS_TO_TICKS(1)); + // waiting is done below } else { // wait until transmission via USB has finished xEventGroupWaitBits(events, EV_TX_COMPLETE | EV_STREAM | EV_RX, pdTRUE, pdFALSE, pdMS_TO_TICKS(100)); } + // + // probe -> host + // if ( !xStreamBufferIsEmpty(stream_uart)) { // // transmit characters target -> picoprobe -> host @@ -103,17 +131,44 @@ void cdc_thread(void *ptr) } // - // transmit characters host -> probe -> target - // (actually don't know if this works, but note that in worst case this loop is taken just every 50ms. - // So this is not for high throughput) + // host -> probe -> target + // ----------------------- + // Characters are transferred bytewise to keep delays into the other direction low. + // So this is not a high throughput solution... // cdc_rx_chars = tud_cdc_n_available(CDC_UART_N); - if (cdc_rx_chars > 0 && uart_is_writable(PICOPROBE_UART_INTERFACE)) { - uint8_t ch; - size_t tx_len; - - tx_len = tud_cdc_n_read(CDC_UART_N, &ch, 1); - uart_write_blocking(PICOPROBE_UART_INTERFACE, &ch, tx_len); + if (cdc_rx_chars != 0) { + if (rtt_console_cb_exists()) { + // + // -> data is going thru RTT + // + uint8_t ch; + uint32_t tx_len; + + tx_len = tud_cdc_n_read(CDC_UART_N, &ch, sizeof(ch)); + if (tx_len != 0) { + rtt_console_send_byte(ch); + } + } + else { + // + // -> data is going thru UART + // assure that the UART has free buffer, otherwise wait (for a short moment) + // + if ( !uart_is_writable(PICOPROBE_UART_INTERFACE)) { + xEventGroupWaitBits(events, EV_TX_COMPLETE | EV_STREAM | EV_RX, pdTRUE, pdFALSE, pdMS_TO_TICKS(1)); + } + else { + uint8_t ch; + uint32_t tx_len; + + tx_len = tud_cdc_n_read(CDC_UART_N, &ch, sizeof(ch)); + if (tx_len != 0) { + led_state(LS_UART_TX_DATA); + uart_write_blocking(PICOPROBE_UART_INTERFACE, &ch, tx_len); + } + } + } } #else xStreamBufferReceive(stream_uart, cdc_tx_buf, sizeof(cdc_tx_buf), pdMS_TO_TICKS(500)); @@ -129,11 +184,7 @@ void cdc_uart_line_coding_cb(cdc_line_coding_t const* line_coding) * CDC bitrate updates are reflected on \a PICOPROBE_UART_INTERFACE */ { - vTaskSuspend(task_uart); - tud_cdc_n_write_clear(CDC_UART_N); - tud_cdc_n_read_flush(CDC_UART_N); uart_set_baudrate(PICOPROBE_UART_INTERFACE, line_coding->bit_rate); - vTaskResume(task_uart); } // cdc_uart_line_coding_cb #endif @@ -141,17 +192,12 @@ void cdc_uart_line_coding_cb(cdc_line_coding_t const* line_coding) void cdc_uart_line_state_cb(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) { - vTaskSuspend(task_uart); -#if CFG_TUD_CDC_UART - tud_cdc_n_write_clear(CDC_UART_N); -#endif + // CDC drivers use linestate as a bodge to activate/deactivate the interface. + if ( !dtr && !rts) { m_connected = false; } else { - vTaskResume(task_uart); + m_connected = true; } } // cdc_uart_line_state_cb @@ -222,7 +268,7 @@ void on_uart_rx(void) } if (cnt != 0) { - led_state(LS_UART_DATA); + led_state(LS_UART_RX_DATA); cdc_uart_put_into_stream(buf, cnt, true); } diff --git a/src/led.c b/src/led.c index 5c91dfcc3..41f90e63e 100644 --- a/src/led.c +++ b/src/led.c @@ -33,6 +33,7 @@ #include "FreeRTOS.h" #include "task.h" +#include "timers.h" #include "picoprobe_config.h" #include "led.h" @@ -41,14 +42,86 @@ static TaskHandle_t task_led; -static bool msc_connected; -static bool dapv1_connected; -static bool dapv2_connected; -static led_state_t sigrok_state; -static bool target_found; -static unsigned rtt_flash_cnt; -static uint64_t uart_data_trigger; -static uint64_t rtt_data_trigger; +static bool msc_connected; +static bool dapv1_connected; +static bool dapv2_connected; +static led_state_t sigrok_state; +static bool target_found; +static unsigned rtt_flash_cnt; +static uint64_t uart_data_trigger; +static uint64_t rtt_data_trigger; + + + +#ifdef PICOPROBE_LED_TARGET_TX + static TimerHandle_t timer_led_tx_off; + static void *timer_led_tx_off_id; + + + static void led_tx_off(TimerHandle_t xTimer) + { + gpio_put(PICOPROBE_LED_TARGET_TX, false); + } + + + static void rx_data_from_target(void) + { + static bool initialized; + + if ( !initialized) { + initialized = true; + gpio_init(PICOPROBE_LED_TARGET_TX); + gpio_set_dir(PICOPROBE_LED_TARGET_TX, GPIO_OUT); + } + gpio_put(PICOPROBE_LED_TARGET_TX, true); + xTimerReset(timer_led_tx_off, 10); + } // rx_data_from_target + + + static void rx_data_from_target_init(void) + { + timer_led_tx_off = xTimerCreate("led_tx_off", pdMS_TO_TICKS(5), pdFALSE, timer_led_tx_off_id, led_tx_off); + } // rx_data_from_target_init +#else + #define rx_data_from_target() + #define rx_data_from_target_init() +#endif + + + +#ifdef PICOPROBE_LED_TARGET_RX + static TimerHandle_t timer_led_rx_off; + static void *timer_led_rx_off_id; + + + static void led_rx_off(TimerHandle_t xTimer) + { + gpio_put(PICOPROBE_LED_TARGET_RX, false); + } + + + static void tx_data_to_target(void) + { + static bool initialized; + + if ( !initialized) { + initialized = true; + gpio_init(PICOPROBE_LED_TARGET_RX); + gpio_set_dir(PICOPROBE_LED_TARGET_RX, GPIO_OUT); + } + gpio_put(PICOPROBE_LED_TARGET_RX, true); + xTimerReset(timer_led_rx_off, 10); + } // tx_data_to_target + + + static void tx_data_to_target_init(void) + { + timer_led_rx_off = xTimerCreate("led_rx_off", pdMS_TO_TICKS(20), pdFALSE, timer_led_rx_off_id, led_rx_off); + } // tx_data_to_target_init +#else + #define tx_data_to_target() + #define tx_data_to_target_init() +#endif @@ -149,7 +222,7 @@ void led_thread(void *ptr) vTaskDelay(pdMS_TO_TICKS(200)); } led(0); - vTaskDelay(pdMS_TO_TICKS(1000 - rtt_flash_cnt * 220)); + vTaskDelay(pdMS_TO_TICKS(1000 - flash_cnt * 220)); } } } // led_thread @@ -205,12 +278,18 @@ void led_state(led_state_t state) rtt_flash_cnt = 2; break; - case LS_RTT_DATA: + case LS_RTT_RX_DATA: rtt_data_trigger = time_us_64(); + rx_data_from_target(); break; - case LS_UART_DATA: + case LS_UART_RX_DATA: uart_data_trigger = time_us_64(); + rx_data_from_target(); + break; + + case LS_UART_TX_DATA: + tx_data_to_target(); break; case LS_SIGROK_WAIT: @@ -235,5 +314,8 @@ void led_init(uint32_t task_prio) led(1); + rx_data_from_target_init(); + tx_data_to_target_init(); + xTaskCreateAffinitySet(led_thread, "LED", configMINIMAL_STACK_SIZE, NULL, task_prio, 1, &task_led); } // led_init diff --git a/src/led.h b/src/led.h index cde1d7d64..cb31b4a33 100644 --- a/src/led.h +++ b/src/led.h @@ -34,8 +34,9 @@ typedef enum _led_state { LS_TARGET_FOUND, // there is a target LS_NO_TARGET, // no target found LS_RTT_CB_FOUND, // found an RTT control block on target - LS_RTT_DATA, // RTT data received from target - LS_UART_DATA, // UART data received from target + LS_RTT_RX_DATA, // RTT data received from target + LS_UART_RX_DATA, // UART data received from target + LS_UART_TX_DATA, // UART data transmitted to target LS_MSC_CONNECTED, // MSC connected LS_MSC_DISCONNECTED, // MSC disconnected LS_DAPV1_CONNECTED, // DAPV1 connected diff --git a/src/main.c b/src/main.c index 1641cde8f..7a531253e 100644 --- a/src/main.c +++ b/src/main.c @@ -83,12 +83,14 @@ #define _DAP_PACKET_SIZE_OPENOCD CFG_TUD_VENDOR_RX_BUFSIZE #define _DAP_PACKET_COUNT_PYOCD 1 #define _DAP_PACKET_SIZE_PYOCD 1024 // pyocd does not like packets > 128 if COUNT != 1 +#define _DAP_PACKET_COUNT_UNKNOWN 1 +#define _DAP_PACKET_SIZE_UNKNOWN 64 #define _DAP_PACKET_COUNT_HID 1 #define _DAP_PACKET_SIZE_HID 64 -uint8_t dap_packet_count = _DAP_PACKET_COUNT_PYOCD; -uint16_t dap_packet_size = _DAP_PACKET_SIZE_PYOCD; +uint8_t dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN; +uint16_t dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN; static uint8_t TxDataBuffer[_DAP_PACKET_COUNT_OPENOCD * CFG_TUD_VENDOR_RX_BUFSIZE]; // maximum required size static uint8_t RxDataBuffer[_DAP_PACKET_COUNT_OPENOCD * CFG_TUD_VENDOR_RX_BUFSIZE]; // maximum required size @@ -211,8 +213,8 @@ void dap_task(void *ptr) uint32_t rx_len = 0; daptool_t tool = E_DAPTOOL_UNKNOWN; - dap_packet_count = _DAP_PACKET_COUNT_PYOCD; - dap_packet_size = _DAP_PACKET_SIZE_PYOCD; + dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN; + dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN; for (;;) { // disconnect after 1s without data if (swd_disconnect_requested && time_us_32() - last_request_us > 1000000) { @@ -223,8 +225,8 @@ void dap_task(void *ptr) sw_unlock("DAPv2"); } swd_disconnect_requested = false; - dap_packet_count = _DAP_PACKET_COUNT_PYOCD; - dap_packet_size = _DAP_PACKET_SIZE_PYOCD; + dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN; + dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN; tool = DAP_FingerprintTool(NULL, 0); } @@ -253,6 +255,10 @@ void dap_task(void *ptr) dap_packet_count = _DAP_PACKET_COUNT_OPENOCD; dap_packet_size = _DAP_PACKET_SIZE_OPENOCD; } + else if (tool == E_DAPTOOL_PYOCD) { + dap_packet_count = _DAP_PACKET_COUNT_PYOCD; + dap_packet_size = _DAP_PACKET_SIZE_PYOCD; + } } // @@ -515,6 +521,15 @@ int main(void) picoprobe_info_out(" [DAPLink MSC]"); #endif picoprobe_info_out("\n"); +#if defined(TARGET_BOARD_PICO) + picoprobe_info(" Probe HW: Pico\n"); +#elif defined(TARGET_BOARD_PICO_W) + picoprobe_info(" Probe HW: Pico_W\n"); +#elif defined(TARGET_BOARD_PICO_DEBUG_PROBE) + picoprobe_info(" Probe HW: Pico Debug Probe\n"); +#else + picoprobe_info(" Running on UNKNOWN board\n"); +#endif picoprobe_info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); events = xEventGroupCreate(); diff --git a/src/pico-sigrok/cdc_sigrok.c b/src/pico-sigrok/cdc_sigrok.c index 423e69ccd..d2d4d2fe5 100755 --- a/src/pico-sigrok/cdc_sigrok.c +++ b/src/pico-sigrok/cdc_sigrok.c @@ -96,6 +96,7 @@ void cdc_sigrok_write(const char *buf, int length) +#if CFG_TUD_CDC_SIGROK //Process incoming character stream //Return true if the device rspstr has a response to send to host //Be sure that rspstr does not have \n or \r. @@ -283,6 +284,7 @@ static bool process_char(sr_device_t *d, char charin) //default return 0 means to not send any kind of response return ret; } // process_char +#endif @@ -295,11 +297,11 @@ void cdc_sigrok_rx_cb(void) void cdc_sigrok_thread() { - static uint8_t cdc_tx_buf[CFG_TUD_CDC_TX_BUFSIZE]; - EventBits_t ev = EV_RX | EV_TX | EV_STREAM; for (;;) { #if CFG_TUD_CDC_SIGROK + static uint8_t cdc_tx_buf[CFG_TUD_CDC_TX_BUFSIZE]; + EventBits_t ev = EV_RX | EV_TX | EV_STREAM; size_t cdc_rx_chars; cdc_rx_chars = tud_cdc_n_available(CDC_SIGROK_N); diff --git a/src/rtt_console.c b/src/rtt_console.c index 0999310c5..27e343871 100755 --- a/src/rtt_console.c +++ b/src/rtt_console.c @@ -29,6 +29,8 @@ #include #include "FreeRTOS.h" +#include "event_groups.h" +#include "stream_buffer.h" #include "task.h" #include "target_board.h" @@ -43,14 +45,22 @@ -#define TARGET_RAM_START g_board_info.target_cfg->ram_regions[0].start -#define TARGET_RAM_END g_board_info.target_cfg->ram_regions[0].end +#define TARGET_RAM_START g_board_info.target_cfg->ram_regions[0].start +#define TARGET_RAM_END g_board_info.target_cfg->ram_regions[0].end -static const uint32_t segger_alignment = 4; -static const uint8_t seggerRTT[16] = "SEGGER RTT\0\0\0\0\0\0"; -static uint32_t prev_rtt_cb = 0; +#define STREAM_RTT_SIZE 128 +#define STREAM_RTT_TRIGGER 1 -static TaskHandle_t task_rtt_console = NULL; +#define EV_RTT_TO_TARGET 0x01 + +static const uint32_t segger_alignment = 4; +static const uint8_t seggerRTT[16] = "SEGGER RTT\0\0\0\0\0\0"; +static uint32_t prev_rtt_cb = 0; +static bool rtt_console_running = false; + +static TaskHandle_t task_rtt_console = NULL; +static StreamBufferHandle_t stream_rtt; +static EventGroupHandle_t events; @@ -123,25 +133,35 @@ static uint32_t search_for_rtt_cb(void) static void do_rtt_console(uint32_t rtt_cb) { - SEGGER_RTT_BUFFER_UP aUp; // Up buffer, transferring information up from target via debug probe to host - uint8_t buf[100]; + SEGGER_RTT_BUFFER_UP aUp; // Up buffer, transferring information up from target via debug probe to host + SEGGER_RTT_BUFFER_DOWN aDown; // Down buffer, transferring information from host via debug probe to target + uint8_t buf[128]; bool ok = true; + static_assert(sizeof(uint32_t) == sizeof(unsigned int)); // why doesn't segger use uint32_t? + if (rtt_cb < TARGET_RAM_START || rtt_cb >= TARGET_RAM_END) { return; } ok = ok && swd_read_memory(rtt_cb + offsetof(SEGGER_RTT_CB, aUp), (uint8_t *)&aUp, sizeof(aUp)); + ok = ok && swd_read_memory(rtt_cb + offsetof(SEGGER_RTT_CB, aDown), + (uint8_t *)&aDown, sizeof(aDown)); // do operations + rtt_console_running = true; while (ok && !sw_unlock_requested()) { - ok = ok && swd_read_memory(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[0].WrOff), (uint8_t *)&(aUp.WrOff), 2*sizeof(unsigned)); + ok = ok && swd_read_word(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[0].WrOff), (uint32_t *)&aUp.WrOff); if (aUp.WrOff == aUp.RdOff) { - vTaskDelay(pdMS_TO_TICKS(10)); + // -> no characters available + xEventGroupWaitBits(events, EV_RTT_TO_TARGET, pdTRUE, pdFALSE, pdMS_TO_TICKS(10)); } else { + // + // fetch characters from target + // uint32_t cnt; if (aUp.WrOff > aUp.RdOff) { @@ -154,14 +174,45 @@ static void do_rtt_console(uint32_t rtt_cb) memset(buf, 0, sizeof(buf)); ok = ok && swd_read_memory((uint32_t)aUp.pBuffer + aUp.RdOff, buf, cnt); - ok = ok && swd_write_word(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[0].RdOff), (aUp.RdOff + cnt) % aUp.SizeOfBuffer); + aUp.RdOff = (aUp.RdOff + cnt) % aUp.SizeOfBuffer; + ok = ok && swd_write_word(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[0].RdOff), aUp.RdOff); // put received data into CDC UART cdc_uart_write(buf, cnt); - led_state(LS_RTT_DATA); + led_state(LS_RTT_RX_DATA); + } + + if ( !xStreamBufferIsEmpty(stream_rtt)) { + // + // send data to target + // + ok = ok && swd_read_word(rtt_cb + offsetof(SEGGER_RTT_CB, aDown[0].RdOff), (uint32_t *)&(aDown.RdOff)); + if ((aDown.WrOff + 1) % aDown.SizeOfBuffer != aDown.RdOff) { + // -> space left in RTT buffer on target + uint32_t cnt; + size_t r; + + if (aDown.WrOff >= aDown.RdOff) { + cnt = aDown.SizeOfBuffer - aDown.WrOff; + } + else { + cnt = (aDown.RdOff - aDown.WrOff) - 1; + } + cnt = MIN(cnt, sizeof(buf)); + + r = xStreamBufferReceive(stream_rtt, &buf, cnt, 0); + if (r > 0) { + ok = ok && swd_write_memory((uint32_t)aDown.pBuffer + aDown.WrOff, buf, r); + aDown.WrOff = (aDown.WrOff + r) % aDown.SizeOfBuffer; + ok = ok && swd_write_word(rtt_cb + offsetof(SEGGER_RTT_CB, aDown[0].WrOff), aDown.WrOff); + + led_state(LS_UART_TX_DATA); + } + } } } + rtt_console_running = false; } // do_rtt_console @@ -252,9 +303,50 @@ void rtt_console_thread(void *ptr) +bool rtt_console_cb_exists(void) +{ + return rtt_console_running; +} // rtt_console_cb_exists + + + +void rtt_console_send_byte(uint8_t ch) +/** + * Write a byte into the RTT stream. + * If there is no space left in the stream, wait 10ms and then try again. + * If there is still no space, then drop a byte from the stream. + */ +{ + size_t available = xStreamBufferSpacesAvailable(stream_rtt); + if (available < sizeof(ch)) { + vTaskDelay(pdMS_TO_TICKS(10)); + available = xStreamBufferSpacesAvailable(stream_rtt); + if (available < sizeof(ch)) { + uint8_t dummy; + xStreamBufferReceive(stream_rtt, &dummy, sizeof(dummy), 0); + picoprobe_error("rtt_console_send_byte: drop byte\n"); + } + } + xStreamBufferSend(stream_rtt, &ch, sizeof(ch), 0); + xEventGroupSetBits(events, EV_RTT_TO_TARGET); +} // rtt_console_send_byte + + + void rtt_console_init(uint32_t task_prio) { picoprobe_debug("rtt_console_init()\n"); + events = xEventGroupCreate(); + + stream_rtt = xStreamBufferCreate(STREAM_RTT_SIZE, STREAM_RTT_TRIGGER); + if (stream_rtt == NULL) { + picoprobe_error("rtt_console_init: cannot create stream_rtt\n"); + } + xTaskCreateAffinitySet(rtt_console_thread, "RTT_CONSOLE", configMINIMAL_STACK_SIZE, NULL, task_prio, 1, &task_rtt_console); + if (task_rtt_console == NULL) + { + picoprobe_error("rtt_console_init: cannot create task_rtt_console\n"); + } } // rtt_console_init diff --git a/src/rtt_console.h b/src/rtt_console.h index f2f170b4f..c2e4ed486 100755 --- a/src/rtt_console.h +++ b/src/rtt_console.h @@ -36,6 +36,8 @@ void rtt_console_init(uint32_t task_prio); +void rtt_console_send_byte(uint8_t ch); +bool rtt_console_cb_exists(void); #ifdef __cplusplus diff --git a/src/sw_lock.c b/src/sw_lock.c index 678a6b477..486d5e119 100755 --- a/src/sw_lock.c +++ b/src/sw_lock.c @@ -74,6 +74,7 @@ void sw_unlock(const char *who) BaseType_t r; r = xSemaphoreGive(sema_lock); + (void)r; // suppress warning from compiler picoprobe_debug("sw_unlock('%s') = %ld\n", who, r); } // sw_unlock