Files
DigiRadio/Software/docs/si4684-rf-investigation-report.md
T
micheleandClaude Sonnet 5 42dadea369 Fix GET_PART_INFO/GET_SYS_STATE/GET_FUNC_INFO byte offsets; confirm Si4684 blobs genuine
Si4684Driver::getPartInfo()/getSysState() existed but were never called, so
their byte-offset bugs never surfaced. Wired them into a boot-time diagnostic
log (part number, firmware revision, active image, streamed blob byte counts)
to check whether the loaded DAB/FM firmware images are genuine and intact, as
an alternative explanation to the QFN exposed-pad hardware hypothesis.

Fixed two rounds of off-by-one bugs found while doing this: the fields were
initially read one byte too far right (e.g. firmwareBuild was reading a flag
byte, not a version number); the first fix undershot in the other direction
by not accounting for readRaw()'s one-byte SPI lead-in before STATUS0 (already
documented and confirmed elsewhere in this file, in pollStc()) -- caught
because the "fixed" GET_SYS_STATE reported image=192 (0xC0), the exact
signature of STATUS3 with PUP_STATE=3 seen throughout this investigation.
All three response buffers were already sized for the lead-in byte, which
confirmed the correct fix.

Verdict, captured live: blob streamed bytes match local file sizes exactly (no
truncation), GET_SYS_STATE reports image=2 (DAB active, correct), GET_PART_INFO
reports part=4684 (matches expected Si4684 part number) with a plausible
firmware revision -- the loaded DAB firmware is genuine and intact. This closes
the last plausible firmware-side explanation for the FM/DAB no-lock symptom;
docs/si4684-rf-investigation-report.md and docs/TODO.md (P4/H5) updated with
the full record and verdict.

Also noted, not yet fixed: BT1035 AT-init now fails deterministically on every
boot (was a one-off earlier this session) -- see report's Open Items.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 22:53:27 +02:00

10 KiB
Raw Blame History

Si4684 RF no-lock investigation — status report

Date: 2026-08-13 Board: PCBWay order W96157ASH49, U6 = Si4684-A10 (confirmed genuine, top marking 4684A10-2112AD254YZ-2112AD-E3)

Symptom

Si4684 (U6) boots, loads the ROM patch and FM/DAB application images, and answers every SPI command correctly (CTS, boot sequence, property reads/writes all succeed). FM and DAB tuning never completes: the STCINT status bit never sets, RSQ/DIGRAD metrics stay at zero (RSSI=0, SNR=0, VALID=0, FIC quality=0, empty service list), on both bands, at every frequency tried.

What has been verified correct (do not re-litigate)

All cross-checked byte-by-byte against the official Skyworks documents (Hardware/DATASHEET/AN649.pdf, AN851_Schematics_Layout.pdf):

  • Boot sequence: RSTB# pulse, ROM patch stream, image stream, BOOT — matches the AN649 flowchart exactly, on every boot, both bands.
  • Crystal / POWER_UP: XTAL_FREQ = 19,200,000 exact (bytes 00 F8 24 01, little-endian 0x0124F800), CLK_MODE = crystal mode (0x17 → bits 5:4 = 01). Matches the physical ABM8-19.200MHZ-10-1-U-T crystal (U7) and BOM/schematic.
  • I2S: Si4684 configured as I2S slave (DIGITAL_IO_OUTPUT_SELECT = 0x0000), 48 kHz; ADAU1701 is bus master. Confirmed via GPIO clock probe (LRCLK ≈ 48000 Hz, BCLK ≈ 3.07 MHz) at every boot.
  • Front-end matching properties: FM/DAB_TUNE_FE_VARM (0x1710), FM/DAB_TUNE_FE_VARB (0x1711), and FM/DAB_TUNE_FE_CFG/VHFSW switch (0x1712, value 0x0001 = closed) all match AN851's "Silicon Labs Recommended Front End Network" table exactly (FM: 0xEDB5/0x01E3; DAB: 0xF8A9/0x01C6; switch closed on both).
  • FM_TUNE_FREQ command: all six ARG bytes decoded bit-by-bit against the AN649 command table (DIR_TUNE, TUNE_MODE, INJECTION, FREQ, ANTCAP, PROG_ID) — correct.
  • INT_CTL_ENABLE / INT_CTL_REPEAT (STCIEN/STCREP, properties 0x0000/0x0001): correct bit positions; confirmed these only gate the physical INTB pin, not the STATUS0 STCINT bit our driver polls directly over SPI.
  • STC polling mechanism: the same raw SPI byte (pollRx[1]) that reliably reports CTS=1 (bit 7) across hundreds of successful commands also reports STCINT=0 (bit 0) — the read path itself is proven reliable by the CTS side, so the "never sets" result is a real hardware/firmware-image observation, not a polling bug.

