Skip to content

rtl8733b: port FastRetune — intra-band hop with TSSI tracking kept live - #398

Merged
josephnef merged 3 commits into
OpenIPC:masterfrom
snokvist:feat/8733b-fastretune
Aug 15, 2026
Merged

rtl8733b: port FastRetune — intra-band hop with TSSI tracking kept live#398
josephnef merged 3 commits into
OpenIPC:masterfrom
snokvist:feat/8733b-fastretune

Conversation

@snokvist

Copy link
Copy Markdown
Contributor

What

Ports FastRetune to the RTL8733B backend — the last generation without a fast path. Intra-band, same-width hops only; a band/width change or cold radio declines with the chip untouched and the wrapper falls back to the full SetMonitorChannel, per the IRtlDevice contract. AdapterCaps.fastretune_ok now states it.

Why / how it was sized

Per-stage profiling of the full path on the USB-HS validation unit (0bda:f72b, cut D): ~330–440 ms total = ~165 ms TSSI disable/re-enable + ~90 ms band/bandwidth switches + ~60 ms channel switch + HALMAC/readback. A same-band same-width hop needs almost none of that. The fast path keeps: RF18 synth program from a compose cache (bandwidth bits preserved; primed by one read on the first fast hop after each full set), RF19 sub-band bits and channel-keyed BB constants on bucket change only, BB reset + IGI toggle. TSSI tracking stays enabled across the hop, with the per-channel rate-offset dwords rewritten in place only when the plan differs — the in-place-with-tracking-live shape #389 validated. Everything that can refuse is computed before the first chip write.

Measured (one unit — the standing 8733B caveat — no SDR)

  • Call: ~55 ms (full: 330–440). Radio-live from hop start: 10.0 ms p50 (min 3.6 / p90 12.9 / max 40.3 — a 1-in-20 tail, not noise), vs 70–100 ms radio-live through the full path. 20-cycle settle harness against a 1 kHz witness emitter, warm-ups excluded, honest-refusal counters all zero.
  • Register parity: read_channel_state().matches(plan) + synth-ready held on 7/7 fast hops (5180→5805).
  • TSSI-live TX proof: 300-frame MCS0 burst injected immediately after five fast hops — 299/300 decoded by an independent RTL8812AU witness.
  • Full-path regression: unchanged (~340–440 ms call, 70–100 ms radio-live, same session).
  • Host build green, ctest 53/53.

Docs updated (src/rtl8733b/CLAUDE.md, root CLAUDE.md frequency-hopping section, docs/rtl8733b.md deferred matrix) with the adversarial counterparts alongside the numbers, per the repo rule.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Hri3rWJfbvw8iCyDDwXRit

The full SetMonitorChannel on this USB-HS part costs ~330-440 ms,
profiled per-stage as ~165 ms TSSI disable/re-enable, ~90 ms
band+bandwidth switches it does not need on a same-band same-width hop,
and ~60 ms channel switch. Phy8733b::fast_retune keeps the subset a hop
needs: the RF18 synth program from a compose cache (bandwidth bits
preserved; primed by one read on the first fast hop after a full set),
RF19 sub-band bits and the channel-keyed BB constants on bucket change
only, then the BB reset + IGI toggle that restart the RX engine. TSSI
tracking stays enabled across the hop with the per-channel rate-offset
dwords rewritten in place when the plan differs — the OpenIPC#389 shape.
Everything that can refuse is computed before the first chip write, so
a declined hop (band/width change, cold radio) leaves the chip untouched
and the device wrapper falls back to the full path per the IRtlDevice
contract. AdapterCaps now states fastretune_ok.

Measured on the validation unit (0bda:f72b, USB HS, 20-cycle settle
harness, 1 kHz witness emitter): call ~55 ms, radio-live 10.0 ms p50
from hop start (min 3.6 / p90 12.9 / max 40.3 — a 1-in-20 tail), vs
70-100 ms radio-live through the full path. Channel-state readback
parity held 7/7 hops; a 300-frame post-hop burst decoded 299/300 at an
RTL8812AU witness, so the TSSI-live claim is air-verified. One unit, no
SDR: radiated power across a hop stays uncharacterized, like every
RF-domain quantity on this backend.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hri3rWJfbvw8iCyDDwXRit
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

RTL8733B: add FastRetune for intra-band hops with live TSSI tracking

✨ Enhancement 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add RTL8733B FastRetune fast path for same-band, same-width channel hops.
• Keep TSSI tracking enabled across hops; rewrite per-channel rate offsets in place.
• Advertise capability via AdapterCaps and document measured hop latency/constraints.
Diagram

