From 7faacfd433ba2ff12272b01229f163fb243e3193 Mon Sep 17 00:00:00 2001 From: MCKero Date: Sat, 29 Aug 2026 04:40:07 +0100 Subject: [PATCH] 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. --- AGENTS.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++-------- README.md | 2 ++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1715780..ae01590 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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: diff --git a/README.md b/README.md index 59eb78d..22313e7 100644 --- a/README.md +++ b/README.md @@ -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