Skip to content

rtl8733b: CCK<->OFDM TSSI table switch costs 84 ms (136 USB register ops) inside send_packet #389

Description

@snokvist

Summary

On the RTL8733B backend, switching the TSSI thermal table between the CCK and OFDM/HT variants costs 84 ms. select_tssi_rate_table runs inside send_packet, so a stream that alternates CCK and OFDM rates is capped at ~11.5 fps.

This is latent, not a live regression. select_tssi_rate_table early-returns when the rate class is unchanged, so a single-rate stream — the normal FPV/video case — pays nothing. It predates #388 and is not caused by it.

Where the 84 ms goes

Measured on the DUT (bare unbranded RTL8731BU module, 0bda:f72b cut D, USB high speed) with an instrumented build counting every BB register access and timing each stage. 60 consecutive switches, steady state:

stage ms reads writes ops
enable_tssi_tracking 44.99 35 35 70
disable_tssi_tracking 25.77 13 34 47
prepare_tssi_thermal 13.50 2 17 19
total 84.26 50 86 136

There are no sleeps in the path — it is 136 USB control transfers. build/reglat on the same adapter measures a register round trip at mean 375 us / p99 407 us, and the mixed read/write traffic here averages ~620 us/op.

The structural cause: to change 16 BB dwords, the backend tears down and rebuilds the entire TSSI front-end. 32 of the 136 ops are the 16-command ANAPAR sequence (write_tssi_anapar), written out by restore_tssi_analog and written straight back by apply_tssi_analog. The channel and band do not change across a rate-table switch, so that analog state is restored only to be re-applied microseconds later.

The premise looks wrong

Rtl8733bDevice::select_tssi_rate_table says the table "cannot be changed while closed-loop tracking is enabled". The vendor's own writer disagrees: _halrf_tssi_set_tmeter_tbl_8733b (hal/phydm/halrf/rtl8733b/halrf_tssi_8733b.c in the pinned reference/rtl8733bu-20230626) writes R_0x4380 and the 16 dwords at 0x4200..0x423c directly and touches no enable bit.

Caveat, stated plainly: the vendor only ever calls it from the full _halrf_tssi_8733b setup sequence, so there is no vendor precedent for a runtime swap in either direction. The vendor picks a table once at TSSI setup from whatever phydm_get_tx_rate returns and leaves it.

The hardware accepts an in-place rewrite

Throwaway build, writing the table with tracking live and reading it back:

  • 101/101 swaps readback-verified across all 16 words against the rate-specific plan.
  • 0x4318 stayed at 7 (tracking enabled) across every swap.
  • 13.77 ms / 20 ops, against 84.26 ms / 136 ops — a 6.1x reduction.

The two plans genuinely differ (8 of the 16 words), so the readback was tracking a real change rather than a no-op.

Why this is not already a PR

TSSI is closed-loop PA control. Register readback proves the write lands; it does not prove the tracking loop behaves correctly across a live table change. Validating that needs an independent witness and ideally an SDR — neither was available — and the failure mode of getting it wrong is transmitting at an uncalibrated power setting.

Suggested work

  1. Add a table-swap-only path to select_tssi_rate_table that writes 0x4380 + 0x4200..0x423c in place and readback-verifies, skipping the disable/enable pair.
  2. Gate it behind a DeviceConfig field until validated, defaulting to the current conservative path.
  3. Validate with a witness at both rate classes across repeated swaps, and an SDR power check either side of a swap to confirm the closed loop settles to the same output power as the teardown path.
  4. If it holds, make it the default and drop the comment claiming the table cannot be changed while tracking is enabled.

Separately: write_tssi_anapar is 16 dwords written twice per switch. If the in-place path lands, that cost disappears with it; if it does not, the restore/re-apply pair is worth revisiting on its own.

Reproducing

# alternating CCK/OFDM so every frame forces a table switch
sudo DEVOURER_PID=0xf72b DEVOURER_CHANNEL=6 DEVOURER_TX_RATE=1M \
     DEVOURER_TX_MCS_SWEEP="1M,6M,2M,MCS0,5.5M,MCS4,11M,MCS7" \
     DEVOURER_TX_MCS_STEP_MS=17 DEVOURER_TX_GAP_US=2000 \
     DEVOURER_TX_FRAMES=500 ./build/txdemo

500 frames takes ~45 s (~87 ms/frame). The single-rate control — same command without DEVOURER_TX_MCS_SWEEP — takes ~3.5 s (~2.6 ms/frame).

Note that on master the per-switch rtl8733b.tssi_thermal_table log cannot show whether the swap took effect: it prints words 0 and 15, which are identical in both tables. #388 changes it to report the words that differ plus a fold over all 16.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions