gateware+libm2sdr: hardware timed-TX gate (deterministic on-air timing)#148
Draft
simonerni wants to merge 1 commit into
Draft
gateware+libm2sdr: hardware timed-TX gate (deterministic on-air timing)#148simonerni wants to merge 1 commit into
simonerni wants to merge 1 commit into
Conversation
The FPGA header extractor now holds each TX DMA buffer until FPGA time + tx_offset reaches the buffer's header air-time, then releases it, so the header ns-timestamp IS the on-air time, deterministic to the 10 ns time grid. Per-buffer semantics match UHD/Soapy: header timestamp 0 = untimed = transmit immediately (continuous streaming, zero added latency); nonzero = gated to that air-time; a frame that arrives past its air-time is dropped whole (the RFIC airs zeros) and counted as a TX underflow (CSR header_tx_underflow, m2sdr_get_tx_underflow). A multi-buffer timed burst stamps only its first buffer; the air-time is quantized to the 10 ns grid, with m2sdr_set_tx_offset for loopback-calibrated TX-pipeline compensation. The gate FSM handles both header widths; the 128-bit path is dormant until a wide datapath instantiates it. Validated on the TX->RX loopback: continuous untimed tone clean; a marker tagged at X airs at X + a deterministic pipeline latency (1250 ns @30.72; 358 ns on the 128-bit @122.88 path), calibrated to X via tx_offset; too-late frames drop and increment the underflow counter; the stream re-anchors on the buffer grid after an underflow (no sample offset). The gate FSM is covered hardware-free by test/test_header.py (13/13, 64- and 128-bit). New harnesses: scripts/timed_tx_selftest (air-time accuracy + calibration + drop) and scripts/latency_probe (RX-delivery latency). CI: cppcheck analyze-libm2sdr clean, pytest passed, user + SoapySDR builds clean.
simonerni
marked this pull request as draft
July 14, 2026 09:15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hardware timed-TX gate (deterministic on-air timing)
Adds a hardware timed-TX gate to the FPGA header extractor: each TX DMA buffer is
held until the FPGA time reaches the buffer's header air-time, then released — so the
ns timestamp the host writes is the on-air time, deterministic to the 10 ns time grid.
This replaces host-side ring-lead / pacing workarounds with a single, exact mechanism.
Semantics (per-buffer, matches UHD
has_time_spec/ SoapySOAPY_SDR_HAS_TIME)0(untimed)FPGA_time + tx_offsetreaches it.timestamp == 0is a safe sentinel: the FPGA time counter is 0-based at power-on, soair-time 0 is always in the past — no legitimate timed frame targets it.
behind it (matches "time applies to the first sample of the burst"). Air-time is quantized
to the 10 ns grid (round-to-nearest); libm2sdr floors any non-zero request up to one tick
so it can never collide with the untimed sentinel.
Host API (libm2sdr)
m2sdr_set_tx_offset(dev, ns)— loopback-calibrated TX-pipeline compensation, so acalibrated "transmit at X" puts the signal on the air at X.
m2sdr_get_tx_underflow(dev, &count)— the on-air TX underflow counter (timed frames thatmissed their air-time), distinct from the DMA-ring underflow in
m2sdr_get_stats().The gate FSM handles both the 64-bit and 128-bit header widths; the 128-bit path is generic
plumbing that stays dormant until a wide datapath instantiates it.
Validation (TX→RX loopback)
(1250 ns @ 30.72, 358 ns on the 128-bit @ 122.88 path), std ≈ 11 ns — sub-sample.
Programming
tx_offsetdrives the RX-observed time to X (residual ≈ 6 ns, within the grid).number of whole DMA buffers (reported, not silent), never a sub-buffer sample offset.
Tests & CI
test/test_header.py— the gate FSM proven hardware-free in simulation (13/13:untimed passthrough, release-on-time, hold-then-release, drop-late + underflow,
tx_offset,recover-after-drop; 64- and 128-bit).
scripts/timed_tx_selftest.c— hardware air-time accuracy + calibration + drop harness.scripts/latency_probe.c— RX-delivery latency measurement.cppcheck analyze-libm2sdrclean,pytestgreen, kernel + user + SoapySDR builds clean.