kestrel: FastRetune firmware IO-offload for the 8852B same-sub-band hop#328
Merged
Conversation
rtw89 has no channel-switch H2C, but its mac_ax firmware exposes a generic register IO-offload (FW_OFLD/CMD_OFLD_REG): a buffer of masked-write commands the on-chip CPU replays locally. Batch the whole same-sub-band hop — the RF18/ 0xcf channel-set writes, halbb_bb_reset_all_8852b, and the fixed-dBm TX-power target — into ONE H2C, collapsing ~20 per-hop USB register round-trips into a single bulk-OUT. Host-side hop cost drops ~9.35 ms -> ~0.15 ms. KestrelFw::reg_write_ofld packs the 16-byte fwcmd_cmd_ofld command buffer; fast_retune_ofld_8852b builds the RF+BB+MAC+DELAY write-list for the hop. The firmware applies each masked write as (reg & ~mask) | ((val << ctz(mask)) & mask), so BB/MAC entries carry the raw field value (RF uses the full MASKRF, shift 0). The offload frees the host, not the radio: the direct path's ~5 ms of bb_reset USB round-trips were incidentally covering the RF synth's ~1.5 ms VCO settle. Removing them exposes that floor — a frame airing sooner lands wrong-channel (on-air settle sweep: 1.0 ms = 20 wrong/2000, >=1.5 ms = 0). Net time-to-usable-channel ~1.7 ms with the host free during the settle. dwelltx's default settle window is raised 500 us -> 2000 us to cover it. Default on (DeviceConfig.tuning.kestrel_fastretune_ofld, DEVOURER_KFR_OFLD=0 forces the self-pacing direct path); armed on all bring-up paths, a no-op on the 8852C and the bucket-crossing synth-relock path. Validated on-air (8852BU): 6020-slot 3-channel soak, zero wrong-channel, 97.6% delivery. tests/dwell1_ab.py gains --kfr-ofld; tests/kfr_ofld_smoke.sh is the no-oracle timing check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
josephnef
added a commit
that referenced
this pull request
Jul 21, 2026
) ## What Cold-boot init on the HalMAC generations is **register-write-bound** — the BB/AGC/RF phy-table load is thousands of synchronous USB register control transfers (measured: 8822E ~1.4 s / 9207 writes; 8812BU ~5.7 s of writes). Realtek's HalMAC firmware exposes `cfg_param` (SUB_CMD `0x08`, class `FW_OFFLOAD`) — the 11ac ancestor of the mac_ax `CMD_OFLD` shipped for Kestrel in #328: a buffer of register-write commands the on-chip CPU replays. This routes the **static** phy-table writes through it, collapsing the control-transfer stream into a handful of bulk transfers. - **New `src/HalmacCfgParam.{h,cpp}`** — generation-agnostic 12-byte-per-command packer + flush with injected transport (`dl_rsvd_page` + `send_h2c_pkt` + a BB read-back verify). Table entries are full-register writes (`MSK_EN=0`), so the fw writes the value verbatim. - **Self-validating**: each flush reads back the batch's last BB write and polls until it lands — this confirms the firmware actually replayed the buffer (vs silently dropping the H2C) **and** gates reuse of the reserved page. A mismatch fails the offload so the caller **redoes the tables direct** (idempotent) — the chip is never left half-configured. - Wired on **Jaguar2** via `DEVOURER_FW_TABLE_OFLD` (`tuning.fw_table_offload`, **default 0**): bit 1 = BB+AGC, bit 2 = also the RF radio tables (`RF_W`). ## Result (on-air, RTL8812BU) | | InitWrite | |---|---| | direct | 6480 ms | | **offload (BB+AGC+RF)** | **5486 ms** | The read-back verify passes (firmware replays correctly; RF state matches the direct path) and 2768 register writes move off the control-transfer path. This DUT's init is dominated by a fixed-cost LCK-timeout poll, so the exposed write fraction (~15%) understates the effect — the **config-identity** (offloaded == direct) is the real proof. ## Gating / safety `DEVOURER_FW_TABLE_OFLD=0` (default) is **byte-identical** to before. A transport error or a read-back mismatch triggers a full direct re-apply. Opt-in — init is higher blast-radius than a hop. ## Jaguar3 (follow-up) Investigated but **not shipped** here. A config-dump A/B proved the 8822C/E firmware *does* replay `cfg_param` (5/6 core BB registers match the direct path with fw-only writes), but it replays **lazily** (needs an end-of-apply verify, not a per-batch read-back) and its reserved-page download stalls ~1 s per batch. Left as a documented follow-up. ## Validation - On-air 8812BU: read-back verify passes, init 6.5 s → 5.5 s, config matches direct. - `ctest` 36/36; per-chip build-config matrix unaffected (new file is in the always-compiled core). - `tests/fw_table_offload.sh`: cold-cycle A/B harness (init-time + read-back verdict). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.
What
rtw89 exposes no channel-switch H2C, but its mac_ax firmware has a generic
register IO-offload (
FW_OFLD/CMD_OFLD_REG): a buffer of masked-writecommands the on-chip CPU replays locally. This batches the whole 8852B
same-sub-band FastRetune hop — the RF18/0xcf channel-set writes,
halbb_bb_reset_all_8852b, and the fixed-dBm TX-power target — into oneH2C, collapsing ~20 per-hop USB register round-trips into a single bulk-OUT.
KestrelFw::reg_write_ofldpacks the 16-bytefwcmd_cmd_ofldcommand buffer.fast_retune_ofld_8852bbuilds the RF+BB+MAC+DELAY write-list for the hop.(reg & ~mask) | ((val << ctz(mask)) & mask), so BB/MAC entries carry the raw field value (RF uses the fullMASKRF, shift 0).Result (on-air, RTL8852BU)
The offload frees the host, not the radio. The direct path's ~5 ms of
bb_resetround-trips were incidentally covering the RF synth's ~1.5 ms VCOsettle; removing them exposes that floor — a frame airing sooner lands
wrong-channel (on-air settle sweep: 1.0 ms → 20 wrong/2000, ≥1.5 ms → 0). Net
time-to-usable-channel ~1.7 ms (~5.5×), with the host free to prep the next
frame / service RX during the settle.
dwelltx's default settle window israised 500 µs → 2000 µs to cover it.
Contract / gating
Returns before the synth settles, so this changes the
FastRetunetimingcontract. Default on (
DEVOURER_KFR_OFLD=0forces the self-pacing directpath); armed on all bring-up paths, a no-op on the 8852C and the
bucket-crossing synth-relock path.
Validation
wrong-channel, 97.6% delivery.
ctest36/36 green; build clean.tests/dwell1_ab.py --kfr-ofld;tests/kfr_ofld_smoke.sh(no-oracle timing).🤖 Generated with Claude Code