Record what the read-path fix revealed about the squelch attempts

The shifted-read bug invalidated four earlier diagnoses, so the notes claiming a
power-save gate or a timing problem were wrong and are corrected.

With reads fixed the interrupt handshake demonstrably works: RAISE pending=0004,
ACK delivering flags=0004, correct bit, collected by the firmware, REG_0C back to
0x0000 so nothing hangs.

g_SquelchLost is still 0 and the S-meter still absent, but the shape of the problem
is now clear and recorded: announcing once lands during startup before the flag
leads anywhere; announcing every poll re-arms the bit inside the firmware's own
untimed collection loop and spins forever; announcing periodically avoids both and
still changes nothing. The blocker is getting the radio into a receiving state at
all, which sits above the device model.

Also records the general lesson, which cost the most time here: when several
independent attempts fail in the same way, suspect the shared transport rather than
the logic layered on top of it.
This commit is contained in:
mckero committed 2026-08-29 04:40:07 +01:00
1 parent ad88ee1519
commit 7faacfd433
2 files changed
+55 -8

No files matched your search

+53 -8
View File
@@ -459,6 +459,34 @@ Two constraints are not negotiable, both from untimed spin loops in the firmware
measuring. Not hypothetical: the first test run decoded 48 registers correctly and
still reported RSSI as 0 for precisely this reason.
### Reads were shifted one bit, and it hid everything else
Fixed in `ad88ee1`, but worth reading because of how long it stayed invisible.
Register reads arrived shifted one place left: seed `REG_0C` with `0x1248` and the
firmware received `0x2490`. Each firmware bit is read/raise/lower, so the eighth
command bit is followed by a falling edge before the data phase — and the model was
treating that edge as a data clock, shifting bit 15 away before the guest sampled it.
Why nobody noticed: **writes were always fine**, 52 registers held exactly what the
firmware wrote, and the register the firmware polls hardest was legitimately `0`.
Reading zero and getting zero looks like success. Verifying a read path requires a
register with a known *non-zero* value — `REG_3F` is `0x0C0C`, `REG_78` is `0x2F5B`.
`tools/test_bk4819_readback.sh` guards it now: seeds `REG_0C` (read ~1700 times per
30 s, so a sample is guaranteed) with a value carrying bits in both halves, and names
the shift direction on failure. Bit 0 is left clear deliberately — with it set the
firmware enters an untimed acknowledge loop, and that test is about alignment only.
This also invalidated four earlier diagnoses. Attempts at the squelch interrupt had
the model raising `REG_0C` bit 0 while the firmware received bit 1, so
while (BK4819_ReadRegister(BK4819_REG_0C) & 1u)
was never true and 1719 polls saw a flag the guest could not act on. Every one of
those rounds was blamed on timing or gating. **When several independent attempts fail
the same way, suspect the shared transport, not the logic on top of it.**
### The squelch interrupt: attempted, backed out
Scanning works — long-press `*` and the frequency really does step, 6 distinct frames
@@ -521,15 +549,32 @@ suppresses that is inside the inlined loop, past the gate. Gating the model on
`REG_30` (zeroed by `BK4819_Sleep`) does not help either — the chip is awake when the
model is asked while the firmware still reports `gRxIdleMode=1`.
**Where it actually stands.** A breakpoint on `BK4819_GetRSSI` never fires at all: in
this idle state the firmware does not read RSSI, so the missing S-meter is not the
model withholding a value. The receive state machine has to be entered first, and the
squelch interrupt is one input to that rather than the switch.
**Where it actually stands, after the read-path fix.** With reads correct, the
handshake demonstrably works — measured `RAISE pending=0004` followed by
`ACK delivering flags=0004`, the right bit, collected by the firmware, and `REG_0C`
back to `0x0000` afterwards so nothing hangs. That part is solved.
Both attempts are reverted and the committed model has no squelch logic. Do not resume
this without a concrete reason to think the state machine can be entered — three
rounds of increasingly precise instrumentation each ended at "the firmware is not
asking", not at a fault in the device.
`g_SquelchLost` still ends up 0, and the S-meter still does not appear. What the
instrumentation shows is a timing shape rather than a wrong value:
- Announce **once** on the transition and the firmware collects it during startup,
before `g_SquelchLost` leads anywhere, then never hears again.
- Announce on **every** poll and the request bit is re-armed inside the firmware's own
collection loop — which re-reads `REG_0C` as its condition and has no timeout — so it
spins forever.
- Announce **periodically** (every 64th poll) and both of those are avoided: the loop
always drains, the news repeats. `REG_0C` reads clear, no hang. `g_SquelchLost` is
still 0.
So the remaining gap is not the interrupt. The radio has to be in a state where
`CheckForIncoming` runs and acts on the flag, and in this idle configuration
(`fn=5 idle=1`, power save, dual watch, squelch level 1) it is not. Consistent with a
breakpoint on `BK4819_GetRSSI` never firing at all.
All squelch work is reverted; the committed model has none. The read-path fix is
committed separately and stands on its own. Before resuming, establish how to get the
firmware into a receiving state at all — that is the actual blocker, and it is above
the device model.
Four measurement mistakes made this take far longer than the code involved. All four
produced a confident, wrong conclusion:
+2
View File
@@ -80,6 +80,7 @@ keypresses silently stop working. Run the test after touching that code;
test_freq_entry.py a typed frequency takes effect and persists
test_serial_rx.py the firmware answers programming commands
test_bk4819.py BK4819 register interface, RSSI not stuck at zero
test_bk4819_readback.sh register reads come back bit-aligned
lib_kill_emulator.sh cleanup that only ever kills emulators
webui.py web remote control: live LCD plus clickable keypad
dn42_firewall.sh restrict the web UI port to DN42 sources
@@ -124,6 +125,7 @@ Then check the build actually works, which takes about a minute:
python3 tools/test_freq_entry.py
python3 tools/test_serial_rx.py
python3 tools/test_bk4819.py
bash tools/test_bk4819_readback.sh
This matters more than it looks. The keypad can break silently under -O2 without
any compiler warning -- see the `volatile` note in [Status](#status) -- so a clean