Add bounded nRF radio yield experiment and correlated worst-hop timings

This commit is contained in:
mikecarper committed 2026-10-23 02:08:10 -07:00
1 parent 2629c15804
commit 47e9dc4b14
5 files changed
+159 -5

No files matched your search

+30 -1
View File
@@ -2,15 +2,44 @@
#include <hal/Arduino/ArduinoHal.h>
#ifndef MC_NRF_RADIO_BUSY_SPIN_POLLS
#define MC_NRF_RADIO_BUSY_SPIN_POLLS 0
#endif
#if MC_NRF_RADIO_BUSY_SPIN_POLLS < 0 || MC_NRF_RADIO_BUSY_SPIN_POLLS > 16
#error "nRF radio yield experiment requires a bounded 0..16 poll budget"
#endif
// nRF52840 SPIClass::transfer(uint8_t) starts one SPIM transaction per byte.
// RadioLib sends whole command buffers; keep its CS, BUSY, and status handling
// but transfer each buffer in one SPIM transaction. LR1110 supports 16 MHz SPI.
class Nrf52BufferedRadioHal : public ArduinoHal {
#if MC_NRF_RADIO_BUSY_SPIN_POLLS > 0
uint8_t _busy_spin_polls_remaining = 0;
#endif
public:
explicit Nrf52BufferedRadioHal(SPIClass& bus, uint32_t hz = 16000000)
: ArduinoHal(bus, SPISettings(hz, MSBFIRST, SPI_MODE0)) {}
void spiTransfer(uint8_t* out, size_t len, uint8_t* in) override {
if (len) spi->transfer(out, in, len);
if (!len) return;
spi->transfer(out, in, len);
#if MC_NRF_RADIO_BUSY_SPIN_POLLS > 0
_busy_spin_polls_remaining = MC_NRF_RADIO_BUSY_SPIN_POLLS;
#endif
}
#if MC_NRF_RADIO_BUSY_SPIN_POLLS > 0
// Diagnostic only, off by default. RadioLib calls yield() during SPI BUSY
// polls and other waits. Spend at most N non-yielding polls after a real
// frame, then delegate every call until another frame resets the budget.
// An unused budget can reach a later non-BUSY wait; this is not BUSY-only
// detection. No GPIO reads, status checks, timeouts or interrupts change.
void yield() override {
if (_busy_spin_polls_remaining) {
--_busy_spin_polls_remaining;
return;
}
ArduinoHal::yield();
}
#endif
};
+13 -3
View File
@@ -18,7 +18,7 @@ enum Stage : uint32_t {
};
enum Result : uint32_t { Success = 0, Busy = 1, Failure = 2 };
static constexpr uint32_t Magic = 0x4d435054; // MCPT
static constexpr uint32_t Version = 1;
static constexpr uint32_t Version = 2;
static constexpr uint32_t HistogramCount = 16;
static constexpr uint32_t HistogramWidthUs = 64;
#if defined(NRF52_PLATFORM)
@@ -37,6 +37,7 @@ struct StageStats {
struct DirectionStats {
uint32_t successes, busy, failures, last_result, last_mask;
uint32_t last_us[StageCount];
uint32_t worst_us[StageCount]; // stages from the same worst successful hop
StageStats stage[StageCount];
};
struct Capture {
@@ -58,7 +59,13 @@ extern volatile Capture mesh_hil_profile_timing;
extern Work mesh_hil_profile_timing_work;
}
inline void barrier() { __asm__ __volatile__("" ::: "memory"); }
inline void barrier() {
#if defined(__arm__) || defined(__thumb__)
__asm__ __volatile__("dmb" ::: "memory");
#else
__asm__ __volatile__("" ::: "memory");
#endif
}
// Call once during wrapper initialization, outside the measured switch.
// The nRF core enables trace and CYCCNT only; it does not enable debug/halt.
@@ -113,6 +120,8 @@ inline void finish(Result result, uint32_t now_ticks) {
++capture.sequence;
barrier();
auto& direction = capture.direction[work.direction];
const bool worst = result == Success && (direction.stage[Whole].count == 0
|| work.duration_us[Whole] > direction.stage[Whole].max_us);
direction.last_result = result;
direction.last_mask = work.mask;
if (result == Success) ++direction.successes;
@@ -121,6 +130,7 @@ inline void finish(Result result, uint32_t now_ticks) {
for (uint32_t i = 0; i < StageCount; ++i) {
const uint32_t duration = work.duration_us[i];
direction.last_us[i] = duration;
if (worst) direction.worst_us[i] = duration;
if (result != Success || !(work.mask & (uint32_t(1) << i))) continue;
auto& stats = direction.stage[i];
const uint32_t previous_low = stats.sum_low_us;
@@ -137,7 +147,7 @@ inline void finish(Result result, uint32_t now_ticks) {
++capture.sequence;
}
static_assert(sizeof(Capture) == 40 + 2 * (20 + StageCount * (4 + 88)),
static_assert(sizeof(Capture) == 40 + 2 * (20 + StageCount * (8 + 88)),
"Unexpected profile timing ABI");
} // namespace profiletiming