1// This file describes the second of three pattern matcher setups that
2//   forms the `xilinx_dsp` pass described in xilinx_dsp.cc
3// At a high level, it works as follows:
4//   (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
5//       and (b) uses the 'C' port
6//   (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
7//       (attached to at most two $mux cells that implement clock-enable or
8//        reset functionality, using a subpattern discussed below)
9// Notes:
10//   - Running CREG packing after xilinx_dsp_pack is necessary since there is no
11//     guarantee that the cell ordering corresponds to the "expected" case (i.e.
12//     the order in which they appear in the source) thus the possiblity existed
13//     that a register got packed as a CREG into a downstream DSP that should
14//     have otherwise been a PREG of an upstream DSP that had not been visited
15//     yet
16//   - The reason this is separated out from the xilinx_dsp.pmg file is
17//     for efficiency --- each *.pmg file creates a class of the same basename,
18//     which when constructed, creates a custom database tailored to the
19//     pattern(s) contained within. Since the pattern in this file must be
20//     executed after the pattern contained in xilinx_dsp.pmg, it is necessary
21//     to reconstruct this database. Separating the two patterns into
22//     independent files causes two smaller, more specific, databases.
23
24pattern xilinx_dsp_packC
25
26udata <std::function<SigSpec(const SigSpec&)>> unextend
27state <SigBit> clock
28state <SigSpec> sigC sigP
29state <Cell*> ffC
30
31// Variables used for subpatterns
32state <SigSpec> argQ argD
33state <int> ffoffset
34udata <SigSpec> dffD dffQ
35udata <SigBit> dffclock
36udata <Cell*> dff
37
38// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
39//     and (b) uses the 'C' port
40match dsp
41	select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1)
42	select param(dsp, \CREG).as_int() == 0
43	select nusers(port(dsp, \C, SigSpec())) > 1
44endmatch
45
46code sigC sigP clock
47	unextend = [](const SigSpec &sig) {
48		int i;
49		for (i = GetSize(sig)-1; i > 0; i--)
50			if (sig[i] != sig[i-1])
51				break;
52		// Do not remove non-const sign bit
53		if (sig[i].wire)
54			++i;
55		return sig.extract(0, i);
56	};
57	sigC = unextend(port(dsp, \C, SigSpec()));
58
59	SigSpec P = port(dsp, \P);
60	if (!dsp->type.in(\DSP48E1) ||
61            param(dsp, \USE_MULT).decode_string() == "MULTIPLY") {
62		// Only care about those bits that are used
63		int i;
64		for (i = GetSize(P)-1; i >= 0; i--)
65			if (nusers(P[i]) > 1)
66				break;
67		i++;
68		log_assert(nusers(P.extract_end(i)) <= 1);
69		sigP = P.extract(0, i);
70	}
71	else
72		sigP = P;
73
74	clock = port(dsp, \CLK, SigBit());
75endcode
76
77// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
78//     (attached to at most two $mux cells that implement clock-enable or
79//      reset functionality, using the in_dffe subpattern)
80code argQ ffC sigC clock
81	argQ = sigC;
82	subpattern(in_dffe);
83	if (dff) {
84		ffC = dff;
85		clock = dffclock;
86		sigC = dffD;
87	}
88endcode
89
90code
91	if (ffC)
92		accept;
93endcode
94
95// #######################
96
97// Subpattern for matching against input registers, based on knowledge of the
98//   'Q' input.
99subpattern in_dffe
100arg argQ clock
101
102code
103	dff = nullptr;
104	if (argQ.empty())
105		reject;
106	for (const auto &c : argQ.chunks()) {
107		// Abandon matches when 'Q' is a constant
108		if (!c.wire)
109			reject;
110		// Abandon matches when 'Q' has the keep attribute set
111		if (c.wire->get_bool_attribute(\keep))
112			reject;
113		// Abandon matches when 'Q' has a non-zero init attribute set
114		// (not supported by DSP48E1)
115		Const init = c.wire->attributes.at(\init, Const());
116		if (!init.empty())
117			for (auto b : init.extract(c.offset, c.width))
118				if (b != State::Sx && b != State::S0)
119					reject;
120	}
121endcode
122
123match ff
124	select ff->type.in($dff, $dffe, $sdff, $sdffe)
125	// DSP48E1 does not support clock inversion
126	select param(ff, \CLK_POLARITY).as_bool()
127
128	// Check that reset value, if present, is fully 0.
129	filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
130
131	slice offset GetSize(port(ff, \D))
132	index <SigBit> port(ff, \Q)[offset] === argQ[0]
133
134	// Check that the rest of argQ is present
135	filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
136	filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
137
138	filter clock == SigBit() || port(ff, \CLK) == clock
139endmatch
140
141code argQ
142	SigSpec Q = port(ff, \Q);
143	dff = ff;
144	dffclock = port(ff, \CLK);
145	dffD = argQ;
146	SigSpec D = port(ff, \D);
147	argQ = Q;
148	dffD.replace(argQ, D);
149endcode
150