Skip to content

perf(dshot): decode the frame in a single pass into one packed word#9

Open
dakejahl wants to merge 1 commit into
mainfrom
perf/dshot-single-pass-decode
Open

perf(dshot): decode the frame in a single pass into one packed word#9
dakejahl wants to merge 1 commit into
mainfrom
perf/dshot-single-pass-decode

Conversation

@dakejahl

@dakejahl dakejahl commented Jun 22, 2026

Copy link
Copy Markdown

Summary

Decode the DShot frame in a single pass, packing the 16 pulse bits into one uint16_t instead of a 16-entry global array, and derive the CRC, throttle value and telemetry bit with shifts and masks.

Problem

The decoder wrote each of the 16 decoded bits into a global int dpulse[16] (64 bytes of RAM) and then recombined them with long per-bit shift expressions to form the check/calculated CRC, the 11-bit value, and the telemetry flag.

Solution

Pack the bits directly into a local uint16_t frame as they are decoded, then compute everything with masks/shifts on that word. Functionally identical — verified bit-for-bit, including the dshot_telemetry CRC inversion (~x + 16 and ~x & 0xF are equal for a 4-bit value) — and it removes the 64-byte global. Also drops a redundant signaltimeout = 0 in the CRC-match branch: the reset at the top of the valid-frametime block already runs on every path that reaches it, so the inner one was dead (behavior unchanged).

@dakejahl dakejahl added the risk:2 Low — isolated, verified, easy to reason about label Jun 22, 2026
computeDshotDMA unpacked the 16 pulses into a 16-entry int array and
then walked it three more times to assemble the CRC nibbles and the
11-bit value. Pack the pulses into one uint16_t as they are measured
(bit 15-i is pulse i) and derive everything with shifts and masks:
checkCRC = frame & 0xF, calcCRC = ((frame>>4)^(frame>>8)^(frame>>12))
& 0xF, value = frame >> 5, telemetry request = frame & 0x10. The
inverted-CRC bidirectional case (~checkCRC) & 0xF equals the old
~checkCRC + 16 for all nibble values. Bit-for-bit equivalent to the
old decode and frees the 64 byte dpulse array.

While here, drop a redundant signaltimeout = 0 inside the CRC-match
branch: the reset at the top of the valid-frametime block already
runs on every path that reaches it, so the inner store was dead.
@dakejahl dakejahl force-pushed the perf/dshot-single-pass-decode branch from 2aae2e0 to 2b58907 Compare June 22, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewed risk:2 Low — isolated, verified, easy to reason about

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant