CMSIS-DAP v2: a zero-length IN packet after a 512-byte reply is read as the next command's reply (Microchip PICkit Basic)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 50/100
Research direction
Start in PyUSBv2.rx_task, where one ep_in.read() is taken per command and a zero-length read becomes the reply to the next command. Read the linked #1871 for the OUT-direction case. The reported workaround skips empty reads and reads again. Done when a 254-word read_memory_block32 succeeds on the PICkit Basic and the zero-length packets are skipped. Verifying this needs that probe or a CMSIS-DAP v2 device that sends a zero-length packet after a full-size reply.
Written by the indexing model from the issue text.
Description
pyOCD: 0.45.1, Windows 10, libusb from libusb-package, Python 3.14
Probe: Microchip MPLAB PICkit Basic on its CMSIS-DAP v2 firmware (switched with Microchip's pycmsisdapswitcher 2.0.2.19), USB high speed, VID 04D8 PID 90AC. Its CMSIS-DAP interface has bulk OUT 0x01 and bulk IN 0x82 (wMaxPacketSize 512), and SWO IN 0x83. DAP_Info reports a packet size of 512 and a packet count of 1.
Target: nRF51822 (-t nrf51822), SWD at 1 MHz.
What happens
When a reply is exactly 512 bytes, the probe ends the IN transfer with a zero-length packet. PyUSBv2.rx_task reads one USB packet per command, so it reads the 512-byte reply, and then the zero-length packet becomes the "reply" to the next command. In the trace below, a read_memory_block32(0x18000, 254) makes a DAP_TransferBlock of 127 words. Its reply is 4 + 127 × 4 = 512 bytes:
USB OUT> (5) 06 00 7f 00 0f
USB RD < (512) 06 7f 00 01 01 d0 0d 48 17 e0 0d 49 01 aa 0b f0 27 ...
USB OUT> (9) 05 00 02 05 f0 ed 00 e0 0f
USB RD < (0)
The next decode fails: IndexError: bytearray index out of range in _decode_transfer_block_data (if data[0] != Command.DAP_TRANSFER_BLOCK), or a DAP_Transfer decode error. The real reply to that command is never read, so it stays in the probe. Every command after it times out (USBTimeoutError [Errno 10060]) until the probe is unplugged and plugged back in.
Reads whose replies are shorter than 512 bytes work. I tested blocks of 16, 100, 127, 128 and 200 words, with replies of up to 511 bytes. Blocks of 254 and 255 words fail every time.
Workaround
No CMSIS-DAP reply is empty, so in rx_task, reading again when ep_in.read() returns 0 bytes fixes it:
while True:
data = self.ep_in.read(self.packet_size, timeout=self.DEFAULT_USB_TIMEOUT_MS).tobytes()
if data:
break
self.rcv_data.put(data)
With this change, erasing an nRF51822, programming 166,548 bytes and reading all of them back works every time. It skips 160 zero-length packets per run.
Note
This is the IN-direction counterpart of #1871, which was about the OUT direction. The probe may be the one that is wrong to send a ZLP after a reply equal to the DAP packet size. But a zero-length read is never a valid CMSIS-DAP reply, so skipping it in rx_task costs nothing.
- Dominant language
- Python
- Stars
- 1.5k
- Forks
- 567
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 20
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from pyocd/pyOCD
-
stlink_probe._get_board_id() raises TypeError, not KeyError, when mbed.htm exists but has no TargetIDPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 3/5 Half a day Newbie friendliness 70/100
pyocd/pyOCD#2042 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
pyocd/pyOCD#2031 · 4 comments ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
nrf54lm20a: flash algorithm faults (IPSR=3) and 'pyocd reset' is a no-opPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Maintainers usually reply within 1 day
Similar issues
-
area:core-platform effort:S priority:P3 status:in-review type:test
Difficulty 2/5 Under an hour Newbie friendliness 80/100
randomparity/kdive#3246 · 1 comment ·
Maintainers usually reply within 1 day
-
Requantification library converts m/z to mass with the hydrogen-atom mass instead of the proton massOpenbug
Difficulty 1/5 Under an hour Newbie friendliness 85/100
nf-core/metaboigniter#130 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
common-workflow-language/cwl-utils#477 ·
Maintainers usually reply within 1 day
-
Add dependabotOpenfeat
Difficulty 2/5 Under an hour Newbie friendliness 72/100
open-nudge/opentemplate#204 ·
Maintainers usually reply within 1 day
-
github-stale-ci-pr-closer: KV state is double-wrapped, never persists, and hits the depth-32 limitPossibly taken @VascoSch92 claimed this today. Openpriority:medium ready-for-dev
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
OpenHands/extensions#781 ·
Maintainers usually reply within 1 day