Add guarded nRF SPI startup diagnostic for private 16 MHz trials

This commit is contained in:
mikecarper committed 2026-10-23 02:59:27 -07:00
1 parent cb03417509
commit c83d43e03c
5 files changed
+254 -11

No files matched your search

@@ -0,0 +1,12 @@
#include "NrfRadioSpiStartupDiagHal.h"
#if MESH_HIL_SPI_STARTUP_DIAG
namespace mesh { namespace spistartup {
extern "C" {
volatile Capture mesh_hil_spi_startup = {
0x4d435353, 1, sizeof(Capture), Idle,
0, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX,
0, 0, 0, UINT32_MAX, 0};
}
} }
#endif
@@ -0,0 +1,77 @@
#pragma once
#ifndef MESH_HIL_SPI_STARTUP_DIAG
#define MESH_HIL_SPI_STARTUP_DIAG 0
#endif
#if MESH_HIL_SPI_STARTUP_DIAG != 0 && MESH_HIL_SPI_STARTUP_DIAG != 1
#error "SPI startup diagnostic flag must be 0 or 1"
#endif
#if MESH_HIL_SPI_STARTUP_DIAG
#if !defined(NRF52_PLATFORM)
#error "SPI startup diagnostic requires nRF52"
#endif
#include "Nrf52BufferedRadioHal.h"
#include <helpers/nrf52/SoftDeviceState.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
namespace mesh { namespace spistartup {
enum Phase : uint32_t { Idle = 0, Query = 1, Transfer = 2, Completed = 3, Refused = 4 };
// Fixed 14-word ABI for private SWD reads, including before USB is usable.
// Each word is independently observable; this is not an atomic snapshot.
struct Capture {
uint32_t magic, version, size_bytes, phase;
uint32_t queries, first_query_result, first_query_enabled;
uint32_t last_query_result, last_query_enabled;
uint32_t begun, completed, refused, last_opcode, last_length;
};
static_assert(sizeof(Capture) == 56 && offsetof(Capture, begun) == 36
&& offsetof(Capture, last_length) == 52, "SPI startup capture ABI changed");
extern "C" {
extern volatile Capture mesh_hil_spi_startup;
}
} } // namespace mesh::spistartup
// Diagnostic only: the global nrfx anomaly-198 workaround cannot coexist
// with SoftDevice. Refuse the actual frame unless its disabled state was
// positively established by the existing LTO-safe supervisor query.
class NrfRadioSpiStartupDiagHal : public Nrf52BufferedRadioHal {
public:
explicit NrfRadioSpiStartupDiagHal(SPIClass& bus, uint32_t hz)
: Nrf52BufferedRadioHal(bus, hz) {}
void spiTransfer(uint8_t* out, size_t len, uint8_t* in) override {
if (!len) return;
auto& capture = mesh::spistartup::mesh_hil_spi_startup;
capture.last_opcode = out ? out[0] : UINT32_MAX;
capture.last_length = static_cast<uint32_t>(len);
capture.phase = mesh::spistartup::Query;
uint8_t enabled = 0;
const uint32_t result = mesh_nrf52::softdeviceIsEnabled(enabled);
if (!capture.queries) {
capture.first_query_result = result;
capture.first_query_enabled = enabled;
}
++capture.queries;
capture.last_query_result = result;
capture.last_query_enabled = enabled;
if (result != 0 || enabled != 0) {
if (in) memset(in, 0x2a, len); // SX126x command-failed status.
++capture.refused;
capture.phase = mesh::spistartup::Refused;
return;
}
++capture.begun;
capture.phase = mesh::spistartup::Transfer;
Nrf52BufferedRadioHal::spiTransfer(out, len, in);
++capture.completed;
capture.phase = mesh::spistartup::Completed;
}
};
#endif