FastLED 3.10.6
Loading...
Searching...
No Matches
can_match.cpp.hpp
Go to the documentation of this file.
1
3
4// IWYU pragma: private
5
8
9namespace fl {
10
12
13// "Strongest" rejection rank used when accumulating per-group failures
14// across an entire driver's groups. We surface the most informative
15// reason: pin/pair > frequency > timing > capacity > protocol.
16// Yes always wins.
18 switch (r) {
19 case HandleResult::Yes: return 100u;
20 case HandleResult::NoPinPair: return 50u;
21 case HandleResult::NoPin: return 45u;
22 case HandleResult::NoFrequency: return 40u;
23 case HandleResult::NoTiming: return 35u;
24 case HandleResult::NoCapacity: return 30u;
25 case HandleResult::NoProtocol: return 10u;
26 }
27 return 0u;
28}
29
30// Resolve which frequency range applies to a request landing in `g`,
31// falling back to the driver-level *_frequency when the group doesn't
32// constrain it.
34 const DriverCapabilities& caps, const PinGroup& g) FL_NO_EXCEPT {
35 if (!g.frequency.isUnspecified()) {
36 return g.frequency;
37 }
38 return (g.protocol == Protocol::Clockless)
39 ? caps.clockless_frequency
40 : caps.spi_frequency;
41}
42
43// "Can this driver hit a phase of integer N ticks where the phase
44// duration N / effective_clock_hz (seconds) is inside `window`
45// (nanoseconds)?" Equivalently: is there an integer N >= 1 such that
46// window.min_ns <= N * 1e9 / effective_clock_hz <= window.max_ns
47// Solving for N integer-safely (no truncated tick_ns intermediate):
48// n_lo = ceil(window.min_ns * effective_clock_hz / 1e9)
49// n_hi = floor(window.max_ns * effective_clock_hz / 1e9)
50// Match iff n_lo <= n_hi AND n_lo <= max_ticks (when max_ticks != 0).
51//
52// The u64 numerator avoids the tick_ns truncation that previously
53// mis-ranked tight windows near boundaries (CodeRabbit #3197).
54inline bool canHitPhase(
55 fl::u32 effective_clock_hz, fl::u16 max_ticks,
56 const NanosRange& window) FL_NO_EXCEPT {
57 if (window.isUnspecified()) {
58 // No chipset constraint on this phase.
59 return true;
60 }
61 if (effective_clock_hz == 0u) {
62 return false;
63 }
64 const fl::u64 kBillion = 1000000000ULL;
65 const fl::u64 lo_num = static_cast<fl::u64>(window.min_ns) * effective_clock_hz;
66 const fl::u64 hi_num = static_cast<fl::u64>(window.max_ns) * effective_clock_hz;
67 // ceil(lo_num / kBillion) — divide-and-round-up without overflow.
68 fl::u64 n_lo_64 = (lo_num + kBillion - 1ULL) / kBillion;
69 fl::u64 n_hi_64 = hi_num / kBillion;
70 if (n_lo_64 < 1ULL) {
71 n_lo_64 = 1ULL;
72 }
73 if (n_lo_64 > n_hi_64) {
74 return false;
75 }
76 if (max_ticks != 0u && n_lo_64 > static_cast<fl::u64>(max_ticks)) {
77 return false;
78 }
79 return true;
80}
81
82// True iff the driver can hit all four chipset tolerance windows at
83// SOME divider value within [min_divider, max_divider].
87 if (cap.isUnspecified()) {
88 // Driver doesn't constrain timing — treat as "any chipset fits"
89 // (e.g. DMA-fed pattern shifters where any tick width is OK).
90 return true;
91 }
92 if (chip.t0h_window.isUnspecified() && chip.t0l_window.isUnspecified()
93 && chip.t1h_window.isUnspecified() && chip.t1l_window.isUnspecified()) {
94 return true;
95 }
96 fl::u16 d_lo = cap.min_divider == 0u ? 1u : cap.min_divider;
97 fl::u16 d_hi = cap.max_divider == 0u ? d_lo : cap.max_divider;
98 if (d_hi < d_lo) {
99 return false;
100 }
101 for (fl::u32 d = d_lo; d <= d_hi; ++d) {
102 if (d == 0u) {
103 continue;
104 }
105 fl::u32 effective_clock = cap.base_clock_hz / static_cast<fl::u32>(d);
106 if (effective_clock == 0u) {
107 continue;
108 }
109 if (canHitPhase(effective_clock, cap.max_phase_ticks, chip.t0h_window) &&
110 canHitPhase(effective_clock, cap.max_phase_ticks, chip.t0l_window) &&
111 canHitPhase(effective_clock, cap.max_phase_ticks, chip.t1h_window) &&
112 canHitPhase(effective_clock, cap.max_phase_ticks, chip.t1l_window)) {
113 return true;
114 }
115 }
116 return false;
117}
118
119// True iff the set bits in `b` form one contiguous run (no gaps).
120// Empty bitset and single-bit bitsets are trivially contiguous.
122 fl::u32 lo = 0u;
123 fl::u32 hi = 0u;
124 bool found_lo = false;
125 for (fl::u32 i = 0; i < kPinSetCapacity; ++i) {
126 if (b.test(i)) {
127 if (!found_lo) {
128 lo = i;
129 found_lo = true;
130 }
131 hi = i;
132 }
133 }
134 if (!found_lo) {
135 return true;
136 }
137 return b.count() == (hi - lo + 1u);
138}
139
140// Driver-level protocol pre-filter — fast-out before walking groups.
143 return (p == Protocol::Clockless) ? caps.supports_clockless
144 : caps.supports_spi;
145}
146
147// Single-group check that handles both single-pin and multi-pin requests
148// uniformly (single-pin is a degenerate bitset of one bit).
150 const DriverCapabilities& caps,
151 const PinGroup& g,
152 const ChannelRequest& r) FL_NO_EXCEPT {
153 if (g.protocol != r.protocol) {
155 }
156 if (!g.data_pins.acceptsAll(r.data_pins)) {
157 return HandleResult::NoPin;
158 }
159 if (g.pins_must_be_contiguous && !isContiguousBitset(r.data_pins)) {
160 return HandleResult::NoPin;
161 }
162 if (r.protocol == Protocol::Spi) {
163 if (!g.clock_pins.accepts(r.clock_pin)) {
165 }
166 }
167 // Capacity: bulk requests consume one slot per set bit on this
168 // group (the "ganged into one group" model). For "fan out across
169 // independent groups" the resolver handles distribution at a higher
170 // layer — this free function answers "does THIS group accept the
171 // entire bitset?". Single-pin requests trivially fit (count=1
172 // <= max_concurrent for any sane group).
173 fl::u32 requested_count = r.data_pins.count();
174 if (g.max_concurrent != 0u && requested_count > static_cast<fl::u32>(g.max_concurrent)) {
176 }
177 if (r.frequency_hz.has_value()) {
179 if (!fr.isUnspecified() && !fr.contains(*r.frequency_hz)) {
181 }
182 }
183 if (r.protocol == Protocol::Clockless) {
184 if (!isClocklessTimingCompatible(caps.clockless_timing, r.timing)) {
186 }
187 }
188 return HandleResult::Yes;
189}
190
191} // namespace can_match_detail
192
193
195 const DriverCapabilities& caps,
197 const ChannelRequest& request) FL_NO_EXCEPT {
198 if (!can_match_detail::driverProtocolGate(caps, request.protocol)) {
200 }
201 if (groups.empty()) {
202 // No groups published; nothing this driver can match concretely.
203 return HandleResult::NoPin;
204 }
205 if (request.data_pins.none()) {
206 // No pins requested — nothing to match against.
207 return HandleResult::NoPin;
208 }
209 // Driver-wide aggregate cap: a bulk request with N pins can't be
210 // served by a driver that publishes max_total_channels < N, even if
211 // an individual group's max_concurrent would otherwise fit it
212 // (CodeRabbit #3197).
213 const fl::u32 requested_count = request.data_pins.count();
214 if (caps.max_total_channels != 0u &&
215 requested_count > static_cast<fl::u32>(caps.max_total_channels)) {
217 }
218
220 for (fl::size i = 0; i < groups.size(); ++i) {
221 HandleResult r = can_match_detail::checkOneGroup(caps, groups[i], request);
222 if (r == HandleResult::Yes) {
223 return r;
224 }
226 best = r;
227 }
228 }
229 return best;
230}
231
232} // namespace fl
Free function that decides whether a ChannelRequest can be served by a driver's published capability ...
Declarative driver capabilities + channel-request types for the Channel Manager's predicate / depende...
HandleResult checkOneGroup(const DriverCapabilities &caps, const PinGroup &g, const ChannelRequest &r) FL_NO_EXCEPT
bool isContiguousBitset(const PinBitset &b) FL_NO_EXCEPT
bool isClocklessTimingCompatible(const ClocklessTimingCapability &cap, const ChipsetClocklessTiming &chip) FL_NO_EXCEPT
bool driverProtocolGate(const DriverCapabilities &caps, Protocol p) FL_NO_EXCEPT
FreqRange effectiveFrequencyRange(const DriverCapabilities &caps, const PinGroup &g) FL_NO_EXCEPT
fl::u8 strength(HandleResult r) FL_NO_EXCEPT
bool canHitPhase(fl::u32 effective_clock_hz, fl::u16 max_ticks, const NanosRange &window) FL_NO_EXCEPT
unsigned char u8
Definition stdint.h:131
fl::bitset_fixed< kPinSetCapacity > PinBitset
HandleResult canMatch(const DriverCapabilities &caps, fl::span< const PinGroup > groups, const ChannelRequest &request) FL_NO_EXCEPT
Test a request (single-pin OR multi-pin ganged) against a driver's published capability data.
HandleResult
@ NoPinPair
clock pin not in the matching group's allowlist
@ NoProtocol
driver doesn't support the requested Protocol
@ NoPin
data pin not in any matching group's allowlist
@ NoCapacity
group full / driver-total cap reached
@ NoTiming
chipset tolerance windows can't be hit
@ NoFrequency
requested freq outside driver/group range
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
InputGamut g
Definition rgbw.h:124
@ Spi
Data + clock (APA102, SK9822, ...)
@ Clockless
Single data line + async timing (WS2812, SK6812, ...)
static constexpr fl::u32 kPinSetCapacity
Maximum pin number representable in a PinSet's bitset.
Base definition for an LED controller.
Definition crgb.hpp:179
constexpr bool isUnspecified() const FL_NO_EXCEPT
constexpr bool contains(fl::u32 hz) const FL_NO_EXCEPT