Refactor using subpattern in_dffe
[yosys.git] / passes / pmgen / xilinx_dsp.pmg
1 pattern xilinx_dsp
2
3 state <std::function<SigSpec(const SigSpec&)>> unextend
4 state <SigBit> clock
5 state <SigSpec> sigA sigffAmuxY sigB sigffBmuxY sigC sigffCmuxY sigD sigffDmuxY sigM sigP
6 state <IdString> postAddAB postAddMuxAB
7 state <bool> ffAenpol ffADenpol ffBenpol ffCenpol ffDenpol ffMenpol ffPenpol
8 state <int> ffPoffset
9
10 state <Cell*> ffAD ffADmux ffA ffAmux ffB ffBmux ffC ffCmux ffD ffDmux
11
12 // subpattern
13 state <SigSpec> dffQ
14 state <bool> dffenpol_
15 udata <SigSpec> dffD
16 udata <SigBit> dffclock
17 udata <Cell*> dff dffmux
18 udata <bool> dffenpol
19
20 match dsp
21 select dsp->type.in(\DSP48E1)
22 endmatch
23
24 code unextend sigA sigB sigC sigD sigM
25 unextend = [](const SigSpec &sig) {
26 int i;
27 for (i = GetSize(sig)-1; i > 0; i--)
28 if (sig[i] != sig[i-1])
29 break;
30 // Do not remove non-const sign bit
31 if (sig[i].wire)
32 ++i;
33 return sig.extract(0, i);
34 };
35 sigA = unextend(port(dsp, \A));
36 sigB = unextend(port(dsp, \B));
37
38 sigC = dsp->connections_.at(\C, SigSpec());
39 sigD = dsp->connections_.at(\D, SigSpec());
40
41 SigSpec P = port(dsp, \P);
42 // Only care about those bits that are used
43 int i;
44 for (i = 0; i < GetSize(P); i++) {
45 if (nusers(P[i]) <= 1)
46 break;
47 sigM.append(P[i]);
48 }
49 log_assert(nusers(P.extract_end(i)) <= 1);
50 //if (GetSize(sigM) <= 10)
51 // reject;
52 endcode
53
54 code dffQ ffAD ffADmux ffADenpol sigA clock
55 if (param(dsp, \ADREG).as_int() == 0) {
56 dffQ = sigA;
57 subpattern(in_dffe);
58 if (dff) {
59 ffAD = dff;
60 clock = dffclock;
61 if (dffmux) {
62 ffADmux = dffmux;
63 ffADenpol = dffenpol;
64 }
65 sigA = dffD;
66 }
67 }
68 endcode
69
70 match preAdd
71 if sigD.empty() || sigD.is_fully_zero()
72 // Ensure that preAdder not already used
73 if dsp->parameters.at(\USE_DPORT, Const("FALSE")).decode_string() == "FALSE"
74 if dsp->connections_.at(\INMODE, Const(0, 5)).is_fully_zero()
75
76 select preAdd->type.in($add)
77 // Output has to be 25 bits or less
78 select GetSize(port(preAdd, \Y)) <= 25
79 select nusers(port(preAdd, \Y)) == 2
80 choice <IdString> AB {\A, \B}
81 // A port has to be 30 bits or less
82 select GetSize(port(preAdd, AB)) <= 30
83 define <IdString> BA (AB == \A ? \B : \A)
84 // D port has to be 25 bits or less
85 select GetSize(port(preAdd, BA)) <= 25
86 index <SigSpec> port(preAdd, \Y) === sigA
87
88 optional
89 endmatch
90
91 code sigA sigD
92 if (preAdd) {
93 sigA = port(preAdd, \A);
94 sigD = port(preAdd, \B);
95 if (GetSize(sigA) < GetSize(sigD))
96 std::swap(sigA, sigD);
97 }
98 endcode
99
100 code dffQ ffA ffAmux ffAenpol sigA clock ffAD ffADmux ffADenpol
101 // Only search for ffA if there was a pre-adder
102 // (otherwise ffA would have been matched as ffAD)
103 if (preAdd) {
104 if (param(dsp, \AREG).as_int() == 0) {
105 dffQ = sigA;
106 subpattern(in_dffe);
107 if (dff) {
108 ffA = dff;
109 clock = dffclock;
110 if (dffmux) {
111 ffAmux = dffmux;
112 ffAenpol = dffenpol;
113 }
114 sigA = dffD;
115 }
116 }
117 }
118 // And if there wasn't a pre-adder,
119 // move AD register to A
120 else if (ffAD) {
121 log_assert(!ffA && !ffAmux);
122 std::swap(ffA, ffAD);
123 std::swap(ffAmux, ffADmux);
124 ffAenpol = ffADenpol;
125 }
126 endcode
127
128 code dffQ ffB ffBmux ffBenpol sigB clock
129 if (param(dsp, \BREG).as_int() == 0) {
130 dffQ = sigB;
131 subpattern(in_dffe);
132 if (dff) {
133 ffB = dff;
134 clock = dffclock;
135 if (dffmux) {
136 ffBmux = dffmux;
137 ffBenpol = dffenpol;
138 }
139 sigB = dffD;
140 }
141 }
142 endcode
143
144 code dffQ ffD ffDmux ffDenpol sigD clock
145 if (param(dsp, \DREG).as_int() == 0) {
146 dffQ = sigD;
147 subpattern(in_dffe);
148 if (dff) {
149 ffD = dff;
150 clock = dffclock;
151 if (dffmux) {
152 ffDmux = dffmux;
153 ffDenpol = dffenpol;
154 }
155 sigD = dffD;
156 }
157 }
158 endcode
159
160 match ffMmux
161 if param(dsp, \MREG).as_int() == 0
162 if nusers(sigM) == 2
163 select ffMmux->type.in($mux)
164 choice <IdString> BA {\B, \A}
165 // new-value net must have exactly two users: dsp and ffMmux
166 select nusers(port(ffMmux, BA)) == 2
167 define <IdString> AB (BA == \B ? \A : \B)
168 // keep-last-value net must have at least three users: ffMmux, ffM, downstream sink(s)
169 select nusers(port(ffMmux, AB)) >= 3
170 // ffMmux output must have two users: ffMmux and ffM.D
171 select nusers(port(ffMmux, \Y)) == 2
172 filter GetSize(unextend(port(ffMmux, BA))) <= GetSize(sigM)
173 filter unextend(port(ffMmux, BA)) == sigM.extract(0, GetSize(unextend(port(ffMmux, BA))))
174 // Remaining bits on sigM must not have any other users
175 filter nusers(sigM.extract_end(GetSize(unextend(port(ffMmux, BA))))) <= 1
176 define <bool> pol (AB == \A)
177 set ffMenpol pol
178 optional
179 endmatch
180
181 code sigM
182 if (ffMmux)
183 sigM = port(ffMmux, \Y);
184 endcode
185
186 match ffM_enable
187 if ffMmux
188 if nusers(sigM) == 2
189 select ffM_enable->type.in($dff)
190 // DSP48E1 does not support clock inversion
191 select param(ffM_enable, \CLK_POLARITY).as_bool()
192 index <SigSpec> port(ffM_enable, \D) === sigM
193 index <SigSpec> port(ffM_enable, \Q) === port(ffMmux, ffMenpol ? \A : \B)
194 endmatch
195
196 match ffM
197 if !ffM_enable
198 if param(dsp, \MREG).as_int() == 0
199 if nusers(sigM) == 2
200 select ffM->type.in($dff)
201 // DSP48E1 does not support clock inversion
202 select param(ffM, \CLK_POLARITY).as_bool()
203 index <SigSpec> port(ffM, \D) === sigM
204 optional
205 endmatch
206
207 code ffM clock sigM sigP
208 if (ffM_enable) {
209 log_assert(!ffM);
210 ffM = ffM_enable;
211 }
212 if (ffM) {
213 sigM = port(ffM, \Q);
214
215 for (auto b : sigM)
216 if (b.wire->get_bool_attribute(\keep))
217 reject;
218
219 SigBit c = port(ffM, \CLK).as_bit();
220 if (clock != SigBit() && c != clock)
221 reject;
222 clock = c;
223 }
224 // No enable mux possible without flop
225 else if (ffMmux)
226 reject;
227
228 sigP = sigM;
229 endcode
230
231 match postAdd
232 // Ensure that Z mux is not already used
233 if port(dsp, \OPMODE).extract(4,3).is_fully_zero()
234
235 select postAdd->type.in($add)
236 select GetSize(port(postAdd, \Y)) <= 48
237 select nusers(port(postAdd, \Y)) == 2
238 choice <IdString> AB {\A, \B}
239 select nusers(port(postAdd, AB)) <= 3
240 filter ffMmux || nusers(port(postAdd, AB)) == 2
241 filter !ffMmux || nusers(port(postAdd, AB)) == 3
242 filter GetSize(unextend(port(postAdd, AB))) <= GetSize(sigP)
243 filter unextend(port(postAdd, AB)) == sigP.extract(0, GetSize(unextend(port(postAdd, AB))))
244 filter nusers(sigP.extract_end(GetSize(unextend(port(postAdd, AB))))) <= 1
245 set postAddAB AB
246 optional
247 endmatch
248
249 code sigC sigP
250 if (postAdd) {
251 sigC = port(postAdd, postAddAB == \A ? \B : \A);
252
253 // TODO for DSP48E1, which will have sign extended inputs/outputs
254 //int natural_mul_width = GetSize(port(dsp, \A)) + GetSize(port(dsp, \B));
255 //int actual_mul_width = GetSize(sigP);
256 //int actual_acc_width = GetSize(sigC);
257
258 //if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
259 // reject;
260 //if ((actual_acc_width != actual_mul_width) && (param(dsp, \A_SIGNED).as_bool() != param(postAdd, \A_SIGNED).as_bool()))
261 // reject;
262
263 sigP = port(postAdd, \Y);
264 }
265 endcode
266
267 match ffPmux
268 if param(dsp, \PREG).as_int() == 0
269 // If ffMmux and no postAdd new-value net must have exactly three users: ffMmux, ffM and ffPmux
270 if !ffMmux || postAdd || nusers(sigP) == 3
271 // Otherwise new-value net must have exactly two users: dsp and ffPmux
272 if (ffMmux && !postAdd) || nusers(sigP) == 2
273
274 select ffPmux->type.in($mux)
275 // ffPmux output must have two users: ffPmux and ffP.D
276 select nusers(port(ffPmux, \Y)) == 2
277 filter GetSize(port(ffPmux, \Y)) >= GetSize(sigP)
278
279 slice offset GetSize(port(ffPmux, \Y))
280 filter offset+GetSize(sigP) <= GetSize(port(ffPmux, \Y))
281 choice <IdString> BA {\B, \A}
282 filter port(ffPmux, BA).extract(offset, GetSize(sigP)) == sigP
283
284 define <IdString> AB (BA == \B ? \A : \B)
285 // keep-last-value net must have at least three users: ffPmux, ffP, downstream sink(s)
286 filter nusers(port(ffPmux, AB)) >= 3
287 define <bool> pol (AB == \A)
288 set ffPenpol pol
289 set ffPoffset offset
290 optional
291 endmatch
292
293 code sigP
294 if (ffPmux)
295 sigP.replace(port(ffPmux, ffPenpol ? \B : \A), port(ffPmux, \Y));
296 endcode
297
298 match ffP_enable
299 if ffPmux
300 if nusers(sigP) == 2
301 select ffP_enable->type.in($dff)
302 // DSP48E1 does not support clock inversion
303 select param(ffP_enable, \CLK_POLARITY).as_bool()
304 index <SigSpec> port(ffP_enable, \D) === port(ffPmux, \Y)
305 index <SigSpec> port(ffP_enable, \Q) === port(ffPmux, ffPenpol ? \A : \B)
306 filter GetSize(port(ffP_enable, \D)) >= GetSize(sigP)
307 filter ffPoffset+GetSize(sigP) <= GetSize(port(ffP_enable, \D))
308 filter port(ffP_enable, \D).extract(ffPoffset, GetSize(sigP)) == sigP
309 endmatch
310
311 match ffP
312 if !ffP_enable
313 if param(dsp, \PREG).as_int() == 0
314 // If ffMmux and no postAdd new-value net must have exactly three users: ffMmux, ffM and ffPmux
315 if !ffMmux || postAdd || nusers(sigP) == 3
316 // Otherwise new-value net must have exactly two users: dsp and ffPmux
317 if (ffMmux && !postAdd) || nusers(sigP) == 2
318
319 select ffP->type.in($dff)
320 // DSP48E1 does not support clock inversion
321 select param(ffP, \CLK_POLARITY).as_bool()
322 filter GetSize(port(ffP, \D)) >= GetSize(sigP)
323 slice offset GetSize(port(ffP, \D))
324 filter offset+GetSize(sigP) <= GetSize(port(ffP, \D))
325 filter port(ffP, \D).extract(offset, GetSize(sigP)) == sigP
326 optional
327 endmatch
328
329 code ffP sigP clock
330 if (ffP_enable) {
331 log_assert(!ffP);
332 ffP = ffP_enable;
333 }
334 if (ffP) {
335 for (auto b : port(ffP, \Q))
336 if (b.wire->get_bool_attribute(\keep))
337 reject;
338
339 SigBit c = port(ffP, \CLK).as_bit();
340
341 if (clock != SigBit() && c != clock)
342 reject;
343
344 clock = c;
345
346 sigP.replace(port(ffP, \D), port(ffP, \Q));
347 }
348 // No enable mux possible without flop
349 else if (ffPmux)
350 reject;
351 endcode
352
353 match postAddMux
354 if postAdd
355 if ffP
356 select postAddMux->type.in($mux)
357 select nusers(port(postAddMux, \Y)) == 2
358 choice <IdString> AB {\A, \B}
359 index <SigSpec> port(postAddMux, AB) === sigP
360 index <SigSpec> port(postAddMux, \Y) === sigC
361 set postAddMuxAB AB
362 optional
363 endmatch
364
365 code sigC
366 if (postAddMux)
367 sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
368 endcode
369
370 code dffQ ffC ffCmux ffCenpol sigC clock
371 if (param(dsp, \CREG).as_int() == 0) {
372 dffQ = sigC;
373 subpattern(in_dffe);
374 if (dff) {
375 ffC = dff;
376 clock = dffclock;
377 if (dffmux) {
378 ffCmux = dffmux;
379 ffCenpol = dffenpol;
380 }
381 sigC = dffD;
382 }
383 }
384 endcode
385
386 code
387 accept;
388 endcode
389
390 subpattern in_dffe
391 arg dffQ clock dffenpol_
392
393 code
394 dff = nullptr;
395 dffmux = nullptr;
396 endcode
397
398 match ff
399 select ff->type.in($dff)
400 // DSP48E1 does not support clock inversion
401 select param(ff, \CLK_POLARITY).as_bool()
402 filter GetSize(port(ff, \Q)) >= GetSize(dffQ)
403 slice offset GetSize(port(ff, \Q))
404 filter offset+GetSize(dffQ) <= GetSize(port(ff, \Q))
405 filter port(ff, \Q).extract(offset, GetSize(dffQ)) == dffQ
406 semioptional
407 endmatch
408
409 code dffQ
410 if (ff) {
411 for (auto b : dffQ)
412 if (b.wire->get_bool_attribute(\keep))
413 reject;
414
415 if (clock != SigBit()) {
416 if (port(ff, \CLK) != clock)
417 reject;
418 }
419 else
420 dffclock = port(ff, \CLK);
421
422 dff = ff;
423 dffD = dffQ;
424 dffD.replace(port(ff, \Q), port(ff, \D));
425 // Only search for ffmux if ff.Q has at
426 // least 3 users (ff, dsp, ffmux) and
427 // its ff.D only has two (ff, ffmux)
428 if (!(nusers(dffQ) >= 3 && nusers(dffD) == 2))
429 dffQ = SigSpec();
430 }
431 else
432 dffQ = SigSpec();
433 endcode
434
435 match ffmux
436 if !dffQ.empty()
437 select ffmux->type.in($mux)
438 index <SigSpec> port(ffmux, \Y) === port(ff, \D)
439 filter GetSize(port(ffmux, \Y)) >= GetSize(dffD)
440 slice offset GetSize(port(ffmux, \Y))
441 filter offset+GetSize(dffD) <= GetSize(port(ffmux, \Y))
442 filter port(ffmux, \Y).extract(offset, GetSize(dffD)) == dffD
443 choice <IdString> AB {\A, \B}
444 filter offset+GetSize(dffQ) <= GetSize(port(ffmux, \Y))
445 filter port(ffmux, AB).extract(offset, GetSize(dffQ)) == dffQ
446 define <bool> pol (AB == \A)
447 set dffenpol_ pol
448 semioptional
449 endmatch
450
451 code
452 if (ffmux) {
453 dffmux = ffmux;
454 dffenpol = dffenpol_;
455 dffD = port(ffmux, dffenpol ? \B : \A);
456 }
457 endcode