Repository navigation
Enforce the IRQ mask, time CAD scans, and decode the rest of the command set - #2
Conversation
Two things the model accepted and then ignored, both on the path a firmware uses to decide whether to transmit. IrqMask was parsed by SetDioIrqParams and never read again, so the status register answered every event whatever the firmware had enabled. That is forgiving in the direction that hides a fault: a driver waiting on an interrupt it forgot to enable waits for ever on silicon and works perfectly here. Every flag now goes through raiseIrq(), and what the mask keeps out is counted in irq_suppressed, because a masked event is silence and silence looks exactly like a quiet mesh. SetCadParams was not decoded at all, so a scan ran with the reset defaults however it had been configured, answered inside the SPI command, and always came back to standby. A firmware could therefore scan the channel and transmit in the same millisecond, which is listen before talk with the listening taken out: the dwell is the entire price of the technique, 1 to 16 symbols, most of a second at SF12. The scan now holds the chip for that dwell, samples the carrier across it rather than at its ends, and ends in standby. detPeak and detMin are recorded and not applied. They threshold a correlator peak and this model is told whether a carrier is present, not how strong it is. Thresholding a boolean would be the chip inventing a signal level and deciding reception from it, which belongs to the simulator. SetRx's timeout came with it, because enforcing a mask over a bit that can never be set is half a job. Continuous receive and single shot stay distinct from a real deadline. vsx_state and vsx_counters gain fields at the end only; VSX_ABI_MINOR 3 -> 4. No host needs rebuilding to keep working, but a host that never programmed IrqMask now sees an empty register, so the pins want moving with a board booted behind them rather than on green tests here.
Four things, all of which present the same way: a node configured wrong that reports nothing wrong. SetModulationParams took every field on trust. A spreading factor, bandwidth code or coding rate the part does not define was adopted or quietly ignored, and the modem stayed on whatever it had. This repository's own harness had been sending bandwidth 0x1A, which is not a code at all, so every timing assertion in it ran at the default 250 kHz while its comment said 62.5 and nothing failed. Each field is now checked, a refused one leaves the setting alone and is counted in params_rejected, and the harness sends 0x03. Low data rate optimisation is the field the firmware programs, not something to infer from the spreading factor. The threshold is a symbol duration, so SF11 is over it at 125 kHz and under it at 250, and inferring it from SF alone was wrong at every bandwidth but one. It was wrong in the figure the firmware asks this chip for to time its own CSMA: at SF11 and 250 kHz a 50 byte frame was quoted at 723 ms against the 641 ms the part would take. The programmed field now wins, and a firmware that never sent one falls back to the symbol duration, which is the rule the engine's own airtime already used. The sync word is written through two registers rather than a command, so it landed in the register array and nothing read it back. Two meshes sharing a frequency under different words therefore heard each other perfectly, which is the opposite of the property the word exists to provide. The chip now comes up on the private word the part resets to, reports what it holds, and refuses a frame sent under another one. vsx_deliver_frame_from carries the transmitter's word; vsx_deliver_frame is unchanged and still delivers, because a host that makes no claim about the transmitter has given no grounds to refuse and every host that has not been taught about the word must go on working. SetTx's timeout, which was discarded like SetRx's. There is no continuous case for a transmission, so any nonzero value is a deadline, and reaching it means the waveform never finished: Timeout, and back to standby. TxDone disarms it, or a chip that transmitted perfectly would report a fault a hundred milliseconds later. vsx_state and vsx_counters gain fields at the end only; VSX_ABI_MINOR 4 -> 5.
…rded Every opcode outside MeshCore's driver used to fall through to "acknowledged and ignored": the firmware issued it, got a status byte, and carried on with the chip in a state it had not asked for. They are decoded now, in commands.cpp, and each one says in its comment which of three things it is - acted on, recorded, or a placeholder - because a model that quietly does nothing is worse than one that does nothing loudly. Acted on. SetPacketType, so GetPacketType stops answering a hardcoded LoRa; only LoRa has a data path, and a firmware that switches is reported switched so a host can refuse to believe the run. StopTimerOnPreamble, which is the setting that stops a bounded receive cutting off a frame it is in the middle of. SetLoRaSymbNumTimeout, the same deadline counted in symbols. SetFs. GetStats and ResetStats. And GetStatus, which was the constant 0x22: it answered standby whatever the part was doing, so a firmware polling it to find out whether its receiver was still armed was told no by a chip that was receiving. Recorded. SetPaConfig, which is half of what sets output power and the board owns the other half. SetRegulatorMode, SetDIO2AsRfSwitchCtrl, SetDIO3AsTcxoCtrl, CalibrateImage, and the two test modes. Recording is not a lesser answer: the fault this model exists to catch is a node configured differently from how its operator believes, and a value nobody can read back is a value nobody can check. Placeholders, each named in the code. SetRxDutyCycle records its periods and leaves the receiver on, because a receiver deaf for a scheduled fraction of the time changes which frames arrive and the engine already decides that. SetRxTxFallbackMode is recorded and FS collapses into standby, because the difference between the three fallbacks is start-up latency and current draw and this model has neither. vsx_deliver_frame_failed is the entry point CrcErr and HeaderErr need to be raisable at all, and nothing calls it: the chip cannot know a CRC failed, and no host is wired to tell it. SetPacketParams reads all six fields. Header type, CRC and invert-IQ were discarded and estAirtimeMs assumed two of them, which is the same shape as the low data rate optimisation bug and in the same figure the firmware times its CSMA on: an implicit header is twenty symbols this model charged for and the part did not. deliverFrame() is now the one path a frame reaches the chip by. inbox stays public because hosts push into it and making it private breaks them, which leaves the hole open: a host that pushes walks around the sync-word check and will walk around the next one too. vsx_state and vsx_counters gain fields at the end only; VSX_ABI_MINOR 5 -> 6.
97f53ca to
8fb9623
Compare
|
Reviewed the full diff, built it locally ( Verified as correct
The honesty discipline is good throughout: the acted-on / recorded / placeholder split, and the note that the pins must not move until a board has booted and forwarded behind this chip. One thing I could not check: formatting against the pinned clang-format 18.1.8 (only 23.1.1 was available here, which disagrees on correct lines). Worth confirming with the pinned wheel before merge. Changes before merge
Minor
|
* bridge: report what the chip's IrqMask kept out The radio stats record grows from 37 bytes to 47. Bytes 37-42 take the emulator's tail so the two writers keep one shape: the DIO1 mask, then a console rate of zero, since a native console is a socket rather than a UART. Bytes 43-46 carry irqSuppressed(), the number of events the firmware's IrqMask kept out of the chip's status register. MeshBench reads the record by length, so a host that predates the field keeps working with either size. irqSuppressed() comes from MeshBench/virtual-sx1262#2; this does not build against the chip at the current pin (2861950). * vendor: virtual-sx1262 at f2604d1, which has irqSuppressed() * vendor: virtual-sx1262 at v1.3.3 v1.3.3 adds, on top of f2604d1: HeaderValid waits for the preamble, the SFD and the header block (#3), and a LoRa header raises HeaderValid alone (#5), so a firmware whose mask correctly leaves SyncWordValid out no longer counts every header as suppressed. No ABI change. --------- Co-authored-by: R0ck <develop@hectospark.co.uk>
The model accepted several things a firmware programs and then ignored them, or answered them with a constant. Each one presents the same way: a node configured wrong that reports nothing wrong. This makes the chip act on what it is told, record what it cannot act on, and count what it refuses.
ABI
1.3→1.6, one minor per commit. Additions only: new fields are appended to the end ofvsx_stateandvsx_counters, and there are two new entry points. No host needs rebuilding to keep loading the library.Commits
ace6864enforce the IRQ mask, and make a scan cost what a scan costs (1.4)IrqMaskfromSetDioIrqParamsis now applied: every flag goes throughraiseIrq(), and masked events are counted inirq_suppressed.SetCadParamsis decoded. A scan holds the chip for its programmed dwell (1–16 symbols), samples the carrier across the whole dwell, and ends in standby.detPeak/detMinare recorded, not applied. New counters:cad_runs,cad_detections.SetRx's timeout is enforced; continuous, single-shot and deadline receive stay distinct.5faabfecheck what the modem was programmed to, and who the chip is listening to (1.5)SetModulationParamsvalidates SF, bandwidth code and coding rate. A refused field leaves the setting unchanged and is counted inparams_rejected. The test harness had been sending bandwidth0x1A, which is not a valid code; it now sends0x03.0x1424, reports it invsx_state.sync_word, and refuses frames delivered with another word via the newvsx_deliver_frame_from(). The counter issync_mismatches.vsx_deliver_frame()is unchanged and still delivers.SetTx's timeout is enforced: if the host has not calledvsx_transmit_finished()before a nonzero deadline, the chip raisesTimeoutand goes to standby.8fb9623decode the rest of the command set, and say which parts are only recorded (1.6)src/commands.cpp. Every command outside MeshCore's driver path is now decoded, and each is marked acted on, recorded, or placeholder.SetPacketType,StopTimerOnPreamble,SetLoRaSymbNumTimeout,SetFs,GetStats/ResetStats.GetStatusalso, which used to be a constant0x22.SetPaConfig,SetRegulatorMode,SetDIO2AsRfSwitchCtrl,SetDIO3AsTcxoCtrl,CalibrateImage, test modes.SetRxDutyCyclerecords its periods but the receiver stays on.SetRxTxFallbackModetreats FS as standby.vsx_deliver_frame_failed()is the only way to raiseCrcErr/HeaderErr, and nothing calls it yet.SetPacketParamsreads all six fields. Header type, CRC and invert-IQ now feedvsx_est_airtime_ms().Behaviour change for hosts
A firmware that never programs
IrqMasknow sees an empty IRQ status register, where it used to see every flag. PerCLAUDE.md, the pins inconsumers.tsv(meshcore-native) and the MeshBench release pin should move only after an emulated board boots on this chip, forwards traffic, and showsirq_suppressed == 0. That boot has not been done yet.Testing
STRICT=1 ./build.sh: all seven test binaries pass with 0 failures, andSTRICT=1 ./build.sh sanitizepasses them again under ASan/UBSan.STRICT=1 ./build.sh sharedbuilds. The new suites aretest_cad,test_modemandtest_commands, andtest_interruptsandtest_c_abiare extended.