Merge pull request #1666 from Xiretza/improve-makefile
[yosys.git] / passes / opt / opt_lut_ins.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "kernel/yosys.h"
21 #include "kernel/sigtools.h"
22
23 USING_YOSYS_NAMESPACE
24 PRIVATE_NAMESPACE_BEGIN
25
26 struct OptLutInsPass : public Pass {
27 OptLutInsPass() : Pass("opt_lut_ins", "discard unused LUT inputs") { }
28 void help() YS_OVERRIDE
29 {
30 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
31 log("\n");
32 log(" opt_lut_ins [options] [selection]\n");
33 log("\n");
34 log("This pass removes unused inputs from LUT cells (that is, inputs that can not\n");
35 log("influence the output signal given this LUT's value). While such LUTs cannot\n");
36 log("be directly emitted by ABC, they can be a result of various post-ABC\n");
37 log("transformations, such as mapping wide LUTs (not all sub-LUTs will use the\n");
38 log("full set of inputs) or optimizations such as xilinx_dffopt.\n");
39 log("\n");
40 log(" -tech <technology>\n");
41 log(" Instead of generic $lut cells, operate on LUT cells specific\n");
42 log(" to the given technology. Valid values are: xilinx, ecp5, gowin.\n");
43 log("\n");
44 }
45 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
46 {
47 log_header(design, "Executing OPT_LUT_INS pass (discard unused LUT inputs).\n");
48 string techname;
49
50 size_t argidx;
51 for (argidx = 1; argidx < args.size(); argidx++)
52 {
53 if (args[argidx] == "-tech" && argidx+1 < args.size()) {
54 techname = args[++argidx];
55 continue;
56 }
57 break;
58 }
59 extra_args(args, argidx, design);
60
61 if (techname != "" && techname != "xilinx" && techname != "ecp5" && techname != "gowin")
62 log_cmd_error("Unsupported technology: '%s'\n", techname.c_str());
63
64 for (auto module : design->selected_modules())
65 {
66 log("Optimizing LUTs in %s.\n", log_id(module));
67
68 std::vector<Cell *> remove_cells;
69 // Gather LUTs.
70 for (auto cell : module->selected_cells())
71 {
72 if (cell->get_bool_attribute(ID::keep))
73 continue;
74 Const lut;
75 std::vector<SigBit> inputs;
76 std::vector<SigBit> output;
77 bool ignore_const = false;
78 if (techname == "") {
79 if (cell->type != ID($lut))
80 continue;
81 inputs = cell->getPort(ID::A).bits();
82 output = cell->getPort(ID::Y);
83 lut = cell->getParam(ID(LUT));
84 } else if (techname == "xilinx" || techname == "gowin") {
85 if (cell->type == ID(LUT1)) {
86 inputs = {
87 cell->getPort(ID(I0)),
88 };
89 } else if (cell->type == ID(LUT2)) {
90 inputs = {
91 cell->getPort(ID(I0)),
92 cell->getPort(ID(I1)),
93 };
94 } else if (cell->type == ID(LUT3)) {
95 inputs = {
96 cell->getPort(ID(I0)),
97 cell->getPort(ID(I1)),
98 cell->getPort(ID(I2)),
99 };
100 } else if (cell->type == ID(LUT4)) {
101 inputs = {
102 cell->getPort(ID(I0)),
103 cell->getPort(ID(I1)),
104 cell->getPort(ID(I2)),
105 cell->getPort(ID(I3)),
106 };
107 } else if (cell->type == ID(LUT5)) {
108 inputs = {
109 cell->getPort(ID(I0)),
110 cell->getPort(ID(I1)),
111 cell->getPort(ID(I2)),
112 cell->getPort(ID(I3)),
113 cell->getPort(ID(I4)),
114 };
115 } else if (cell->type == ID(LUT6)) {
116 inputs = {
117 cell->getPort(ID(I0)),
118 cell->getPort(ID(I1)),
119 cell->getPort(ID(I2)),
120 cell->getPort(ID(I3)),
121 cell->getPort(ID(I4)),
122 cell->getPort(ID(I5)),
123 };
124 } else {
125 // Not a LUT.
126 continue;
127 }
128 lut = cell->getParam(ID(INIT));
129 if (techname == "xilinx")
130 output = cell->getPort(ID(O));
131 else
132 output = cell->getPort(ID(F));
133 } else if (techname == "ecp5") {
134 if (cell->type == ID(LUT4)) {
135 inputs = {
136 cell->getPort(ID::A),
137 cell->getPort(ID::B),
138 cell->getPort(ID(C)),
139 cell->getPort(ID(D)),
140 };
141 lut = cell->getParam(ID(INIT));
142 output = cell->getPort(ID(Z));
143 ignore_const = true;
144 } else {
145 // Not a LUT.
146 continue;
147 }
148 }
149 std::vector<int> swizzle;
150 std::vector<SigBit> new_inputs;
151 bool doit = false;
152 for (int i = 0; i < GetSize(inputs); i++) {
153 SigBit input = inputs[i];
154 if (!input.wire) {
155 if (input.data == State::S1)
156 swizzle.push_back(-2);
157 else
158 swizzle.push_back(-1);
159 // For ECP5, smaller LUTs are
160 // implemented as LUT4s with
161 // extra const inputs. Do not
162 // consider that to be a reason
163 // to redo a LUT.
164 if (!ignore_const)
165 doit = true;
166 } else {
167 bool redundant = true;
168 for (int j = 0; j < GetSize(lut); j++) {
169 if (lut[j] != lut[j ^ 1 << i])
170 redundant = false;
171 }
172 if (redundant) {
173 swizzle.push_back(-1);
174 doit = true;
175 } else {
176 swizzle.push_back(GetSize(new_inputs));
177 new_inputs.push_back(input);
178 }
179 }
180 }
181 if (!doit)
182 continue;
183 log(" Optimizing lut %s (%d -> %d)\n", log_id(cell), GetSize(inputs), GetSize(new_inputs));
184 if (techname == "ecp5") {
185 // Pad the LUT to 4 inputs, adding consts from the front.
186 int extra = 4 - GetSize(new_inputs);
187 log_assert(extra >= 0);
188 if (extra) {
189 for (int i = 0; i < extra; i++)
190 new_inputs.insert(new_inputs.begin(), State::S0);
191 for (auto &swz : swizzle)
192 if (swz >= 0)
193 swz += extra;
194 }
195 }
196 Const new_lut(0, 1 << GetSize(new_inputs));
197 for (int i = 0; i < GetSize(new_lut); i++) {
198 int lidx = 0;
199 for (int j = 0; j < GetSize(inputs); j++) {
200 int val;
201 if (swizzle[j] == -2) {
202 val = 1;
203 } else if (swizzle[j] == -1) {
204 val = 0;
205 } else {
206 val = (i >> swizzle[j]) & 1;
207 }
208 lidx |= val << j;
209 }
210 new_lut[i] = lut[lidx];
211 }
212 // For ecp5, do not replace with a const driver — the nextpnr
213 // packer requires a complete set of LUTs for wide LUT muxes.
214 if (new_inputs.empty() && techname != "ecp5") {
215 // const driver.
216 remove_cells.push_back(cell);
217 module->connect(output, new_lut[0]);
218 } else {
219 if (techname == "") {
220 cell->setParam(ID(LUT), new_lut);
221 cell->setParam(ID(WIDTH), GetSize(new_inputs));
222 cell->setPort(ID::A, new_inputs);
223 } else if (techname == "ecp5") {
224 log_assert(GetSize(new_inputs) == 4);
225 cell->setParam(ID(INIT), new_lut);
226 cell->setPort(ID::A, new_inputs[0]);
227 cell->setPort(ID::B, new_inputs[1]);
228 cell->setPort(ID(C), new_inputs[2]);
229 cell->setPort(ID(D), new_inputs[3]);
230 } else {
231 // xilinx, gowin
232 cell->setParam(ID(INIT), new_lut);
233 if (techname == "xilinx")
234 log_assert(GetSize(new_inputs) <= 6);
235 else
236 log_assert(GetSize(new_inputs) <= 4);
237 if (GetSize(new_inputs) == 1)
238 cell->type = ID(LUT1);
239 else if (GetSize(new_inputs) == 2)
240 cell->type = ID(LUT2);
241 else if (GetSize(new_inputs) == 3)
242 cell->type = ID(LUT3);
243 else if (GetSize(new_inputs) == 4)
244 cell->type = ID(LUT4);
245 else if (GetSize(new_inputs) == 5)
246 cell->type = ID(LUT5);
247 else if (GetSize(new_inputs) == 6)
248 cell->type = ID(LUT6);
249 else
250 log_assert(0);
251 cell->unsetPort(ID(I0));
252 cell->unsetPort(ID(I1));
253 cell->unsetPort(ID(I2));
254 cell->unsetPort(ID(I3));
255 cell->unsetPort(ID(I4));
256 cell->unsetPort(ID(I5));
257 cell->setPort(ID(I0), new_inputs[0]);
258 if (GetSize(new_inputs) >= 2)
259 cell->setPort(ID(I1), new_inputs[1]);
260 if (GetSize(new_inputs) >= 3)
261 cell->setPort(ID(I2), new_inputs[2]);
262 if (GetSize(new_inputs) >= 4)
263 cell->setPort(ID(I3), new_inputs[3]);
264 if (GetSize(new_inputs) >= 5)
265 cell->setPort(ID(I4), new_inputs[4]);
266 if (GetSize(new_inputs) >= 6)
267 cell->setPort(ID(I5), new_inputs[5]);
268 }
269 }
270 }
271 for (auto cell : remove_cells)
272 module->remove(cell);
273 }
274 }
275 } XilinxDffOptPass;
276
277 PRIVATE_NAMESPACE_END
278