abc9: generate $abc9_holes design instead of <name>$holes
[yosys.git] / passes / techmap / extract_reduce.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2017 Robert Ou <rqou@robertou.com>
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 #include <deque>
23
24 USING_YOSYS_NAMESPACE
25 PRIVATE_NAMESPACE_BEGIN
26
27 struct ExtractReducePass : public Pass
28 {
29 enum GateType {
30 And,
31 Or,
32 Xor
33 };
34
35 ExtractReducePass() : Pass("extract_reduce", "converts gate chains into $reduce_* cells") { }
36
37 void help() YS_OVERRIDE
38 {
39 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
40 log("\n");
41 log(" extract_reduce [options] [selection]\n");
42 log("\n");
43 log("converts gate chains into $reduce_* cells\n");
44 log("\n");
45 log("This command finds chains of $_AND_, $_OR_, and $_XOR_ cells and replaces them\n");
46 log("with their corresponding $reduce_* cells. Because this command only operates on\n");
47 log("these cell types, it is recommended to map the design to only these cell types\n");
48 log("using the `abc -g` command. Note that, in some cases, it may be more effective\n");
49 log("to map the design to only $_AND_ cells, run extract_reduce, map the remaining\n");
50 log("parts of the design to AND/OR/XOR cells, and run extract_reduce a second time.\n");
51 log("\n");
52 log(" -allow-off-chain\n");
53 log(" Allows matching of cells that have loads outside the chain. These cells\n");
54 log(" will be replicated and folded into the $reduce_* cell, but the original\n");
55 log(" cell will remain, driving its original loads.\n");
56 log("\n");
57 }
58
59 inline bool IsRightType(Cell* cell, GateType gt)
60 {
61 return (cell->type == ID($_AND_) && gt == GateType::And) ||
62 (cell->type == ID($_OR_) && gt == GateType::Or) ||
63 (cell->type == ID($_XOR_) && gt == GateType::Xor);
64 }
65
66 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
67 {
68 log_header(design, "Executing EXTRACT_REDUCE pass.\n");
69 log_push();
70
71 size_t argidx;
72 bool allow_off_chain = false;
73 for (argidx = 1; argidx < args.size(); argidx++)
74 {
75 if (args[argidx] == "-allow-off-chain")
76 {
77 allow_off_chain = true;
78 continue;
79 }
80 break;
81 }
82 extra_args(args, argidx, design);
83
84 for (auto module : design->selected_modules())
85 {
86 SigMap sigmap(module);
87
88 // Index all of the nets in the module
89 dict<SigBit, Cell*> sig_to_driver;
90 dict<SigBit, pool<Cell*>> sig_to_sink;
91 for (auto cell : module->selected_cells())
92 {
93 for (auto &conn : cell->connections())
94 {
95 if (cell->output(conn.first))
96 for (auto bit : sigmap(conn.second))
97 sig_to_driver[bit] = cell;
98
99 if (cell->input(conn.first))
100 {
101 for (auto bit : sigmap(conn.second))
102 {
103 if (sig_to_sink.count(bit) == 0)
104 sig_to_sink[bit] = pool<Cell*>();
105 sig_to_sink[bit].insert(cell);
106 }
107 }
108 }
109 }
110
111 // Need to check if any wires connect to module ports
112 pool<SigBit> port_sigs;
113 for (auto wire : module->selected_wires())
114 if (wire->port_input || wire->port_output)
115 for (auto bit : sigmap(wire))
116 port_sigs.insert(bit);
117
118 // Actual logic starts here
119 pool<Cell*> consumed_cells;
120 for (auto cell : module->selected_cells())
121 {
122 if (consumed_cells.count(cell))
123 continue;
124
125 GateType gt;
126
127 if (cell->type == ID($_AND_))
128 gt = GateType::And;
129 else if (cell->type == ID($_OR_))
130 gt = GateType::Or;
131 else if (cell->type == ID($_XOR_))
132 gt = GateType::Xor;
133 else
134 continue;
135
136 log("Working on cell %s...\n", cell->name.c_str());
137
138 // If looking for a single chain, follow linearly to the sink
139 pool<Cell*> sinks;
140 if(!allow_off_chain)
141 {
142 Cell* head_cell = cell;
143 Cell* x = cell;
144 while (true)
145 {
146 if(!IsRightType(x, gt))
147 break;
148
149 head_cell = x;
150
151 auto y = sigmap(x->getPort(ID::Y));
152 log_assert(y.size() == 1);
153
154 // Should only continue if there is one fanout back into a cell (not to a port)
155 if (sig_to_sink[y[0]].size() != 1)
156 break;
157
158 x = *sig_to_sink[y[0]].begin();
159 }
160
161 sinks.insert(head_cell);
162 }
163
164 //If off-chain loads are allowed, we have to do a wider traversal to see what the longest chain is
165 else
166 {
167 //BFS, following all chains until they hit a cell of a different type
168 //Pick the longest one
169 auto y = sigmap(cell->getPort(ID::Y));
170 pool<Cell*> current_loads = sig_to_sink[y];
171 pool<Cell*> next_loads;
172
173 while(!current_loads.empty())
174 {
175 //Find each sink and see what they are
176 for(auto x : current_loads)
177 {
178 //Not one of our gates? Don't follow any further
179 //(but add the originating cell to the list of sinks)
180 if(!IsRightType(x, gt))
181 {
182 sinks.insert(cell);
183 continue;
184 }
185
186 //If this signal drives a port, add it to the sinks
187 //(even though it may not be the end of a chain)
188 if(port_sigs.count(x) && !consumed_cells.count(x))
189 sinks.insert(x);
190
191 //It's a match, search everything out from it
192 auto& next = sig_to_sink[x];
193 for(auto z : next)
194 next_loads.insert(z);
195 }
196
197 //If we couldn't find any downstream loads, stop.
198 //Create a reduction for each of the max-length chains we found
199 if(next_loads.empty())
200 {
201 for(auto s : current_loads)
202 {
203 //Not one of our gates? Don't follow any further
204 if(!IsRightType(s, gt))
205 continue;
206
207 sinks.insert(s);
208 }
209 break;
210 }
211
212 //Otherwise, continue down the chain
213 current_loads = next_loads;
214 next_loads.clear();
215 }
216 }
217
218 //We have our list, go act on it
219 for(auto head_cell : sinks)
220 {
221 log(" Head cell is %s\n", head_cell->name.c_str());
222
223 //Avoid duplication if we already were covered
224 if(consumed_cells.count(head_cell))
225 continue;
226
227 pool<Cell*> cur_supercell;
228 std::deque<Cell*> bfs_queue = {head_cell};
229 while (bfs_queue.size())
230 {
231 Cell* x = bfs_queue.front();
232 bfs_queue.pop_front();
233
234 cur_supercell.insert(x);
235
236 auto a = sigmap(x->getPort(ID::A));
237 log_assert(a.size() == 1);
238
239 // Must have only one sink unless we're going off chain
240 // XXX: Check that it is indeed this node?
241 if( allow_off_chain || (sig_to_sink[a[0]].size() + port_sigs.count(a[0]) == 1) )
242 {
243 Cell* cell_a = sig_to_driver[a[0]];
244 if(cell_a && IsRightType(cell_a, gt))
245 {
246 // The cell here is the correct type, and it's definitely driving
247 // this current cell.
248 bfs_queue.push_back(cell_a);
249 }
250 }
251
252 auto b = sigmap(x->getPort(ID::B));
253 log_assert(b.size() == 1);
254
255 // Must have only one sink
256 // XXX: Check that it is indeed this node?
257 if( allow_off_chain || (sig_to_sink[b[0]].size() + port_sigs.count(b[0]) == 1) )
258 {
259 Cell* cell_b = sig_to_driver[b[0]];
260 if(cell_b && IsRightType(cell_b, gt))
261 {
262 // The cell here is the correct type, and it's definitely driving only
263 // this current cell.
264 bfs_queue.push_back(cell_b);
265 }
266 }
267 }
268
269 log(" Cells:\n");
270 for (auto x : cur_supercell)
271 log(" %s\n", x->name.c_str());
272
273 if (cur_supercell.size() > 1)
274 {
275 // Worth it to create reduce cell
276 log(" Creating $reduce_* cell!\n");
277
278 pool<SigBit> input_pool;
279 pool<SigBit> input_pool_intermed;
280 for (auto x : cur_supercell)
281 {
282 input_pool.insert(sigmap(x->getPort(ID::A))[0]);
283 input_pool.insert(sigmap(x->getPort(ID::B))[0]);
284 input_pool_intermed.insert(sigmap(x->getPort(ID::Y))[0]);
285 }
286 SigSpec input;
287 for (auto b : input_pool)
288 if (input_pool_intermed.count(b) == 0)
289 input.append(b);
290
291 SigBit output = sigmap(head_cell->getPort(ID::Y)[0]);
292
293 auto new_reduce_cell = module->addCell(NEW_ID,
294 gt == GateType::And ? ID($reduce_and) :
295 gt == GateType::Or ? ID($reduce_or) :
296 gt == GateType::Xor ? ID($reduce_xor) : "");
297 new_reduce_cell->setParam(ID::A_SIGNED, 0);
298 new_reduce_cell->setParam(ID::A_WIDTH, input.size());
299 new_reduce_cell->setParam(ID::Y_WIDTH, 1);
300 new_reduce_cell->setPort(ID::A, input);
301 new_reduce_cell->setPort(ID::Y, output);
302
303 if(allow_off_chain)
304 consumed_cells.insert(head_cell);
305 else
306 {
307 for (auto x : cur_supercell)
308 consumed_cells.insert(x);
309 }
310 }
311 }
312 }
313
314 // Remove all of the head cells, since we supplant them.
315 // Do not remove the upstream cells since some might still be in use ("clean" will get rid of unused ones)
316 for (auto cell : consumed_cells)
317 module->remove(cell);
318 }
319
320 log_pop();
321 }
322 } ExtractReducePass;
323
324 PRIVATE_NAMESPACE_END