Front-end network component mismatch (found this session, not the cause by itself)

The board's actual front-end network (RF1 → C13(33pF) → L1(18nH) → C14(2.7pF shunt) → L3(120nH shunt) → VHFI, L2(22nH) bridging VHFI↔VHFSW) differs from Silicon Labs' AN851 reference network the VARM/VARB constants were derived from (C1=33pF, L1=56nH, L2=120nH‖L3=120nH). This was flagged as a plausible contributor, then tested directly and ruled out as the sole cause (see below).

Empirical sweeps (all negative — zero variation)

  • IBIAS/CTUN (crystal startup calibration, POWER_UP ARG3/ARG8): 8 candidates across the practical range, full reboot between each. No change.
  • ANTCAP (FM_TUNE_FREQ ARG4/5, bypasses FE_VARM/VARB auto-tune entirely and forces the on-chip antenna varactor directly, per AN851 Appendix A): ~100 of 128 possible values swept, across three antenna conditions (disconnected, loose contact, directly soldered 70 cm — correct quarter-wave for FM). Every single attempt returned byte-identical RSQ raw data (00 80 00 00 c0 00 00 00 00 00 00 00, RSSI=0/SNR=0/VALID=0). If the RF path were electrically functional, at least one of ~100 forced varactor values across the full physical range should have produced resonance. None did.
  • Reset type: software RSTB# vs. full USB power-cycle (15 s cold) — no change.
  • PCB continuity: RF1 (antenna connector) → C13 → L1 → U6 pin 10 (VHFI) confirmed intact with a multimeter (tested each leg separately to work around C13's DC block). No broken trace/via.

Leading hypothesis: QFN-48 exposed pad (EP) solder defect

U6 is a 7×7 mm QFN-48 with an exposed thermal/ground pad (pin 49, tied to GND). Insufficient solder or voiding under this pad during reflow is a well-documented QFN assembly failure mode that produces exactly this symptom: digital I/O (peripheral pins, less ground-sensitive) works perfectly, while the RF/analog front end (which references the exposed pad for a clean ground) fails entirely. ESD was considered and set aside — the antenna input has ESD clamp protection (D3, BAV99) ahead of the RF path, and no digital-side symptom consistent with ESD damage (SPI glitches, crystal instability) has ever appeared.

Action taken: a technical report was sent to PCBWay (order W96157ASH49) requesting an assembly quality review of U6's solder joints, specifically the exposed pad, laying out the same evidence above (firmware ruled out by the ANTCAP-bypasses-firmware argument).

Action pending: a manual hot-air reflow of U6 (re-melt only, no added solder/paste) was planned as a lower-risk first attempt before considering full chip removal and re-paste. Outcome not yet recorded in this document as of this report's writing — update this section once attempted.

Separate finding this session: audio profile save was broken, now partially fixed

Independent of the Si4684 investigation, while testing the internet radio streaming feature (components/services/webradio), audio profile changes via PUT /api/audio/profile and POST /api/audio/reset were found to fail (store_failed) — which is why the ESP32 mixer channel (needed to hear the web radio stream through the DSP mixer, muted at -96 dB by the default "radio-first" mix) could not be un-muted via the API.

Root-caused and fixed: sigma_i2c_write() (components/drivers/adau1701/src/SigmaStudioFW.c) had no retry on I2C transaction failure. A full EQ apply chains ~55 sequential I2C transactions (5 bands × safeload block); a single transient NACK anywhere in that burst aborted the whole sequence, while short bursts (e.g. the 2-write beep toggle) reliably succeeded. Added a 3-attempt retry with a 2 ms backoff.

After the fix, DSP-side writes (mixer, EQ) succeed. A second, separate failure remains: NvsAudioProfileStore::saveProfile() still fails, now isolated to the NVS write step itself (not the DSP). Diagnostic logging was added (nvs_open/nvs_set_str/nvs_commit error codes) to pin down the exact esp_err_t; the leading suspicion is NVS partition space/fragmentation (the nvs partition is only 24 KB, and this session alone did many repeated writes across streaming config, beep toggles, Wi-Fi, and station data). Not yet confirmed with the actual error code — re-run the diagnostic build and capture the log line to close this out.

Blob/firmware-image integrity check (completed, negative — blobs are genuine)

getPartInfo() and getSysState() (components/drivers/si4684/src/Si4684Driver.cpp) already existed to decode GET_PART_INFO/GET_FUNC_INFO/GET_SYS_STATE, but were never called anywhere in the codebase — dead code, so their byte-offset bugs had never surfaced. Found and fixed two rounds of off-by-one bugs while wiring them into a boot-time diagnostic log:

  1. First pass: every field (chip ID, firmware major/minor/build, image type) read one byte too far right — e.g. firmwareBuild was actually reading the NOSVN/LOCATION flag byte, not a version number.
  2. That "fix" was itself wrong in the other direction. readRaw() responses carry a one-byte SPI lead-in before STATUS0 — the same convention already confirmed and commented in pollStc() elsewhere in this file — which the first pass didn't account for. Caught empirically: the "fixed" GET_SYS_STATE reported image=192 (0xC0), the exact byte pattern of STATUS3 with PUP_STATE=3 seen dozens of times elsewhere in this investigation — proof the read was still one byte off, in the other direction. All three existing response buffer sizes (7/13/24 bytes) already matched "N response bytes + 1 lead-in", confirming the lead-in-byte offset (not the no-lead-in offset) is correct.

Result after the fix, captured live from the device (DAB boots first at startup):

Si4684: blob streamed: 5796/5796 bytes      (rom_patch_016.bin, matches file size exactly)
Si4684: blob streamed: 517524/517524 bytes  (dab_firmware.bin, matches file size exactly)
Si4684: GET_SYS_STATE: image=2              (2 = DAB active, correct per AN649)
Si4684: GET_PART_INFO/GET_FUNC_INFO: part=4684 rev=4.0.5 svnid=0x00001754

part=4684 matches the expected Si4684 part number exactly; rev=4.0.5 and the SVN ID are plausible, sane values, not garbage. Blob byte counts streamed over SPI match the local file sizes exactly — no truncation in transit. Verdict: the DAB blob loaded on the chip is genuine and intact. (FM boot's GET_FUNC_INFO was not yet captured — a BT1035 AT-init failure, see below, has been blocking the device from reaching the point in the boot sequence where FM is exercised. Not expected to change this verdict; DAB alone already answers the blob-integrity question this check was for.)

This closes the last plausible firmware-side explanation for the no-lock symptom. Combined with everything else in this report, the QFN exposed-pad hardware hypothesis is now the leading and best-supported explanation.

Open items

  1. Confirm the exact NVS error code for the audio-profile save failure and fix accordingly (likely: erase/compact the audio_profile_json key, or address partition fragmentation — do not perform a full NVS erase without explicit confirmation, it would wipe Wi-Fi credentials, stations, and the saved BT speaker pairing).
  2. Record the hot-air rework outcome (RSSI response test) once attempted.
  3. If rework doesn't change the symptom: escalate to full chip removal + re-paste, or treat the PCBWay claim as the primary path forward.
  4. New, separate issue: BT1035 AT init failed at boot, now reproducing on every reset (was a one-off earlier in this session, now persistent/deterministic — same ~7.7 s timeout every time). Not yet investigated; may be related to physical handling of the board during the antenna soldering/rework work today. Blocks the full boot sequence (hardware::HardwareBootstrap::boot() halts app_main() on any companion-chip failure), so also blocks reaching Wi-Fi/HTTP and the FM boot path.