Skip to content

kestrel: FastRetune firmware IO-offload for the 8852B same-sub-band hop#328

Merged
josephnef merged 1 commit into
masterfrom
kestrel-fastretune-io-offload
Jul 20, 2026
Merged

kestrel: FastRetune firmware IO-offload for the 8852B same-sub-band hop#328
josephnef merged 1 commit into
masterfrom
kestrel-fastretune-io-offload

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

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-write
commands 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 one
H2C
, collapsing ~20 per-hop USB register round-trips into a single bulk-OUT.

  • 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).

Result (on-air, RTL8852BU)

host hop cost
direct compose-cache path ~9.35 ms
IO-offload (this PR) ~0.15 ms (~45×, one bulk-OUT)

The offload frees the host, not the radio. The direct path's ~5 ms of
bb_reset 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 (~5.5×), with the host free to prep the next
frame / service RX during the settle. dwelltx's default settle window is
raised 500 µs → 2000 µs to cover it.

Contract / gating

Returns before the synth settles, so this changes the FastRetune timing
contract. Default on (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.

Validation

  • On-air soak: 6020-slot, 3-channel (36/40/44 same-bucket), zero
    wrong-channel, 97.6% delivery
    .
  • Default-behavior run (no env at all): 0 wrong, 97.7% delivery.
  • ctest 36/36 green; build clean.
  • Harness: tests/dwell1_ab.py --kfr-ofld; tests/kfr_ofld_smoke.sh (no-oracle timing).

🤖 Generated with Claude Code

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
josephnef merged commit fac5451 into master Jul 20, 2026
18 checks passed
@josephnef
josephnef deleted the kestrel-fastretune-io-offload branch July 20, 2026 20:13
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>
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