233 lines
9.9 KiB
C++
233 lines
9.9 KiB
C++
#include <cassert>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <helpers/radiolib/ESP32RadioTrace.h>
|
|
|
|
using namespace mesh::radiotrace;
|
|
static int radio;
|
|
static constexpr uint64_t Base = UINT64_C(0x100000000) + 100000;
|
|
|
|
static void begin(Trace& t) {
|
|
t.context(&radio, 0, 7);
|
|
assert(t.arm(Base, 0, MaxCaptureMs));
|
|
assert(t.tick(Base));
|
|
}
|
|
|
|
static void lifetime() {
|
|
Trace t;
|
|
assert(!t.pending() && !t.tick(Base));
|
|
assert(!t.arm(Base, 0, 0));
|
|
assert(!t.arm(Base, MaxDelayMs + 1, 1));
|
|
assert(!t.arm(Base, 0, MaxCaptureMs + 1));
|
|
assert(t.arm(Base, 10, 20));
|
|
assert(t.session == 1 && t.state == State::Armed);
|
|
assert(!t.arm(Base, 0, 1));
|
|
assert(!t.tick(Base + 9999));
|
|
assert(t.tick(Base + 10000));
|
|
assert(t.state == State::Capturing);
|
|
assert(!t.tick(Base + 30000));
|
|
assert(t.state == State::Frozen && !t.pending());
|
|
assert(t.arm(Base + 40000, 0, 1) && t.session == 2);
|
|
t.stop(Base + 40500);
|
|
assert(t.state == State::Frozen);
|
|
assert(t.arm(Base, 10, 10));
|
|
t.stop(Base);
|
|
assert(t.state == State::Frozen && !t.span_count && !t.flags);
|
|
}
|
|
|
|
static void holdsAndReceipts() {
|
|
Trace t; begin(t);
|
|
t.receiveEntry(&radio, Base, Base + 1, 0);
|
|
t.irq(&radio, Base + 10, Base + 11, 0, 0);
|
|
t.activity(&radio, Base + 20, Base + 21, true, false, None);
|
|
t.activity(&radio, Base + 30, Base + 31, true, false, None);
|
|
t.activity(&radio, Base + 40, Base + 41, true, true, None);
|
|
t.activity(&radio, Base + 50, Base + 51, true, true, None);
|
|
assert(t.span_count == 1 && t.spans[0].begin_us == 21);
|
|
assert(t.spans[0].header_us == 41 && t.spans[0].end_us == NoTime);
|
|
t.irq(&radio, Base + 60, Base + 61, 0, 2);
|
|
auto i = t.receiveRead(&radio, Base + 70);
|
|
assert(i == 0 && t.rx_count == 1);
|
|
assert(t.rx[0].done_lo_us == 10 && t.rx[0].done_hi_us == 61);
|
|
assert(t.rx[0].span_index == 0 && t.spans[0].rx_index == 0);
|
|
assert(t.spans[0].end_us == 70 && t.spans[0].reason == ReceiveRead);
|
|
t.operation(&radio, Base + 80, Rearm);
|
|
t.receiveEntry(&radio, Base + 80, Base + 90, 0);
|
|
t.receiveFinish(&radio, i, Base + 100, UINT64_C(0xaabbccddeeff1234), 101, true);
|
|
assert(t.rx[0].hash64 == UINT64_C(0xaabbccddeeff1234));
|
|
assert(t.rx[0].len == 101 && !t.rx[0].flags);
|
|
assert(t.rx[0].rearm_end_us == 90 && t.max_blackout_us == 20);
|
|
t.activity(&radio, Base + 110, Base + 111, true, false, None);
|
|
t.activity(&radio, Base + 210, Base + 211, false, false, PreambleExpired);
|
|
assert(t.spans[1].header_us == NoTime && t.spans[1].end_us == 210);
|
|
assert(t.spans[1].reason == PreambleExpired);
|
|
t.irq(&radio, Base + 220, Base + 221, 0, 2);
|
|
i = t.receiveRead(&radio, Base + 230);
|
|
assert(t.rx[i].span_index == NoIndex); // no stale abandoned-span link
|
|
t.receiveEntry(&radio, Base + 231, Base + 235, -1);
|
|
t.receiveFinish(&radio, i, Base + 240, 0, 0, false);
|
|
assert((t.rx[i].flags & (Invalid | RearmFailed | ReadError)) == (Invalid | RearmFailed | ReadError));
|
|
}
|
|
|
|
static void transmitBounds() {
|
|
Trace t; begin(t);
|
|
t.transmitPrepare(&radio, Base + 10, 0x1234, 101, 397824);
|
|
t.transmitLaunch(&radio, Base + 20, Base + 30, Base + 30, 0);
|
|
t.dio(&radio, Base + 400000, Base + 400001, false);
|
|
t.irq(&radio, Base + 400010, Base + 400020, 0, 0);
|
|
t.irq(&radio, Base + 400030, Base + 400040, 0, 1);
|
|
t.dio(&radio, Base + 400050, Base + 400051, false); // cannot move a latched bound
|
|
t.irq(&radio, Base + 400060, Base + 400070, 0, 1); // retain first positive
|
|
t.transmitFinish(&radio, Base + 400080, true);
|
|
auto& r = t.tx[0];
|
|
assert(t.tx_count == 1 && !r.flags && r.hash64 == 0x1234);
|
|
assert(r.done_lo_us == 400010 && r.done_hi_us == 400040);
|
|
assert(r.launch_lo_us == 20 && r.launch_hi_us == 30 && r.busy_low_us == 30);
|
|
assert(r.toa_us == 397824 && !r.spi_errors);
|
|
t.transmitFinish(&radio, Base + 400090, false); // normal finish cleanup is inert
|
|
assert(!r.flags);
|
|
t.transmitPrepare(&radio, Base + 500000, 0x5678, 101, 652288);
|
|
t.transmitLaunch(&radio, Base + 500001, Base + 500002, Base + 500003, 0);
|
|
t.irq(&radio, Base + 500010, Base + 500020, 0, 1);
|
|
t.transmitFinish(&radio, Base + 500030, true);
|
|
assert(t.tx[1].flags == MissingBound); // old TX cannot donate its lower bound
|
|
}
|
|
|
|
static void spiFailure() {
|
|
Trace t; begin(t);
|
|
t.transmitPrepare(&radio, Base + 10, 1, 101, 100);
|
|
t.transmitLaunch(&radio, Base + 11, Base + 12, Base + 13, 0);
|
|
t.irq(&radio, Base + 20, Base + 21, -707, 0);
|
|
t.irq(&radio, Base + 30, Base + 31, 0, 1);
|
|
t.transmitFinish(&radio, Base + 40, true);
|
|
assert(t.spi_errors == 1 && t.invalidations == 1);
|
|
assert((t.tx[0].flags & (Invalid | SpiError | MissingBound)) == (Invalid | SpiError | MissingBound));
|
|
assert(t.tx[0].spi_errors == 1);
|
|
Trace r; begin(r);
|
|
r.receiveEntry(&radio, Base, Base + 1, 0);
|
|
r.activity(&radio, Base + 10, Base + 11, true, false, None);
|
|
r.irq(&radio, Base + 20, Base + 21, -707, 0);
|
|
assert(r.spans[0].reason == SpiFailure && r.spans[0].end_us == 20);
|
|
r.irq(&radio, Base + 30, Base + 31, 0, 2);
|
|
auto i = r.receiveRead(&radio, Base + 40);
|
|
assert(r.rx[i].flags & MissingBound);
|
|
}
|
|
|
|
static void interruptionsAndFreeze() {
|
|
Trace t; begin(t);
|
|
t.activity(&radio, Base + 10, Base + 11, true, true, None);
|
|
t.operation(&radio, Base + 20, Reconfigure);
|
|
assert(t.spans[0].end_us == 20 && t.spans[0].reason == Reconfigure);
|
|
t.transmitPrepare(&radio, Base + 30, 1, 101, 100);
|
|
t.stop(Base + 40);
|
|
assert(t.state == State::Frozen && (t.flags & Invalid));
|
|
assert((t.tx[0].flags & (Invalid | Clipped | NotConfirmed)) == (Invalid | Clipped | NotConfirmed));
|
|
const auto frozen = t;
|
|
t.activity(&radio, Base + 50, Base + 51, true, false, None);
|
|
t.irq(&radio, Base + 60, Base + 61, 0, 1);
|
|
t.transmitFinish(&radio, Base + 70, true);
|
|
assert(!std::memcmp(&frozen, &t, sizeof(t)));
|
|
Trace r; begin(r);
|
|
r.receiveEntry(&radio, Base, Base + 1, 0);
|
|
r.irq(&radio, Base + 10, Base + 11, 0, 2);
|
|
r.receiveRead(&radio, Base + 20);
|
|
r.tick(r.end_us);
|
|
assert(r.state == State::Frozen && (r.rx[0].flags & Clipped));
|
|
}
|
|
|
|
static void overflowAndOwnership() {
|
|
Trace t; begin(t);
|
|
for (size_t i = 0; i < SpanCapacity + 2; ++i) {
|
|
t.activity(&radio, Base + i * 10 + 1, Base + i * 10 + 2, true, false, None);
|
|
t.activity(&radio, Base + i * 10 + 3, Base + i * 10 + 4, false, false, HeaderError);
|
|
}
|
|
assert(t.span_count == SpanCapacity && t.span_dropped == 2);
|
|
assert(t.spans[0].begin_us == 2 && (t.flags & Invalid));
|
|
Trace r; begin(r);
|
|
for (size_t i = 0; i < RxCapacity + 1; ++i) {
|
|
const auto record = r.receiveRead(&radio, Base + i * 10 + 1);
|
|
r.receiveEntry(&radio, Base + i * 10 + 2, Base + i * 10 + 3, 0);
|
|
r.receiveFinish(&radio, record, Base + i * 10 + 4, i, 101, true);
|
|
}
|
|
assert(r.rx_count == RxCapacity && r.rx_dropped == 1);
|
|
Trace s; begin(s);
|
|
for (size_t i = 0; i < TxCapacity + 1; ++i) {
|
|
s.transmitPrepare(&radio, Base + i * 10 + 1, i, 101, 100);
|
|
s.transmitFinish(&radio, Base + i * 10 + 2, false);
|
|
}
|
|
assert(s.tx_count == TxCapacity && s.tx_dropped == 1);
|
|
int other;
|
|
s.context(&other, 1, 9);
|
|
assert(s.context_errors == 1);
|
|
const auto count = s.tx_count;
|
|
s.transmitPrepare(&other, Base + 10000, 0, 101, 100);
|
|
assert(s.tx_count == count);
|
|
}
|
|
|
|
static void scanTiming() {
|
|
Trace t; begin(t);
|
|
t.scan(&radio, Base + 10, Base, 100);
|
|
t.scan(&radio, Base + 1000, Base, 100, false);
|
|
t.scan(&radio, Base + 2010, Base + 2000, 100);
|
|
assert(t.max_scan_gap_us == 2000 && t.max_visit_overdue_us == 900);
|
|
assert(t.buckets[0].max_scan_gap_us == 2000);
|
|
assert(t.buckets[0].max_scan_gap_end_us == 2010);
|
|
assert(t.buckets[0].max_visit_overdue_end_us == 1000);
|
|
t.scan(&radio, Base + BucketWidthUs + 10, Base + BucketWidthUs, 100);
|
|
assert(t.bucket_count == 2 && t.buckets[1].start_us == BucketWidthUs);
|
|
assert(t.buckets[0].max_scan_gap_end_us == 2010);
|
|
// Expiry is exclusive, so the final valid bucket is 59 for 180 seconds.
|
|
t.scan(&radio, Base + uint64_t(MaxCaptureMs) * 1000 - 1, 0, 0);
|
|
assert(t.bucket_count == 60 && !(t.flags & Invalid));
|
|
}
|
|
|
|
static void epochsAndMissingLaunch() {
|
|
Trace t; begin(t);
|
|
t.transmitPrepare(&radio, Base + 10, 1, 101, 100);
|
|
t.dio(&radio, Base + 11, Base + 12, false);
|
|
t.irq(&radio, Base + 13, Base + 14, 0, 1); // previous epoch, before SetTx
|
|
t.transmitLaunchBegin(&radio, Base + 20);
|
|
t.transmitLaunch(&radio, Base + 20, Base + 21, Base + 22, 0);
|
|
t.irq(&radio, Base + 120, Base + 121, 0, 1);
|
|
t.transmitFinish(&radio, Base + 122, true);
|
|
assert(t.tx[0].done_hi_us == 121 && t.tx[0].flags == MissingBound);
|
|
t.transmitPrepare(&radio, Base + 200, 2, 101, 100);
|
|
t.irq(&radio, Base + 201, Base + 202, 0, 1);
|
|
t.transmitFinish(&radio, Base + 203, true);
|
|
assert((t.tx[1].flags & (Invalid | MissingBound | NotConfirmed)) == (Invalid | MissingBound | NotConfirmed));
|
|
|
|
Trace r;r.context(&radio,0,1);
|
|
assert(r.arm(Base,10,1000));
|
|
r.receiveEntry(&radio,Base+100,Base+101,0); // establish mode while armed
|
|
r.irq(&radio,Base+10010,Base+10011,0,0);
|
|
r.irq(&radio,Base+10020,Base+10021,0,2);
|
|
auto i=r.receiveRead(&radio,Base+10030);
|
|
assert(!(r.rx[i].flags&MissingBound));
|
|
r.receiveEntry(&radio,Base+10031,Base+10032,0);
|
|
r.receiveFinish(&radio,i,Base+10033,1,101,true);
|
|
r.stop(Base+10040);
|
|
assert(r.arm(Base+20000,0,1000)); // stale mode/bounds from old capture cannot survive
|
|
r.irq(&radio,Base+20010,Base+20011,0,0);
|
|
r.irq(&radio,Base+20020,Base+20021,0,2);
|
|
i=r.receiveRead(&radio,Base+20030);
|
|
assert(r.rx[i].flags&MissingBound);
|
|
Trace receiver;begin(receiver);
|
|
receiver.context(&radio,0,7);
|
|
receiver.context(&radio,0,8);
|
|
assert((receiver.flags&(Invalid|Interrupted))==(Invalid|Interrupted));
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
assert(argc == 2);
|
|
if (!std::strcmp(argv[1], "lifetime")) lifetime();
|
|
else if (!std::strcmp(argv[1], "holds")) holdsAndReceipts();
|
|
else if (!std::strcmp(argv[1], "tx")) transmitBounds();
|
|
else if (!std::strcmp(argv[1], "spi")) spiFailure();
|
|
else if (!std::strcmp(argv[1], "freeze")) interruptionsAndFreeze();
|
|
else if (!std::strcmp(argv[1], "overflow")) overflowAndOwnership();
|
|
else if (!std::strcmp(argv[1], "scan")) scanTiming();
|
|
else if (!std::strcmp(argv[1], "epochs")) epochsAndMissingLaunch();
|
|
else assert(false);
|
|
}
|