1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// SPDX-License-Identifier: GPL-3.0-or-later
//! Real f32 dot product, with an optional caller-provided backend.
//!
//! The DSP hot loops in this crate reduce to `Σ a[k]·b[k]` over a
//! contiguous pair of f32 slices —
//! [`PolyphaseResampler::dot`](super::polyphase::PolyphaseResampler)
//! and [`FirStage::dot`](super::fir_decimate::FirStage) both literally,
//! and `engine::sync2d`'s coherent correlator once its complex product
//! is expanded per channel. On a host that loop is fine. On Xtensa it
//! is the single largest cost in the FST4 embedded DDC pipeline, and
//! the portable form leaves most of the chip on the table.
//!
//! Measured on real CoreS3 hardware (issue #307,
//! `embedded-poc/m5stack-cores3-app/src/bin/dotprod_bench.rs`,
//! 20 000 iterations per depth):
//!
//! | depth | this loop | esp-dsp `ae32` | esp-dsp `aes3` (aligned) |
//! |---|---:|---:|---:|
//! | 107 | 6733 ns | 1918 ns | 905 ns |
//! | 288 | 18052 ns | 4937 ns | 2031 ns |
//! | 422 | 26432 ns | 7171 ns | 2881 ns |
//!
//! i.e. ~15 cycles/MAC portable versus ~4.1 (`ae32`) and ~1.6 (`aes3`
//! 4-wide PIE). Hence this seam.
//!
//! ## Why a dot-product hook rather than a whole-stage one
//!
//! `docs/notes/FST4_DDC_DESIGN.md` §4.5 originally proposed replacing
//! an entire `FirDecimator` stage, on the reasoning that esp-dsp's
//! `fir_f32_t` owns its own delay line and position state, so swapping
//! only the inner product would leave two state machines fighting.
//! That is true of `dsps_fird_f32`, but it does not apply to
//! `dsps_dotprod_f32`, which is stateless — it takes two pointers and a
//! length. The narrower seam keeps every existing history-buffer,
//! phase-carry and compaction invariant in Rust, where the unit tests
//! already cover them, and lets the backend be a pure function.
//!
//! It also serves the rational resampler, which the stage-level hook
//! could not: `dsps_fird_f32` decimates by an integer factor and cannot
//! express `PolyphaseResampler`'s per-output tap-phase rotation. The
//! wideband FST4 cascade has *no* integer stages at all, so the
//! stage-level hook would have accelerated none of it.
//!
//! ## Alignment is not a correctness concern
//!
//! `dsps_dotprod_f32_aes3` checks `len % 4 == 0` and 16-byte alignment
//! on both pointers and branches to its scalar body when either fails
//! (verified in that function's own assembly, and measured: the
//! misaligned path lands at `ae32` speed, still ~3.6x this loop). So a
//! backend may be handed any slice pair; unaligned input costs
//! throughput, never correctness. This is materially different from
//! `engine::fft`'s ESP-DSP backend, where misalignment corrupted an
//! allocator block header.
/// Backend hook: `Σ a[k]·b[k]`, where `a.len() == b.len()`.
///
/// With `dotprod-extern`, the binary supplies:
///
/// ```ignore
/// #[unsafe(no_mangle)]
/// pub extern "Rust" fn mfsk_core_dotprod_f32(a: &[f32], b: &[f32]) -> f32 { .. }
/// ```
///
/// Slices, not raw pointers, so the hook carries its own length and the
/// caller cannot pass a mismatched pair — the same shape
/// `mfsk_core_make_default_fft_planner` uses for its own factory.
/// The portable reference implementation, and the fallback when no
/// backend is configured. Kept public so a backend can defer to it for
/// lengths too short to be worth an FFI hop, and so tests can compare
/// against it directly.
///
/// Four partial sums rather than one: the accumulation is a dependent
/// chain and Xtensa's `fadd` has ~3-4 cycle latency against 1-cycle
/// throughput, the same reasoning
/// [`FirStage::dot`](super::fir_decimate::FirStage) documents.