16f5e598ddfd4a3e7e9fea2f7d4ca62f10ab9983
[yosys.git] / passes / pmgen / xilinx_dsp48a.pmg
1 // This file describes the main pattern matcher setup (of three total) that
2 // forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for
3 // DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6).
4 // At a high level, it works as follows:
5 // ( 1) Starting from a DSP48A/DSP48A1 cell
6 // ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
7 // (attached to at most two $mux cells that implement clock-enable or
8 // reset functionality, using a subpattern discussed below)
9 // If B1REG matched, treat 'B' input as input of B1REG
10 // ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
11 // (pre-adder)
12 // ( 4) Match 'B' input for B0REG
13 // ( 5) Match 'A' input for A1REG
14 // If A1REG, then match 'A' input for A0REG
15 // ( 6) Match 'D' input for DREG
16 // ( 7) Match 'P' output that exclusively drives an MREG
17 // ( 8) Match 'P' output that exclusively drives one of two inputs to an $add
18 // cell (post-adder).
19 // The other input to the adder is assumed to come in from the 'C' input
20 // (note: 'P' -> 'C' connections that exist for accumulators are
21 // recognised in xilinx_dsp.cc).
22 // ( 9) Match 'P' output that exclusively drives a PREG
23 // (10) If post-adder and PREG both present, match for a $mux cell driving
24 // the 'C' input, where one of the $mux's inputs is the PREG output.
25 // This indicates an accumulator situation, and one where a $mux exists
26 // to override the accumulated value:
27 // +--------------------------------+
28 // | ____ |
29 // +--| \ |
30 // |$mux|-+ |
31 // 'C' ---|____/ | |
32 // | /-------\ +----+ |
33 // +----+ +-| post- |___|PREG|---+ 'P'
34 // |MREG|------ | adder | +----+
35 // +----+ \-------/
36 // Notes: see the notes in xilinx_dsp.pmg
37
38 pattern xilinx_dsp48a_pack
39
40 state <SigBit> clock
41 state <SigSpec> sigA sigB sigC sigD sigM sigP
42 state <IdString> postAddAB postAddMuxAB
43 state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
44 state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
45 state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
46 state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
47 state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
48
49 // Variables used for subpatterns
50 state <SigSpec> argQ argD
51 state <bool> ffcepol ffrstpol
52 state <int> ffoffset
53 udata <SigSpec> dffD dffQ
54 udata <SigBit> dffclock
55 udata <Cell*> dff dffcemux dffrstmux
56 udata <bool> dffcepol dffrstpol
57
58 // (1) Starting from a DSP48A/DSP48A1 cell
59 match dsp
60 select dsp->type.in(\DSP48A, \DSP48A1)
61 endmatch
62
63 code sigA sigB sigC sigD sigM clock
64 auto unextend = [](const SigSpec &sig) {
65 int i;
66 for (i = GetSize(sig)-1; i > 0; i--)
67 if (sig[i] != sig[i-1])
68 break;
69 // Do not remove non-const sign bit
70 if (sig[i].wire)
71 ++i;
72 return sig.extract(0, i);
73 };
74 sigA = unextend(port(dsp, \A));
75 sigB = unextend(port(dsp, \B));
76
77 sigC = port(dsp, \C, SigSpec());
78 sigD = port(dsp, \D, SigSpec());
79
80 SigSpec P = port(dsp, \P);
81 // Only care about those bits that are used
82 int i;
83 for (i = GetSize(P)-1; i >= 0; i--)
84 if (nusers(P[i]) > 1)
85 break;
86 i++;
87 log_assert(nusers(P.extract_end(i)) <= 1);
88 // This sigM could have no users if downstream sinks (e.g. $add) is
89 // narrower than $mul result, for example
90 if (i == 0)
91 reject;
92 sigM = P.extract(0, i);
93
94 clock = port(dsp, \CLK, SigBit());
95 endcode
96
97 // (2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
98 // (attached to at most two $mux cells that implement clock-enable or
99 // reset functionality, using a subpattern discussed above)
100 // If matched, treat 'B' input as input of B1REG
101 code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
102 if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
103 argQ = sigB;
104 subpattern(in_dffe);
105 if (dff) {
106 ffB1 = dff;
107 clock = dffclock;
108 if (dffrstmux) {
109 ffB1rstmux = dffrstmux;
110 ffBrstpol = dffrstpol;
111 }
112 if (dffcemux) {
113 ffB1cemux = dffcemux;
114 ffBcepol = dffcepol;
115 }
116 sigB = dffD;
117 }
118 }
119 endcode
120
121 // (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
122 // (pre-adder)
123 match preAdd
124 if sigD.empty() || sigD.is_fully_zero()
125 if param(dsp, \B0REG).as_int() == 0
126 // Ensure that preAdder not already used
127 if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()
128
129 select preAdd->type.in($add, $sub)
130 // Output has to be 18 bits or less
131 select GetSize(port(preAdd, \Y)) <= 18
132 select nusers(port(preAdd, \Y)) == 2
133 // D port has to be 18 bits or less
134 select GetSize(port(preAdd, \A)) <= 18
135 // B port has to be 18 bits or less
136 select GetSize(port(preAdd, \B)) <= 18
137 index <SigSpec> port(preAdd, \Y) === sigB
138
139 optional
140 endmatch
141
142 code sigB sigD
143 if (preAdd) {
144 sigD = port(preAdd, \A);
145 sigB = port(preAdd, \B);
146 }
147 endcode
148
149 // (4) Match 'B' input for B0REG
150 code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
151 if (param(dsp, \B0REG).as_int() == 0) {
152 argQ = sigB;
153 subpattern(in_dffe);
154 if (dff) {
155 if (ffB1) {
156 if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
157 goto ffB0_end;
158 if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
159 goto ffB0_end;
160 if (dffrstmux) {
161 if (ffBrstpol != dffrstpol)
162 goto ffB0_end;
163 if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
164 goto ffB0_end;
165 ffB0rstmux = dffrstmux;
166 }
167 if (dffcemux) {
168 if (ffBcepol != dffcepol)
169 goto ffB0_end;
170 if (port(ffB1cemux, \S) != port(dffcemux, \S))
171 goto ffB0_end;
172 ffB0cemux = dffcemux;
173 }
174 }
175 ffB0 = dff;
176 clock = dffclock;
177 if (dffrstmux) {
178 ffB0rstmux = dffrstmux;
179 ffBrstpol = dffrstpol;
180 }
181 if (dffcemux) {
182 ffB0cemux = dffcemux;
183 ffBcepol = dffcepol;
184 }
185 sigB = dffD;
186 }
187 }
188 ffB0_end:
189 endcode
190
191 // (5) Match 'A' input for A1REG
192 // If A1REG, then match 'A' input for A0REG
193 code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
194 if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
195 argQ = sigA;
196 subpattern(in_dffe);
197 if (dff) {
198 ffA1 = dff;
199 clock = dffclock;
200 if (dffrstmux) {
201 ffA1rstmux = dffrstmux;
202 ffArstpol = dffrstpol;
203 }
204 if (dffcemux) {
205 ffA1cemux = dffcemux;
206 ffAcepol = dffcepol;
207 }
208 sigA = dffD;
209
210 // Now attempt to match A0
211 if (ffA1) {
212 argQ = sigA;
213 subpattern(in_dffe);
214 if (dff) {
215 if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
216 goto ffA0_end;
217 if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
218 goto ffA0_end;
219 if (dffrstmux) {
220 if (ffArstpol != dffrstpol)
221 goto ffA0_end;
222 if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
223 goto ffA0_end;
224 ffA0rstmux = dffrstmux;
225 }
226 if (dffcemux) {
227 if (ffAcepol != dffcepol)
228 goto ffA0_end;
229 if (port(ffA1cemux, \S) != port(dffcemux, \S))
230 goto ffA0_end;
231 ffA0cemux = dffcemux;
232 }
233
234 ffA0 = dff;
235 clock = dffclock;
236
237 if (dffcemux) {
238 ffA0cemux = dffcemux;
239 ffAcepol = dffcepol;
240 }
241 sigA = dffD;
242
243 ffA0_end: ;
244 }
245 }
246
247 }
248 }
249 endcode
250
251 // (6) Match 'D' input for DREG
252 code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
253 if (param(dsp, \DREG).as_int() == 0) {
254 argQ = sigD;
255 subpattern(in_dffe);
256 if (dff) {
257 ffD = dff;
258 clock = dffclock;
259 if (dffrstmux) {
260 ffDrstmux = dffrstmux;
261 ffDrstpol = dffrstpol;
262 }
263 if (dffcemux) {
264 ffDcemux = dffcemux;
265 ffDcepol = dffcepol;
266 }
267 sigD = dffD;
268 }
269 }
270 endcode
271
272 // (7) Match 'P' output that exclusively drives an MREG
273 code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
274 if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
275 argD = sigM;
276 subpattern(out_dffe);
277 if (dff) {
278 ffM = dff;
279 clock = dffclock;
280 if (dffrstmux) {
281 ffMrstmux = dffrstmux;
282 ffMrstpol = dffrstpol;
283 }
284 if (dffcemux) {
285 ffMcemux = dffcemux;
286 ffMcepol = dffcepol;
287 }
288 sigM = dffQ;
289 }
290 }
291 sigP = sigM;
292 endcode
293
294 // (8) Match 'P' output that exclusively drives one of two inputs to an $add
295 // cell (post-adder).
296 // The other input to the adder is assumed to come in from the 'C' input
297 // (note: 'P' -> 'C' connections that exist for accumulators are
298 // recognised in xilinx_dsp.cc).
299 match postAdd
300 // Ensure that Z mux is not already used
301 if port(dsp, \OPMODE, SigSpec()).extract(2,2).is_fully_zero()
302
303 select postAdd->type.in($add)
304 select GetSize(port(postAdd, \Y)) <= 48
305 choice <IdString> AB {\A, \B}
306 select nusers(port(postAdd, AB)) <= 3
307 filter ffMcemux || nusers(port(postAdd, AB)) == 2
308 filter !ffMcemux || nusers(port(postAdd, AB)) == 3
309
310 index <SigBit> port(postAdd, AB)[0] === sigP[0]
311 filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
312 filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
313 // Check that remainder of AB is a sign- or zero-extension
314 filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
315
316 set postAddAB AB
317 optional
318 endmatch
319
320 code sigC sigP
321 if (postAdd) {
322 sigC = port(postAdd, postAddAB == \A ? \B : \A);
323 sigP = port(postAdd, \Y);
324 }
325 endcode
326
327 // (9) Match 'P' output that exclusively drives a PREG
328 code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
329 if (param(dsp, \PREG).as_int() == 0) {
330 int users = 2;
331 // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
332 if (ffMcemux && !postAdd) users++;
333 if (nusers(sigP) == users) {
334 argD = sigP;
335 subpattern(out_dffe);
336 if (dff) {
337 ffP = dff;
338 clock = dffclock;
339 if (dffrstmux) {
340 ffPrstmux = dffrstmux;
341 ffPrstpol = dffrstpol;
342 }
343 if (dffcemux) {
344 ffPcemux = dffcemux;
345 ffPcepol = dffcepol;
346 }
347 sigP = dffQ;
348 }
349 }
350 }
351 endcode
352
353 // (10) If post-adder and PREG both present, match for a $mux cell driving
354 // the 'C' input, where one of the $mux's inputs is the PREG output.
355 // This indicates an accumulator situation, and one where a $mux exists
356 // to override the accumulated value:
357 // +--------------------------------+
358 // | ____ |
359 // +--| \ |
360 // |$mux|-+ |
361 // 'C' ---|____/ | |
362 // | /-------\ +----+ |
363 // +----+ +-| post- |___|PREG|---+ 'P'
364 // |MREG|------ | adder | +----+
365 // +----+ \-------/
366 match postAddMux
367 if postAdd
368 if ffP
369 select postAddMux->type.in($mux)
370 select nusers(port(postAddMux, \Y)) == 2
371 choice <IdString> AB {\A, \B}
372 index <SigSpec> port(postAddMux, AB) === sigP
373 index <SigSpec> port(postAddMux, \Y) === sigC
374 set postAddMuxAB AB
375 optional
376 endmatch
377
378 code sigC
379 if (postAddMux)
380 sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
381 endcode
382
383 code
384 accept;
385 endcode
386
387 // #######################
388
389 // Subpattern for matching against input registers, based on knowledge of the
390 // 'Q' input. Typically, identifying registers with clock-enable and reset
391 // capability would be a task would be handled by other Yosys passes such as
392 // dff2dffe, but since DSP inference happens much before this, these patterns
393 // have to be manually identified.
394 // At a high level:
395 // (1) Starting from a $dff cell that (partially or fully) drives the given
396 // 'Q' argument
397 // (2) Match for a $mux cell implementing synchronous reset semantics ---
398 // one that exclusively drives the 'D' input of the $dff, with one of its
399 // $mux inputs being fully zero
400 // (3) Match for a $mux cell implement clock enable semantics --- one that
401 // exclusively drives the 'D' input of the $dff (or the other input of
402 // the reset $mux) and where one of this $mux's inputs is connected to
403 // the 'Q' output of the $dff
404 subpattern in_dffe
405 arg argD argQ clock
406
407 code
408 dff = nullptr;
409 if (GetSize(argQ) == 0)
410 reject;
411 for (const auto &c : argQ.chunks()) {
412 // Abandon matches when 'Q' is a constant
413 if (!c.wire)
414 reject;
415 // Abandon matches when 'Q' has the keep attribute set
416 if (c.wire->get_bool_attribute(\keep))
417 reject;
418 // Abandon matches when 'Q' has a non-zero init attribute set
419 // (not supported by DSP48E1)
420 Const init = c.wire->attributes.at(\init, Const());
421 if (!init.empty())
422 for (auto b : init.extract(c.offset, c.width))
423 if (b != State::Sx && b != State::S0)
424 reject;
425 }
426 endcode
427
428 // (1) Starting from a $dff cell that (partially or fully) drives the given
429 // 'Q' argument
430 match ff
431 select ff->type.in($dff)
432 // DSP48E1 does not support clock inversion
433 select param(ff, \CLK_POLARITY).as_bool()
434
435 slice offset GetSize(port(ff, \D))
436 index <SigBit> port(ff, \Q)[offset] === argQ[0]
437
438 // Check that the rest of argQ is present
439 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
440 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
441
442 filter clock == SigBit() || port(ff, \CLK) == clock
443
444 set ffoffset offset
445 endmatch
446
447 code argQ argD
448 SigSpec Q = port(ff, \Q);
449 dff = ff;
450 dffclock = port(ff, \CLK);
451 dffD = argQ;
452 argD = port(ff, \D);
453 argQ = Q;
454 dffD.replace(argQ, argD);
455 // Only search for ffrstmux if dffD only
456 // has two (ff, ffrstmux) users
457 if (nusers(dffD) > 2)
458 argD = SigSpec();
459 endcode
460
461 // (2) Match for a $mux cell implementing synchronous reset semantics ---
462 // exclusively drives the 'D' input of the $dff, with one of the $mux
463 // inputs being fully zero
464 match ffrstmux
465 if !argD.empty()
466 select ffrstmux->type.in($mux)
467 index <SigSpec> port(ffrstmux, \Y) === argD
468
469 choice <IdString> BA {\B, \A}
470 // DSP48E1 only supports reset to zero
471 select port(ffrstmux, BA).is_fully_zero()
472
473 define <bool> pol (BA == \B)
474 set ffrstpol pol
475 semioptional
476 endmatch
477
478 code argD
479 if (ffrstmux) {
480 dffrstmux = ffrstmux;
481 dffrstpol = ffrstpol;
482 argD = port(ffrstmux, ffrstpol ? \A : \B);
483 dffD.replace(port(ffrstmux, \Y), argD);
484
485 // Only search for ffcemux if argQ has at
486 // least 3 users (ff, <upstream>, ffrstmux) and
487 // dffD only has two (ff, ffrstmux)
488 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
489 argD = SigSpec();
490 }
491 else
492 dffrstmux = nullptr;
493 endcode
494
495 // (3) Match for a $mux cell implement clock enable semantics --- one that
496 // exclusively drives the 'D' input of the $dff (or the other input of
497 // the reset $mux) and where one of this $mux's inputs is connected to
498 // the 'Q' output of the $dff
499 match ffcemux
500 if !argD.empty()
501 select ffcemux->type.in($mux)
502 index <SigSpec> port(ffcemux, \Y) === argD
503 choice <IdString> AB {\A, \B}
504 index <SigSpec> port(ffcemux, AB) === argQ
505 define <bool> pol (AB == \A)
506 set ffcepol pol
507 semioptional
508 endmatch
509
510 code argD
511 if (ffcemux) {
512 dffcemux = ffcemux;
513 dffcepol = ffcepol;
514 argD = port(ffcemux, ffcepol ? \B : \A);
515 dffD.replace(port(ffcemux, \Y), argD);
516 }
517 else
518 dffcemux = nullptr;
519 endcode
520
521 // #######################
522
523 // Subpattern for matching against output registers, based on knowledge of the
524 // 'D' input.
525 // At a high level:
526 // (1) Starting from an optional $mux cell that implements clock enable
527 // semantics --- one where the given 'D' argument (partially or fully)
528 // drives one of its two inputs
529 // (2) Starting from, or continuing onto, another optional $mux cell that
530 // implements synchronous reset semantics --- one where the given 'D'
531 // argument (or the clock enable $mux output) drives one of its two inputs
532 // and where the other input is fully zero
533 // (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
534 // output of the previous clock enable or reset $mux cells)
535 subpattern out_dffe
536 arg argD argQ clock
537
538 code
539 dff = nullptr;
540 for (auto c : argD.chunks())
541 // Abandon matches when 'D' has the keep attribute set
542 if (c.wire->get_bool_attribute(\keep))
543 reject;
544 endcode
545
546 // (1) Starting from an optional $mux cell that implements clock enable
547 // semantics --- one where the given 'D' argument (partially or fully)
548 // drives one of its two inputs
549 match ffcemux
550 select ffcemux->type.in($mux)
551 // ffcemux output must have two users: ffcemux and ff.D
552 select nusers(port(ffcemux, \Y)) == 2
553
554 choice <IdString> AB {\A, \B}
555 // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
556 select nusers(port(ffcemux, AB)) >= 3
557
558 slice offset GetSize(port(ffcemux, \Y))
559 define <IdString> BA (AB == \A ? \B : \A)
560 index <SigBit> port(ffcemux, BA)[offset] === argD[0]
561
562 // Check that the rest of argD is present
563 filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
564 filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
565
566 set ffoffset offset
567 define <bool> pol (AB == \A)
568 set ffcepol pol
569
570 semioptional
571 endmatch
572
573 code argD argQ
574 dffcemux = ffcemux;
575 if (ffcemux) {
576 SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
577 SigSpec Y = port(ffcemux, \Y);
578 argQ = argD;
579 argD.replace(BA, Y);
580 argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
581
582 dffcemux = ffcemux;
583 dffcepol = ffcepol;
584 }
585 endcode
586
587 // (2) Starting from, or continuing onto, another optional $mux cell that
588 // implements synchronous reset semantics --- one where the given 'D'
589 // argument (or the clock enable $mux output) drives one of its two inputs
590 // and where the other input is fully zero
591 match ffrstmux
592 select ffrstmux->type.in($mux)
593 // ffrstmux output must have two users: ffrstmux and ff.D
594 select nusers(port(ffrstmux, \Y)) == 2
595
596 choice <IdString> BA {\B, \A}
597 // DSP48E1 only supports reset to zero
598 select port(ffrstmux, BA).is_fully_zero()
599
600 slice offset GetSize(port(ffrstmux, \Y))
601 define <IdString> AB (BA == \B ? \A : \B)
602 index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
603
604 // Check that offset is consistent
605 filter !ffcemux || ffoffset == offset
606 // Check that the rest of argD is present
607 filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
608 filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
609
610 set ffoffset offset
611 define <bool> pol (AB == \A)
612 set ffrstpol pol
613
614 semioptional
615 endmatch
616
617 code argD argQ
618 dffrstmux = ffrstmux;
619 if (ffrstmux) {
620 SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
621 SigSpec Y = port(ffrstmux, \Y);
622 argD.replace(AB, Y);
623
624 dffrstmux = ffrstmux;
625 dffrstpol = ffrstpol;
626 }
627 endcode
628
629 // (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
630 // output of the previous clock enable or reset $mux cells)
631 match ff
632 select ff->type.in($dff)
633 // DSP48E1 does not support clock inversion
634 select param(ff, \CLK_POLARITY).as_bool()
635
636 slice offset GetSize(port(ff, \D))
637 index <SigBit> port(ff, \D)[offset] === argD[0]
638
639 // Check that offset is consistent
640 filter (!ffcemux && !ffrstmux) || ffoffset == offset
641 // Check that the rest of argD is present
642 filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
643 filter port(ff, \D).extract(offset, GetSize(argD)) == argD
644 // Check that FF.Q is connected to CE-mux
645 filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
646
647 filter clock == SigBit() || port(ff, \CLK) == clock
648
649 set ffoffset offset
650 endmatch
651
652 code argQ
653 SigSpec D = port(ff, \D);
654 SigSpec Q = port(ff, \Q);
655 if (!ffcemux) {
656 argQ = argD;
657 argQ.replace(D, Q);
658 }
659
660 // Abandon matches when 'Q' has a non-zero init attribute set
661 // (not supported by DSP48E1)
662 for (auto c : argQ.chunks()) {
663 Const init = c.wire->attributes.at(\init, Const());
664 if (!init.empty())
665 for (auto b : init.extract(c.offset, c.width))
666 if (b != State::Sx && b != State::S0)
667 reject;
668 }
669
670 dff = ff;
671 dffQ = argQ;
672 dffclock = port(ff, \CLK);
673 endcode