graph TD
  caller([Caller / demos]) --> iface["IRtlDevice::FastRetune"] --> dev["Rtl8733bDevice::FastRetune"] --> phy["Phy8733b::fast_retune"] --> hw[("RTL8733B RF/BB regs")]
  phy -->|"declines"| full["SetMonitorChannel"] --> hw
  dev --> caps["AdapterCaps.fastretune_ok"]
  subgraph Legend
    direction LR
    _act(["Caller"]) ~~~ _svc["Software component"] ~~~ _hw[("Hardware regs")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Simpler hop: disable/re-enable TSSI around FastRetune
  • ➕ Reduces state bookkeeping (_fr_tssi_offsets/_fr_tssi_path) and in-place rewrite risk
  • ➕ Uses already-validated full-path TSSI sequencing
  • ➖ Sacrifices the major latency win (TSSI disable/enable dominates full path)
  • ➖ More dead-air and slower sweep/hop dwell cadence
2. Firmware/HALMAC-assisted retune command (if available)
  • ➕ Potentially fewer host-side control transfers and better tail latency
  • ➕ Centralizes chip-specific sequencing in firmware
  • ➖ May not exist for 8733B; requires discovery/reverse engineering
  • ➖ Harder to validate parity and failure safety vs explicit host writes
3. Shared cross-family FastRetune framework (common cache + admission logic)
  • ➕ Reduces duplicated “fast hop” patterns across chip backends
  • ➕ More consistent semantics/telemetry across generations
  • ➖ Upfront refactor cost and higher blast radius
  • ➖ Risk of over-generalizing hardware-specific invariants (TSSI, BB buckets, RF words)

Recommendation: Current approach is the right performance/complexity trade: an explicit, host-driven fast path with strict pre-write admission checks (chip untouched on decline) matches the IRtlDevice fallback contract while retaining live TSSI tracking. The main review focus should be on correctness of the admission gates (band/width/offset, cold-radio), cache invalidation on full set_channel, and ensuring the in-place TSSI rate-offset rewrite is safe under all tracked modes.

Files changed (7) +246 / -8

Enhancement (4) +206 / -0
Phy8733b.cppImplement Phy8733b::fast_retune with RF compose cache and live TSSI rewrite +144/-0

Implement Phy8733b::fast_retune with RF compose cache and live TSSI rewrite

• Introduces the RTL8733B fast hop core: precomputes refusal conditions, primes/uses cached RF18/RF19 words, programs synth and selective BB constants on bucket changes, and restarts RX via BB reset + IGI toggle. Keeps TSSI tracking enabled across the hop and rewrites per-channel rate-offset dwords only when the plan differs; adds bookkeeping to track current plan and cached state across full sets and tracking enable/disable.

src/rtl8733b/Phy8733b.cpp

Phy8733b.hExpose fast_retune API and add fast-retune state tracking fields +36/-0

Expose fast_retune API and add fast-retune state tracking fields

• Adds the fast_retune method declaration with detailed contract comments (admission, caching knob, fallback expectations). Introduces persisted state for current channel plan, cached RF18/RF19 compose words, and the currently-programmed TSSI rate-offset table/path bit.

src/rtl8733b/Phy8733b.h

Rtl8733bDevice.cppOverride IRtlDevice::FastRetune and advertise fastretune_ok capability +21/-0

Override IRtlDevice::FastRetune and advertise fastretune_ok capability

• Adds an RTL8733B FastRetune override that attempts the PHY fast path under the device lock and falls back to SetMonitorChannel when declined. Sets AdapterCaps.fastretune_ok to true to reflect that a lean fast-hop implementation exists.

src/rtl8733b/Rtl8733bDevice.cpp

Rtl8733bDevice.hDeclare FastRetune override for RTL8733B device +5/-0

Declare FastRetune override for RTL8733B device

• Extends the device interface to override FastRetune with documentation about supported scope and fallback behavior. Keeps the default cache_rf parameter aligned with the IRtlDevice contract.

src/rtl8733b/Rtl8733bDevice.h

Documentation (3) +40 / -8
CLAUDE.mdDocument RTL8733B FastRetune availability and hop performance +10/-6

Document RTL8733B FastRetune availability and hop performance

• Updates the project-level frequency hopping documentation to state RTL8733B now supports FastRetune with live TSSI tracking. Adds measured latency numbers and clarifies fallback behavior on non-eligible hops.

CLAUDE.md

rtl8733b.mdUpdate RTL8733B validation matrix to include FastRetune witness results +7/-1

Update RTL8733B validation matrix to include FastRetune witness results

• Moves FastRetune out of the “not validated” bucket and records independent witness validation results and measured hop timing. Explicitly notes remaining SDR-gated unknowns (e.g., radiated power across a hop).

docs/rtl8733b.md

CLAUDE.mdAdd RTL8733B FastRetune contract, sizing rationale, and measurements +23/-1

Add RTL8733B FastRetune contract, sizing rationale, and measurements

• Adds a dedicated FastRetune section describing the exact supported scope (intra-band, same-width) and the “TSSI kept live” in-place rewrite contract. Records profiling breakdown, measured hop/radio-live timing, and validation counterparts/limitations.

src/rtl8733b/CLAUDE.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 15, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (2) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Stale TSSI-DE on hop ✓ Resolved 🐞 Bug ≡ Correctness
Description
Phy8733b::fast_retune keeps TSSI tracking enabled but doesn’t update the channel-group-dependent
TSSI-DE offsets that are programmed on each full channel set, so a fast hop that crosses DE/trim
group boundaries can leave the power-tracking loop using the previous channel’s DE calibration. This
can mis-calibrate TX power on the new channel until a full SetMonitorChannel re-applies the correct
offsets.
Code

src/rtl8733b/Phy8733b.cpp[R1650-1653]

+  if (tssi && tssi->rate_offsets != *_fr_tssi_offsets) {
+    for (size_t i = 0; i < tssi->rate_offsets.size(); ++i)
+      set_bb(static_cast<uint16_t>(0x3a00 + i * 4), kDwordMask,
+             tssi->rate_offsets[i]);
Evidence
The full TX-power setup programs channel-dependent DE offsets (via tssi_de_plan and
prepare_tssi_offsets) before enabling tracking; the new fast_retune path keeps tracking enabled but
only rewrites the 0x3a00 rate-offset dwords and never reprograms DE registers
(0x433c/0x434c/0x4334/0x4344/0x43b0/0x43b4/0x43b8). Therefore, a fast hop across channel buckets can
keep DE calibration from the previous channel.

src/rtl8733b/Phy8733b.cpp[275-374]
src/rtl8733b/Phy8733b.cpp[1397-1456]
src/rtl8733b/Rtl8733bDevice.cpp[151-195]
src/rtl8733b/Phy8733b.cpp[1531-1657]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Phy8733b::fast_retune()` keeps TSSI tracking live and only rewrites the rate-offset dwords (0x3a00..), but the full TX-power bring-up path also programs channel-bucketed TSSI-DE offsets (via `prepare_tssi_offsets()` and `tssi_de_plan()`), which vary across channel ranges. A fast hop that crosses those buckets can therefore retain stale DE calibration from the previous channel.
### Issue Context
- `tssi_de_plan()` chooses `de_group`/`trim_group` based on the requested primary channel.
- `prepare_tssi_offsets()` writes the DE registers for that plan during the full path.
- `fast_retune()` does not touch those DE registers while allowing intra-band hops.
### Fix Focus Areas
Pick one safe approach:
1) **Conservative & low-risk:** while `tssi_live` is true, decline the fast path when the DE/trim grouping would change (return `false` so the wrapper falls back to full `SetMonitorChannel`).
2) **More aggressive:** implement an in-place DE rewrite that is safe with tracking live (with readback verification), and update internal bookkeeping accordingly.
- src/rtl8733b/Phy8733b.cpp[1531-1658]
- src/rtl8733b/Phy8733b.cpp[275-375]
- src/rtl8733b/Phy8733b.cpp[1397-1456]
- src/rtl8733b/Phy8733b.h[273-286]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. fast_retune lacks config gate 📎 Requirement gap ☼ Reliability
Description
The new Phy8733b::fast_retune path performs in-place TSSI rate-offset rewrites while tracking
remains enabled, and Rtl8733bDevice::FastRetune will use it whenever _tssi_tracking is true.
This violates the requirement to gate tracking-live in-place swaps behind a DeviceConfig flag
(default conservative) so the behavior can be disabled on unvalidated/unknown units.
Code

src/rtl8733b/Phy8733b.cpp[R1650-1654]

+  if (tssi && tssi->rate_offsets != *_fr_tssi_offsets) {
+    for (size_t i = 0; i < tssi->rate_offsets.size(); ++i)
+      set_bb(static_cast<uint16_t>(0x3a00 + i * 4), kDwordMask,
+             tssi->rate_offsets[i]);
+    _fr_tssi_offsets = tssi->rate_offsets;
Evidence
PR Compliance ID 3 requires the tracking-live in-place swap behavior be guarded by a DeviceConfig
option with a conservative default. The added fast retune implementation rewrites the TSSI
rate-offset dwords in place when tssi_live is true (tracking enabled), and the new
Rtl8733bDevice::FastRetune calls it based on _tssi_tracking without any configuration gate.

Gate the in-place swap behavior behind a DeviceConfig flag (default conservative)
src/rtl8733b/Phy8733b.cpp[1650-1654]
src/rtl8733b/Rtl8733bDevice.cpp[353-368]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Phy8733b::fast_retune` can rewrite TSSI rate-offset dwords in-place while tracking is enabled, but there is no `DeviceConfig` flag to disable this behavior (default conservative).
## Issue Context
PR Compliance ID 3 requires gating tracking-live in-place swap behavior behind a config flag, defaulting to the conservative behavior.
## Fix Focus Areas
- src/rtl8733b/Rtl8733bDevice.cpp[353-368]
- src/rtl8733b/Phy8733b.cpp[1646-1655]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. No SDR power validation documented 📎 Requirement gap ☼ Reliability
Description
The documentation added for RTL8733B FastRetune explicitly states there was no SDR validation and
radiated power across a hop is uncharacterized. This does not meet the compliance requirement to
validate tracking-enabled table swaps with an SDR power check before relying on the behavior.
Code

docs/rtl8733b.md[R207-212]

+- Fast retune is now ported and independently witnessed (intra-band,
+  same-width; `src/rtl8733b/CLAUDE.md` has the measured contract and its
+  counterparts): channel-state readback parity 7/7 hops, a 299/300 post-hop
+  burst decoded by an RTL8812AU witness, ~55 ms call / ~10 ms p50 radio-live
+  on this unit. Radiated power across a hop remains SDR-gated like every
+  other RF-domain quantity here.
Evidence
PR Compliance ID 4 requires documented validation with an independent witness and an SDR power check
for tracking-enabled swaps across repeated transitions. The updated RTL8733B documentation
explicitly notes that there was no SDR validation and that radiated power across a hop remains
uncharacterized/SDR-gated.

Validate tracking-enabled table swaps across repeated CCK/OFDM transitions (with independent witness and SDR power check)
docs/rtl8733b.md[207-212]
src/rtl8733b/CLAUDE.md[147-150]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
FastRetune’s tracking-live in-place behavior is documented as lacking SDR validation, but PR Compliance ID 4 requires an SDR power check to validate tracking-enabled swaps.
## Issue Context
The current docs state SDR is not used and radiated power across the hop is not characterized, which conflicts with the rule’s success criteria.
## Fix Focus Areas
- docs/rtl8733b.md[207-212]
- src/rtl8733b/CLAUDE.md[142-150]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. FastRetune timing comment mismatch ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
Rtl8733bDevice::GetAdapterCaps comments FastRetune as “~30 ms”, but the RTL8733B FastRetune
documentation in this PR reports ~55 ms call time; the mismatch can mislead profiling/expectations
(it’s unclear if the comment is referring to a different metric like radio-live).
Code

src/rtl8733b/Rtl8733bDevice.cpp[R662-665]

+  /* Lean FastRetune override exists (Phy8733b::fast_retune): intra-band,
+   * same-width hops with TSSI tracking kept live. Measured on the .181
+   * validation unit: ~30 ms vs the ~330 ms full path (USB HS). */
+  caps.fastretune_ok = true;
Evidence
The in-code comment states “~30 ms”, while the RTL8733B FastRetune doc section added in this PR
states “call ~55 ms”. Without clarifying metric differences, the repo contains contradictory numbers
for the same operation.

src/rtl8733b/Rtl8733bDevice.cpp[642-665]
src/rtl8733b/CLAUDE.md[130-145]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GetAdapterCaps()`’s FastRetune comment says “~30 ms”, while the backend docs report “call ~55 ms” (and separately mention radio-live p50 ~10 ms). The discrepancy is confusing without clarifying which timing is meant.
### Issue Context
The capability flag is correct; this is purely about keeping performance notes consistent and unambiguous.
### Fix Focus Areas
- Update the comment to match the documented measurement, or explicitly label it as a different metric (e.g., “radio-live p50”).
- src/rtl8733b/Rtl8733bDevice.cpp[642-666]
- src/rtl8733b/CLAUDE.md[130-145]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/rtl8733b/Phy8733b.cpp Outdated
Comment thread docs/rtl8733b.md Outdated
Comment thread src/rtl8733b/Phy8733b.cpp Outdated
Comment thread src/rtl8733b/Rtl8733bDevice.cpp
snokvist and others added 2 commits August 15, 2026 13:01
…n place

Review finding on the first cut (qodo, PR OpenIPC#398): fast_retune kept TSSI
tracking live but rewrote only the 0x3a00 rate-offset dwords, which are
band-keyed and never change intra-band. The channel-varying TSSI
calibration is the DE offsets from tssi_de_plan, whose 2.4 GHz buckets
are ~3 channels wide — so nearly every hop crossed one and kept tracking
with the previous channel's DE calibration until the next full set.

prepare_tssi_offsets now records the applied DE plan and the EFUSE
calibration that derives it; fast_retune recomputes the target plan
before its first chip write (declining to the full path when it cannot)
and, on bucket change, replays prepare's field sequence minus the 0x4318
tracking-disable write. disable_tssi_tracking drops the cache.

Measured on the validation unit, 22 ch1->ch13 fast hops (every one
bucket-crossing): DE readback parity 22/22, tracking-enable field held
at 7 throughout, settle p50 unchanged at 10.4 ms. The selftest pins the
bucket property the trigger depends on: plans equal within a bucket,
different across a boundary.

Also aligns the GetAdapterCaps timing comment with the documented
measurements (~55 ms call / ~10 ms p50 radio-live).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hri3rWJfbvw8iCyDDwXRit
…keeping on a failed hop

The SCO fc tables, the ch14 TX shape and the AGC/SCO bucket ladders were
duplicated verbatim between switch_channel and fast_retune — an edit to one
that missed the other would be a full-vs-fast register parity divergence only
a hardware parity run could catch. Hoisted to file-scope constexpr shared by
both paths.

A transport failure after fast_retune's first chip write used to propagate
with _fr_plan still recording the old channel while the radio sat
part-hopped; a later fast hop would then bucket-compare against a baseline
the chip no longer holds and could skip a needed write. The catch now drops
the fast-path bookkeeping so the next call declines to the full-path
fallback, which reprograms everything.

Also: disable_tssi_tracking resets _fr_tssi_power with its two siblings, and
the FastRetune override no longer re-declares the cache_rf default (it binds
at the IRtlDevice declaration, like the Jaguar overrides).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@josephnef josephnef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against the full path, register by register:

  • The DE rewrite sequence is prepare_tssi_offsets byte-for-byte minus its 0x4318 tracking-disable write, including the repeated ht40 lane writes.
  • RF18/RF19 compose masks match switch_channel and preserve the bandwidth bits (10/11), so the cached word carries width as claimed.
  • Every write the fast path skips is genuinely band-keyed or constant (0x1ea8, the 2.4 GHz 0x18ac pair, spur_cancellation, 0x2a38[27] — touched nowhere else, so already 0 after the prior full set the fast path requires).
  • Refuse-before-first-write holds: both TSSI plans and the cache-priming reads complete before the first chip write.
  • The TSSI invariants hold: _tssi_tracking == true implies both prepare_tssi_offsets (readback-matched, DE plan + EFUSE recorded) and enable_tssi_tracking (offsets + path recorded) succeeded, with the same target cap at enable and hop.
  • The selftest addition pins exactly the bucket-boundary property the DE rewrite depends on.

Build + ctest 53/53 verified locally. The docs carry their adversarial counterparts (1-in-20 tail, one unit, no SDR) per the repo rule.

Two findings, both applied in 5c51692 rather than requested back:

  1. The SCO fc tables, ch14 TX shape and AGC/SCO bucket ladders were duplicated verbatim between switch_channel and fast_retune — a one-sided edit would be a full-vs-fast parity divergence only a hardware parity run could catch. Hoisted to shared file-scope constexpr.
  2. A transport failure after the first chip write propagated with _fr_plan still recording the old channel while the radio sat part-hopped; a later fast hop would bucket-compare against a baseline the chip no longer holds. The catch now drops the fast-path bookkeeping so the next call declines to the full-path fallback.

Plus two nits in the same commit: disable_tssi_tracking resets _fr_tssi_power with its siblings, and the override no longer re-declares the cache_rf default (binds at the interface, like the Jaguar overrides).

🤖 Generated with Claude Code

@josephnef
josephnef merged commit c8f3531 into OpenIPC:master Aug 15, 2026
22 checks passed
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.

2 participants