8#if FL_PLATFORM_HAS_LARGE_MEMORY
11#define DEBUG_PRINT(x) do {} while(0)
12#define DEBUG_PRINTLN(x) do {} while(0)
60 remote.bind(
"testSimd", [](
const fl::json&
args) -> fl::json {
64 using autoresearch::simd_check::SimdTestEntry;
65 const SimdTestEntry* tests =
nullptr;
73 for (
int i = 0; i < num_tests; i++) {
74 bool ok = tests[i].func();
79 failures.
push_back(fl::string(tests[i].name));
84 response.set(
"passed", failed_count == 0);
85 response.set(
"totalTests",
static_cast<int64_t
>(num_tests));
86 response.set(
"passedTests",
static_cast<int64_t
>(passed_count));
87 response.set(
"failedTests",
static_cast<int64_t
>(failed_count));
88 if (failed_count > 0) {
95 remote.bind(
"testSimdBenchmark", [](
const fl::json&
args) -> fl::json {
100 if (
args.is_object()) {
102 }
else if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
105 if (!config.
is_null() && config.
contains(
"iterations") && config[
"iterations"].is_int()) {
106 iters =
static_cast<int>(config[
"iterations"].
as_int().value());
107 if (iters < 1) iters = 1;
108 if (iters > 1000000) iters = 1000000;
117 add.set(
"float_us",
result.add_float_us);
119 add.set(
"s16x16_us",
result.add_s16x16_us);
120 add.set(
"u16x16_us",
result.add_u16x16_us);
125 sub.
set(
"float_us",
result.sub_float_us);
127 sub.
set(
"s16x16_us",
result.sub_s16x16_us);
128 sub.
set(
"u16x16_us",
result.sub_u16x16_us);
133 mul.
set(
"float_us",
result.mul_float_us);
135 mul.
set(
"s16x16_us",
result.mul_s16x16_us);
136 mul.
set(
"u16x16_us",
result.mul_u16x16_us);
141 div.
set(
"float_us",
result.div_float_us);
143 div.
set(
"s16x16_us",
result.div_s16x16_us);
144 div.
set(
"u16x16_us",
result.div_u16x16_us);
155 remote.bind(
"animartrixPerlinBench", [](
const fl::json&
args) -> fl::json {
160 if (
args.is_object()) {
162 }
else if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
165 if (!config.
is_null() && config.
contains(
"iterations") && config[
"iterations"].is_int()) {
166 iters =
static_cast<int>(config[
"iterations"].
as_int().value());
167 if (iters < 1) iters = 1;
168 if (iters > 10000) iters = 10000;
177 response.set(
"pnoise_calls_per_iter",
static_cast<int64_t
>(256));
182 if (
result.pnoise_i16_us > 0) {
183 double speedup =
static_cast<double>(
result.pnoise_float_us) /
184 static_cast<double>(
result.pnoise_i16_us);
186 response.set(
"speedup_x1000",
static_cast<int64_t
>(speedup * 1000.0));
195 remote.bind(
"wave8ExpandBenchmark", [](
const fl::json&
args) -> fl::json {
200 if (
args.is_object()) {
202 }
else if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
205 if (!config.
is_null() && config.
contains(
"iterations") && config[
"iterations"].is_int()) {
206 iters =
static_cast<int>(config[
"iterations"].
as_int().value());
212 response.set(
"iterations",
static_cast<int64_t
>(r.iters));
213 response.set(
"expand_nibble_us",
static_cast<int64_t
>(r.expand_nibble_us));
214 response.set(
"expand_byte_us",
static_cast<int64_t
>(r.expand_byte_us));
215 response.set(
"expand_batched_us",
static_cast<int64_t
>(r.expand_batched_us));
216 response.set(
"transpose16_nibble_us",
static_cast<int64_t
>(r.transpose16_nibble_us));
217 response.set(
"transpose16_byte_us",
static_cast<int64_t
>(r.transpose16_byte_us));
218 response.set(
"sink",
static_cast<int64_t
>(r.sink));
224 remote.bind(
"ieee754CodecTest", [](
const fl::json&
args) -> fl::json {
229 response.set(
"tests_run",
static_cast<int64_t
>(r.tests_run));
230 response.set(
"tests_failed",
static_cast<int64_t
>(r.tests_failed));
231 response.set(
"first_failure", r.first_failure ? r.first_failure :
"");
232 response.set(
"expected_bits",
static_cast<int64_t
>(r.expected_bits));
233 response.set(
"actual_bits",
static_cast<int64_t
>(r.actual_bits));
239 remote.bind(
"mathExpBenchmark", [](
const fl::json&
args) -> fl::json {
240 fl::u32 iters = 20000;
241 const fl::json config =
args.is_array() &&
args.size() > 0 ?
args[0] :
args;
242 if (!config.
is_null() && config.
contains(
"iterations") && config[
"iterations"].is_int()) {
243 iters =
static_cast<fl::u32
>(config[
"iterations"].
as_int().value());
248 response.set(
"iterations",
static_cast<int64_t
>(r.iterations));
249 response.set(
"accuracy_points",
static_cast<int64_t
>(r.accuracy_points));
250 response.set(
"worst_ulp_float",
static_cast<double>(r.worst_ulp_float));
251 response.set(
"worst_x_float",
static_cast<double>(r.worst_x_float));
252 response.set(
"worst_ulp_double", r.worst_ulp_double);
253 response.set(
"worst_x_double", r.worst_x_double);
254 response.set(
"fl_expf_us",
static_cast<int64_t
>(r.fl_expf_us));
255 response.set(
"libm_expf_us",
static_cast<int64_t
>(r.libm_expf_us));
256 response.set(
"fl_exp_us",
static_cast<int64_t
>(r.fl_exp_us));
257 response.set(
"libm_exp_us",
static_cast<int64_t
>(r.libm_exp_us));
258 response.set(
"large_memory",
static_cast<int64_t
>(r.large_memory));
259 response.set(
"sink",
static_cast<double>(r.sink));
269 remote.bind(
"fftCqOctaveBench", [](
const fl::json&
args) -> fl::json {
270 fl::i32 samples = 512;
273 const fl::json config =
args.is_array() &&
args.size() > 0 ?
args[0] :
args;
275 if (config.
contains(
"samples") && config[
"samples"].is_int())
276 samples =
static_cast<fl::i32
>(config[
"samples"].
as_int().value());
277 if (config.
contains(
"bands") && config[
"bands"].is_int())
278 bands =
static_cast<fl::i32
>(config[
"bands"].
as_int().value());
279 if (config.
contains(
"frames") && config[
"frames"].is_int())
280 frames =
static_cast<fl::i32
>(config[
"frames"].
as_int().value());
283 response.set(
"samples",
static_cast<int64_t
>(samples));
284 response.set(
"bands",
static_cast<int64_t
>(bands));
285 response.set(
"frames",
static_cast<int64_t
>(frames));
286 if (samples < 64 || samples > 4096 || bands < 2 || bands > 512 ||
287 frames < 1 || frames > 10000) {
289 response.set(
"error",
"out_of_range");
292 const fl::i32 kSampleRate = 44100;
293 fl::vector<fl::i16> signal(
static_cast<fl::size
>(samples));
294 for (fl::i32 i = 0; i < samples; ++i) {
295 const float t =
static_cast<float>(i) /
static_cast<float>(kSampleRate);
296 const float v = 0.5f *
fl::sinf(2.0f *
static_cast<float>(
FL_M_PI) * 220.0f *
t) +
298 signal[
static_cast<fl::size
>(i)] =
static_cast<fl::i16
>(v * 20000.0f);
301 fl::audio::fft::Args fftArgs(samples, bands, 174.6f, 4698.3f, kSampleRate,
303 fl::audio::fft::Impl
fft(fftArgs);
304 fl::audio::fft::Bins out(
static_cast<fl::size
>(bands));
306 fft.run(signal, &out);
310 for (fl::i32 f = 0; f < frames; ++f) {
311 fft.run(signal, &out);
313 const fl::u32 warmTotalUs =
fl::micros() - t1;
315 float checksum = 0.0f;
316 for (
float b : out.raw()) checksum += b;
318 response.set(
"first_frame_us",
static_cast<int64_t
>(firstUs));
319 response.set(
"warm_frame_us",
static_cast<double>(warmTotalUs) / frames);
320 response.set(
"heap_before",
static_cast<int64_t
>(heapBefore));
321 response.set(
"heap_after_first",
static_cast<int64_t
>(heapAfterFirst));
322 response.set(
"heap_after_warm",
static_cast<int64_t
>(heapAfterWarm));
323 response.set(
"checksum",
static_cast<double>(checksum));
336 remote.bind(
"mp3CodecTest", [](
const fl::json&
args) -> fl::json {
341 response.set(
"streams_run",
static_cast<int64_t
>(r.streams_run));
342 response.set(
"streams_failed",
static_cast<int64_t
>(r.streams_failed));
343 response.set(
"first_failure", r.first_failure ? r.first_failure :
"");
344 response.set(
"expected_fnv1a",
static_cast<int64_t
>(r.expected_fnv1a));
345 response.set(
"actual_fnv1a",
static_cast<int64_t
>(r.actual_fnv1a));
347 static_cast<int64_t
>(r.expected_samples));
348 response.set(
"actual_samples",
static_cast<int64_t
>(r.actual_samples));
349 response.set(
"decode_micros",
static_cast<int64_t
>(r.decode_micros));
350 response.set(
"scratch_bytes",
static_cast<int64_t
>(r.scratch_bytes));
351 response.set(
"shared_decoder_matched", r.shared_decoder_matched);
353 static_cast<int64_t
>(r.samples_decoded));
355 static_cast<int64_t
>(r.frames_decoded));
357 static_cast<int64_t
>(r.combined_fnv1a));
359 static_cast<int64_t
>(r.verify_micros));
361 static_cast<int64_t
>(r.audio_micros));
370 remote.bind(
"parlioStreamValidate", [
this](
const fl::json&
args) -> fl::json {
372 auto invalidArgs = [](
const char* message) -> fl::json {
374 error.
set(
"success",
false);
375 error.
set(
"error",
"InvalidArgs");
376 error.
set(
"message", message);
383 int timeout_ms = 200;
384 int base_tx_pin =
mState->pin_tx;
386 bool has_tx_pins =
false;
387 bool num_lanes_provided =
false;
393 if (
args.is_object()) {
395 }
else if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
399 if (config.
contains(
"baseTxPin") && config[
"baseTxPin"].is_int())
400 base_tx_pin =
static_cast<int>(config[
"baseTxPin"].
as_int().value());
402 if (!config[
"numLanes"].is_int()) {
403 return invalidArgs(
"numLanes must be an integer");
405 num_lanes =
static_cast<int>(config[
"numLanes"].
as_int().value());
406 num_lanes_provided =
true;
408 if (config.
contains(
"numLeds") && config[
"numLeds"].is_int())
409 num_leds =
static_cast<int>(config[
"numLeds"].
as_int().value());
410 if (config.
contains(
"iterations") && config[
"iterations"].is_int())
411 iterations =
static_cast<int>(config[
"iterations"].
as_int().value());
412 if (config.
contains(
"timeoutMs") && config[
"timeoutMs"].is_int())
413 timeout_ms =
static_cast<int>(config[
"timeoutMs"].
as_int().value());
415 if (!config[
"txPins"].is_array()) {
416 return invalidArgs(
"txPins must be an integer array");
418 if (!num_lanes_provided) {
419 return invalidArgs(
"txPins requires numLanes");
423 return invalidArgs(
"numLanes out of range for txPins");
426 const fl::json
pins = config[
"txPins"];
427 if (
pins.size() !=
static_cast<size_t>(num_lanes)) {
428 return invalidArgs(
"txPins length must match numLanes");
431 for (
int i = 0; i < num_lanes; ++i) {
432 if (!
pins[i].is_int()) {
433 return invalidArgs(
"txPins entries must be integers");
435 int64_t pin_value =
pins[i].as_int().value();
436 if (pin_value < 0 || pin_value >= 64) {
437 return invalidArgs(
"txPins entries must be in range 0..63");
439 tx_pins[i] =
static_cast<int>(pin_value);
442 base_tx_pin = tx_pins[0];
447 if (base_tx_pin < 0) base_tx_pin = 0;
448 if (num_lanes < 1) num_lanes = 1;
449 if (num_lanes > 16) num_lanes = 16;
450 if (num_leds < 1) num_leds = 1;
451 if (num_leds > 256) num_leds = 256;
452 if (iterations < 1) iterations = 1;
456 if (timeout_ms < 1) timeout_ms = 1;
457 if (timeout_ms > 5000) timeout_ms = 5000;
460 base_tx_pin, num_lanes, num_leds, iterations,
462 has_tx_pins ? tx_pins :
nullptr);
465 response.set(
"channelsOk", r.channels_ok);
466 response.set(
"completed", r.completed);
467 response.set(
"baseTxPin",
static_cast<int64_t
>(r.base_tx_pin));
468 int last_tx_pin = r.base_tx_pin + r.lanes - 1;
469 if (r.explicit_tx_pins) {
470 for (
int i = r.lanes - 1; i >= 0; --i) {
471 if (r.tx_pins[i] >= 0) {
472 last_tx_pin = r.tx_pins[i];
477 response.set(
"lastTxPin",
static_cast<int64_t
>(last_tx_pin));
478 response.set(
"explicitTxPins", r.explicit_tx_pins);
479 response.set(
"lanes",
static_cast<int64_t
>(r.lanes));
480 response.set(
"ledsPerLane",
static_cast<int64_t
>(r.leds_per_lane));
481 response.set(
"iterations",
static_cast<int64_t
>(r.iterations));
482 response.set(
"steadyAvgUs",
static_cast<int64_t
>(r.steady_avg_us));
483 response.set(
"steadyAvgShowUs",
static_cast<int64_t
>(r.steady_avg_show_us));
484 response.set(
"steadyAvgWaitUs",
static_cast<int64_t
>(r.steady_avg_wait_us));
485 response.set(
"failedIter",
static_cast<int64_t
>(r.failed_iter));
486 response.set(
"timeoutMs",
static_cast<int64_t
>(r.timeout_ms));
487 response.set(
"txDoneCount",
static_cast<int64_t
>(r.tx_done_count));
488 response.set(
"workerIsrCount",
static_cast<int64_t
>(r.worker_isr_count));
489 response.set(
"underrunCount",
static_cast<int64_t
>(r.underrun_count));
490 response.set(
"ringCount",
static_cast<int64_t
>(r.ring_count));
491 response.set(
"bytesTotal",
static_cast<int64_t
>(r.bytes_total));
492 response.set(
"bytesTransmitted",
static_cast<int64_t
>(r.bytes_transmitted));
493 response.set(
"ringError", r.ring_error);
494 response.set(
"hardwareIdle", r.hardware_idle);
496 for (
int i = 0; i < r.lanes; ++i) {
497 response_tx_pins.
push_back(
static_cast<int64_t
>(r.tx_pins[i]));
499 response.set(
"txPins", response_tx_pins);
503 for (
int i = 0; i < r.iterations; ++i) {
504 per_iter.
push_back(
static_cast<int64_t
>(r.per_iter_us[i]));
505 per_iter_show.
push_back(
static_cast<int64_t
>(r.per_iter_show_us[i]));
506 per_iter_wait.
push_back(
static_cast<int64_t
>(r.per_iter_wait_us[i]));
508 response.set(
"perIterUs", per_iter);
509 response.set(
"perIterShowUs", per_iter_show);
510 response.set(
"perIterWaitUs", per_iter_wait);
518 remote.bind(
"parlioEncodeBenchmark", [](
const fl::json&
args) -> fl::json {
523 if (
args.is_object()) {
525 }
else if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
528 if (!config.
is_null() && config.
contains(
"iterations") && config[
"iterations"].is_int()) {
529 iters =
static_cast<int>(config[
"iterations"].
as_int().value());
534 response.set(
"success", r.iters > 0);
535 response.set(
"iters",
static_cast<int64_t
>(r.iters));
536 response.set(
"lanes",
static_cast<int64_t
>(r.lanes));
537 response.set(
"leds_per_lane",
static_cast<int64_t
>(r.leds_per_lane));
538 response.set(
"scratchPsramOk", r.scratch_psram_ok);
539 response.set(
"outputPsramOk", r.output_psram_ok);
540 response.set(
"perpos_ss_us",
static_cast<int64_t
>(r.perpos_ss_us));
541 response.set(
"perpos_sp_us",
static_cast<int64_t
>(r.perpos_sp_us));
542 response.set(
"perpos_ps_us",
static_cast<int64_t
>(r.perpos_ps_us));
543 response.set(
"perpos_pp_us",
static_cast<int64_t
>(r.perpos_pp_us));
545 constexpr fl::u32 kFrameBytePositions = 256 * 3;
546 response.set(
"frame_ss_us",
static_cast<int64_t
>(
547 static_cast<fl::u64
>(r.perpos_ss_us) * kFrameBytePositions / r.iters));
548 response.set(
"frame_sp_us",
static_cast<int64_t
>(
549 static_cast<fl::u64
>(r.perpos_sp_us) * kFrameBytePositions / r.iters));
550 response.set(
"frame_ps_us",
static_cast<int64_t
>(
551 static_cast<fl::u64
>(r.perpos_ps_us) * kFrameBytePositions / r.iters));
552 response.set(
"frame_pp_us",
static_cast<int64_t
>(
553 static_cast<fl::u64
>(r.perpos_pp_us) * kFrameBytePositions / r.iters));
555 response.set(
"sink",
static_cast<int64_t
>(r.sink));
569 remote.bind(
"flexIoDeviceInfo", [](
const fl::json&
args) -> fl::json {
572 if (
args.is_object()) {
574 }
else if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
578 config[
"instance"].is_int()) {
579 int64_t iv = config[
"instance"].
as_int().value();
580 if (iv >= 0 && iv <= 255) {
581 instance =
static_cast<fl::u8>(iv);
Bus::FLEX_IO runtime-binding introspection RPC handler (FastLED#3515 Phase B).
On-device verification for the integer-only IEEE 754 decimal codec.
On-device accuracy and speed of fl::exp against the toolchain libm (#4288).
On-device verification of the fixed-point MP3 decoder.
Full PARLIO encode bench for ESP32-P4 (post-byte-LUT, #2526 landed).
PARLIO ISR-streaming functional validation (#2548 deep-dive follow-up).
Issue #2994 repro: compounded per-sequence timing drift vs 3.10.3.
Wave8 expansion micro-benchmark for #2526.
fl::unique_ptr< fl::Remote > remote
fl::net::ble — BLE GATT transport layer for JSON-RPC
SPI encoder configuration for clocked LED chipsets.
fl::shared_ptr< AutoResearchState > mState
void bindMathMethods(fl::Remote &remote)
JSON-RPC server with scheduling support.
bool is_object() const FL_NO_EXCEPT
fl::optional< i64 > as_int() const FL_NO_EXCEPT
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
bool is_null() const FL_NO_EXCEPT
void push_back(const json &value) FL_NO_EXCEPT
bool contains(size_t idx) const FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
static json array() FL_NO_EXCEPT
Task executor — runs registered task runners and manages the run loop.
Platform-abstracted heap memory query functions.
FastLED's Elegant JSON Library: fl::json
PerlinBenchResult runPerlinBenchmark(int iters)
fl::json buildDeviceInfo(fl::u8 instance) FL_NO_EXCEPT
Build the device-info JSON returned by the flexIoDeviceInfo RPC.
Result run(fl::u32 iterations)
Runs the accuracy sweep and the timed loops. iterations = calls per loop.
ParlioEncodeResult measureParlioEncode(int=12000)
ValidateResult validateParlioStreaming(int base_tx_pin, int num_lanes, int num_leds, int iterations, uint32_t timeout_ms, const int *tx_pins=nullptr)
Run the PARLIO streaming functional test.
constexpr int kMaxIterations
void getTests(const SimdTestEntry **out_tests, int *out_count)
Get the static test table. Used by both runSimdTests() and the RPC handler.
BenchmarkResult runMultiplyBenchmark(int iters=10000)
Wave8ExpandResult measureWave8Expand(int=30000)
HeapInfo getFreeHeap()
Query available heap memory.
float sinf(float value) FL_NO_EXCEPT
fl::u32 micros()
Universal microsecond timer - returns microseconds since system startup.
expected< T, E > result
Alias for expected (Rust-style naming)
float add(float &a, float &b)
Promise-based fluent API for FastLED - standalone async primitives.
Umbrella header for SIMD subsystem.
fl::size free_sram
Free SRAM in bytes (internal fast memory)