diff --git a/AGENTS.md b/AGENTS.md index 9deb70d..9f676c0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -437,17 +437,37 @@ Note the ELF at `uvk5-sat/build/CW/nr7y.cw.elf` carries no DWARF, so gdb reports `'gEeprom' has unknown type`. Scalars work if you cast through their address (`*(unsigned short*)&gDebounceCounter`); struct fields need manual offsets. -## What this cannot do +## The BK4819, and where modelling it stops -It reproduces what the firmware *commanded* — frequency, power step, carrier -keying in time. It does not reproduce the analogue result: keying envelopes, -spurious emissions, sensitivity. +The register interface is modelled (`TYPE_UVK5_BK4819`): the bit-banged three-wire +bus is decoded, registers read back what the firmware wrote, and the ones it reads +without writing return plausible values. Wiring is CS on PF9, SCL PB8, SDA PB9 with +both directions connected. `tools/test_bk4819.py` inspects the register file over QOM. -That is not a gap to close later. The BK4819/BK4829 transceiver has no public -datasheet, so its driver is the only specification available, and a driver tells -you which registers were written, never what left the antenna. Those questions -need a real radio and a spectrum analyser. Do not let anyone conclude otherwise -from a passing emulator test. +This is what it fixed: RSSI used to read hard zero at 18 call sites — -160 dBm — so +the S-meter showed empty and squelch and scan logic evaluated a dead band. The main +screen now comes up on 400 MHz rather than the 18 MHz floor, because band setup is no +longer reading zeros. + +Two constraints are not negotiable, both from untimed spin loops in the firmware: + +- **REG_0C bit 0 must stay clear.** `app/app.c:910` and `:1417` are + `while (BK4819_ReadRegister(BK4819_REG_0C) & 1u)` with no timeout at all. A stuck + bit hangs the guest; it does not degrade. +- **A soft reset must re-seed the measurement registers.** `REG_00` bit 15, which + `BK4819_Init` issues first, would otherwise leave them zero — real hardware keeps + measuring. Not hypothetical: the first test run decoded 48 registers correctly and + still reported RSSI as 0 for precisely this reason. + +**Where it stops.** This models the register interface, not the radio. It reproduces +what the firmware *commanded* — frequency, power step, carrier keying in time — never +the analogue result: keying envelopes, spurious emissions, sensitivity. + +That is not a gap to close later. The chip has no public datasheet, so its driver is +the only specification available, and a driver tells you which registers were +written, never what left the antenna. Those questions need a real radio and a +spectrum analyser. Do not let anyone conclude otherwise from a passing emulator test, +including the one added here. Timing is also deliberately wrong — see the SysTick section in README.md. Fine for menus and control flow; useless for signal timing. diff --git a/README.md b/README.md index a9d62f5..59eb78d 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,9 @@ has no public datasheet, so its driver is the only specification available. | Keypad and menu navigation | works, including waking from power save | | Serial output (firmware log) | works, appears in the web UI log | | Serial input, CPS programming protocol | works, `-serial` any chardev | +| BK4819 register interface | works, RSSI and status readable | | Timing accuracy | deliberately wrong, see [Timing](#timing) | -| Radio/RF behaviour | not modelled | +| Analogue RF behaviour | **not modelled and never will be**, see [AGENTS.md](AGENTS.md#the-bk4819-and-where-modelling-it-stops) | A short `tools/key.py MENU` opens the menu, UP/DOWN move through it, MENU enters a submenu, and typing a menu number jumps straight to that entry. Press duration @@ -78,6 +79,7 @@ keypresses silently stop working. Run the test after touching that code; test_flash_persist.py flash writes survive a power cycle 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 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 @@ -121,6 +123,7 @@ Then check the build actually works, which takes about a minute: python3 tools/test_flash_persist.py python3 tools/test_freq_entry.py python3 tools/test_serial_rx.py + python3 tools/test_bk4819.py 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 diff --git a/qemu/py32f071.c b/qemu/py32f071.c index 2917033..c409665 100644 --- a/qemu/py32f071.c +++ b/qemu/py32f071.c @@ -341,10 +341,11 @@ static void py32_gpio_reset(DeviceState *dev) * active low, so a floating pin has to read as "not pressed". * * Exception: PB9 is the bidirectional data line of the software-driven - * three-wire bus to the BK4819 transceiver. Idling it high makes every - * register read return 0xFFFF, and RADIO_SetupRegisters then spins forever - * waiting for bit 0 of REG_0C to clear. Idle it low until that bus has a - * device model, so reads come back as zero and the wait terminates. + * three-wire bus to the BK4819 transceiver, which now has a device model + * driving it (see TYPE_UVK5_BK4819). Idle it low anyway, for the window + * between reset and the bus being wired up: a high idle makes reads return + * 0xFFFF, and RADIO_SetupRegisters spins on bit 0 of REG_0C with no timeout, + * so it would hang outright rather than degrade. */ s->idr = 0xffff; if (s->port_name && s->port_name[0] == 'b') {