Skip to content

libbladeRF: fix unit mix-up in ts_remaining() for X2 layouts - #1080

Open
wormuz wants to merge 1 commit into
Nuand:masterfrom
wormuz:fix/sync-x2-ts-remaining
Open

libbladeRF: fix unit mix-up in ts_remaining() for X2 layouts#1080
wormuz wants to merge 1 commit into
Nuand:masterfrom
wormuz:fix/sync-x2-ts-remaining

Conversation

@wormuz

@wormuz wormuz commented Aug 19, 2026

Copy link
Copy Markdown

curr_msg_off counts samples: it is advanced by samples_to_copy and compared against samples_per_msg. ts_remaining() subtracted it from samples_per_msg / samples_per_ts, which counts time steps per message. The units only coincide for single-channel layouts, where samples_per_ts == 1.

In X2 mode the unsigned subtraction underflows as soon as the offset passes half a message:

  • with assertions enabled, a bladerf_sync_rx() call that seeks to a timestamp inside the current message aborts the whole process: sync.c:451: ts_remaining: Assertion 'ret <= UINT_MAX' failed.
  • with NDEBUG, the huge result sends the seek logic into the fast forward within the current message branch when the target actually lies beyond it, and curr_msg_off runs past the message boundary — the stream stalls instead of crashing. This looks like the dual-channel stall reported in Dual-channel async receive with metadata broken for FPGA code 0.12.0 through 0.15.0 (xA9)? #944.

Reproduced on a bladeRF 2.0 micro xA4 (FX3 2.6.0, FPGA 0.16.0) with an RX_X2 SC16_Q11_META stream reading back-to-back explicit timestamps: the abort fires within the first few reads. With the units fixed, the same sequence completes 3000/3000 contiguous reads with no timestamp drift (past the ~1794-buffer stall point reported in #944), and single-channel metadata reads are unaffected.

The fix subtracts the sample offset first and then converts to time steps; a new assertion documents the invariant (curr_msg_off <= samples_per_msg) the old expression silently assumed.

curr_msg_off counts samples: it is advanced by samples_to_copy and
compared against samples_per_msg. ts_remaining() subtracted it from
samples_per_msg / samples_per_ts, which counts TIME STEPS per message.
The units only coincide for single-channel layouts (samples_per_ts == 1).

In X2 mode the unsigned subtraction underflows as soon as the offset
passes half a message. With assertions enabled a bladerf_sync_rx() call
that seeks to a timestamp inside the current message aborts the process:

    sync.c:451: ts_remaining: Assertion 'ret <= UINT_MAX' failed.

With NDEBUG the huge result sends the seek logic into the 'fast forward
within the current message' branch when the target lies beyond it, and
curr_msg_off runs past the message boundary - the stream stalls instead
of crashing. This matches the dual-channel stalls reported in Nuand#944.

Reproduced on a bladeRF 2.0 micro xA4 (FX3 2.6.0, FPGA 0.16.0) with an
RX_X2 SC16_Q11_META stream reading back-to-back explicit timestamps:
the abort fires within the first few reads. With the units fixed the
same sequence completes 3000/3000 contiguous reads with no timestamp
drift, and single-channel metadata reads are unaffected.

Subtract the sample offset first, then convert to time steps. The new
assertion documents the invariant the old expression silently assumed.

(cherry picked from commit 0960e7f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant