Merge remote-tracking branch 'origin/master' into eddie/abc9_refactor
[yosys.git] / passes / techmap / abc9_ops.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 * 2019 Eddie Hung <eddie@fpgeh.com>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 */
20
21 #include "kernel/register.h"
22 #include "kernel/sigtools.h"
23 #include "kernel/utils.h"
24
25 USING_YOSYS_NAMESPACE
26 PRIVATE_NAMESPACE_BEGIN
27
28 void break_scc(RTLIL::Module *module)
29 {
30 // For every unique SCC found, (arbitrarily) find the first
31 // cell in the component, and convert all wires driven by
32 // its output ports into a new PO, and drive its previous
33 // sinks with a new PI
34 pool<RTLIL::Const> ids_seen;
35 for (auto cell : module->selected_cells()) {
36 auto it = cell->attributes.find(ID(abc9_scc_id));
37 if (it == cell->attributes.end())
38 continue;
39 auto r = ids_seen.insert(it->second);
40 cell->attributes.erase(it);
41 if (!r.second)
42 continue;
43 for (auto &c : cell->connections_) {
44 if (c.second.is_fully_const()) continue;
45 if (cell->output(c.first)) {
46 SigBit b = c.second.as_bit();
47 Wire *w = b.wire;
48 if (w->port_input) {
49 // In this case, hopefully the loop break has been already created
50 // Get the non-prefixed wire
51 Wire *wo = module->wire(stringf("%s.abco", b.wire->name.c_str()));
52 log_assert(wo != nullptr);
53 log_assert(wo->port_output);
54 log_assert(b.offset < GetSize(wo));
55 c.second = RTLIL::SigBit(wo, b.offset);
56 }
57 else {
58 // Create a new output/input loop break
59 w->port_input = true;
60 w = module->wire(stringf("%s.abco", w->name.c_str()));
61 if (!w) {
62 w = module->addWire(stringf("%s.abco", b.wire->name.c_str()), GetSize(b.wire));
63 w->port_output = true;
64 }
65 else {
66 log_assert(w->port_input);
67 log_assert(b.offset < GetSize(w));
68 }
69 w->set_bool_attribute(ID(abc9_scc_break));
70 c.second = RTLIL::SigBit(w, b.offset);
71 }
72 }
73 }
74 }
75
76 module->fixup_ports();
77 }
78
79 void unbreak_scc(RTLIL::Module *module)
80 {
81 // Now 'unexpose' those wires by undoing
82 // the expose operation -- remove them from PO/PI
83 // and re-connecting them back together
84 for (auto wire : module->wires()) {
85 auto it = wire->attributes.find(ID(abc9_scc_break));
86 if (it != wire->attributes.end()) {
87 wire->attributes.erase(it);
88 log_assert(wire->port_output);
89 wire->port_output = false;
90 std::string name = wire->name.str();
91 RTLIL::Wire *i_wire = module->wire(name.substr(0, GetSize(name) - 5));
92 log_assert(i_wire);
93 log_assert(i_wire->port_input);
94 i_wire->port_input = false;
95 module->connect(i_wire, wire);
96 }
97 }
98 module->fixup_ports();
99 }
100
101 void prep_dff(RTLIL::Module *module)
102 {
103 auto design = module->design;
104 log_assert(design);
105
106 SigMap assign_map(module);
107
108 typedef SigSpec clkdomain_t;
109 dict<clkdomain_t, int> clk_to_mergeability;
110
111 for (auto cell : module->selected_cells()) {
112 auto inst_module = design->module(cell->type);
113 if (!inst_module || !inst_module->attributes.count("\\abc9_flop")
114 || cell->get_bool_attribute("\\abc9_keep"))
115 continue;
116
117 Wire *abc9_clock_wire = module->wire(stringf("%s.$abc9_clock", cell->name.c_str()));
118 if (abc9_clock_wire == NULL)
119 log_error("'%s$abc9_clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
120 SigSpec abc9_clock = assign_map(abc9_clock_wire);
121
122 clkdomain_t key(abc9_clock);
123
124 auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1));
125 auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second));
126 log_assert(r2.second);
127
128 Wire *abc9_init_wire = module->wire(stringf("%s.$abc9_init", cell->name.c_str()));
129 if (abc9_init_wire == NULL)
130 log_error("'%s.$abc9_init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
131 log_assert(GetSize(abc9_init_wire) == 1);
132 SigSpec abc9_init = assign_map(abc9_init_wire);
133 if (!abc9_init.is_fully_const())
134 log_error("'%s.$abc9_init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
135 r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const()));
136 log_assert(r2.second);
137 }
138
139 RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str()));
140 if (holes_module) {
141 dict<SigSig, SigSig> replace;
142 for (auto it = holes_module->cells_.begin(); it != holes_module->cells_.end(); ) {
143 auto cell = it->second;
144 if (cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
145 "$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) {
146 SigBit D = cell->getPort("\\D");
147 SigBit Q = cell->getPort("\\Q");
148 // Remove the DFF cell from what needs to be a combinatorial box
149 it = holes_module->cells_.erase(it);
150 Wire *port;
151 if (GetSize(Q.wire) == 1)
152 port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str()));
153 else
154 port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset));
155 log_assert(port);
156 // Prepare to replace "assign <port> = DFF.Q;" with "assign <port> = DFF.D;"
157 // in order to extract the combinatorial control logic that feeds the box
158 // (i.e. clock enable, synchronous reset, etc.)
159 replace.insert(std::make_pair(SigSig(port,Q), SigSig(port,D)));
160 // Since `flatten` above would have created wires named "<cell>.Q",
161 // extract the pre-techmap cell name
162 auto pos = Q.wire->name.str().rfind(".");
163 log_assert(pos != std::string::npos);
164 IdString driver = Q.wire->name.substr(0, pos);
165 // And drive the signal that was previously driven by "DFF.Q" (typically
166 // used to implement clock-enable functionality) with the "<cell>.$abc9_currQ"
167 // wire (which itself is driven an input port) we inserted above
168 Wire *currQ = holes_module->wire(stringf("%s.$abc9_currQ", driver.c_str()));
169 log_assert(currQ);
170 holes_module->connect(Q, currQ);
171 }
172 else
173 ++it;
174 }
175
176 for (auto &conn : holes_module->connections_) {
177 auto it = replace.find(conn);
178 if (it != replace.end())
179 conn = it->second;
180 }
181 }
182 }
183
184 void prep_holes(RTLIL::Module *module)
185 {
186 auto design = module->design;
187 log_assert(design);
188
189 SigMap sigmap(module);
190
191 // TODO: Speed up toposort -- ultimately we care about
192 // box ordering, but not individual AIG cells
193 dict<SigBit, pool<IdString>> bit_drivers, bit_users;
194 TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
195 bool abc9_box_seen = false;
196
197 for (auto cell : module->selected_cells()) {
198 if (cell->type == "$_NOT_")
199 {
200 SigBit A = sigmap(cell->getPort("\\A").as_bit());
201 SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
202 toposort.node(cell->name);
203 bit_users[A].insert(cell->name);
204 bit_drivers[Y].insert(cell->name);
205 continue;
206 }
207
208 if (cell->type == "$_AND_")
209 {
210 SigBit A = sigmap(cell->getPort("\\A").as_bit());
211 SigBit B = sigmap(cell->getPort("\\B").as_bit());
212 SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
213 toposort.node(cell->name);
214 bit_users[A].insert(cell->name);
215 bit_users[B].insert(cell->name);
216 bit_drivers[Y].insert(cell->name);
217 continue;
218 }
219
220 if (cell->type == "$__ABC9_FF_")
221 continue;
222
223 RTLIL::Module* inst_module = design->module(cell->type);
224 if (inst_module) {
225 if (!inst_module->attributes.count("\\abc9_box_id") || cell->get_bool_attribute("\\abc9_keep"))
226 continue;
227
228 for (const auto &conn : cell->connections()) {
229 auto port_wire = inst_module->wire(conn.first);
230 // Ignore inout for the sake of topographical ordering
231 if (port_wire->port_input && !port_wire->port_output)
232 for (auto bit : sigmap(conn.second))
233 bit_users[bit].insert(cell->name);
234 if (port_wire->port_output)
235 for (auto bit : sigmap(conn.second))
236 bit_drivers[bit].insert(cell->name);
237 }
238
239 abc9_box_seen = true;
240
241 toposort.node(cell->name);
242 }
243 }
244
245 if (!abc9_box_seen)
246 return;
247
248 for (auto &it : bit_users)
249 if (bit_drivers.count(it.first))
250 for (auto driver_cell : bit_drivers.at(it.first))
251 for (auto user_cell : it.second)
252 toposort.edge(driver_cell, user_cell);
253
254 #if 0
255 toposort.analyze_loops = true;
256 #endif
257 bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
258 #if 0
259 unsigned i = 0;
260 for (auto &it : toposort.loops) {
261 log(" loop %d\n", i++);
262 for (auto cell_name : it) {
263 auto cell = module->cell(cell_name);
264 log_assert(cell);
265 log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
266 }
267 }
268 #endif
269 log_assert(no_loops);
270
271 vector<Cell*> box_list;
272 for (auto cell_name : toposort.sorted) {
273 RTLIL::Cell *cell = module->cell(cell_name);
274 log_assert(cell);
275
276 RTLIL::Module* box_module = design->module(cell->type);
277 if (!box_module || !box_module->attributes.count("\\abc9_box_id")
278 || cell->get_bool_attribute("\\abc9_keep"))
279 continue;
280
281 bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */);
282
283 // Fully pad all unused input connections of this box cell with S0
284 // Fully pad all undriven output connections of this box cell with anonymous wires
285 // NB: Assume box_module->ports are sorted alphabetically
286 // (as RTLIL::Module::fixup_ports() would do)
287 for (const auto &port_name : box_module->ports) {
288 RTLIL::Wire* w = box_module->wire(port_name);
289 log_assert(w);
290 auto it = cell->connections_.find(port_name);
291 if (w->port_input) {
292 RTLIL::SigSpec rhs;
293 if (it != cell->connections_.end()) {
294 if (GetSize(it->second) < GetSize(w))
295 it->second.append(RTLIL::SigSpec(State::S0, GetSize(w)-GetSize(it->second)));
296 rhs = it->second;
297 }
298 else {
299 rhs = RTLIL::SigSpec(State::S0, GetSize(w));
300 cell->setPort(port_name, rhs);
301 }
302 }
303 if (w->port_output) {
304 RTLIL::SigSpec rhs;
305 auto it = cell->connections_.find(w->name);
306 if (it != cell->connections_.end()) {
307 if (GetSize(it->second) < GetSize(w))
308 it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second)));
309 rhs = it->second;
310 }
311 else {
312 Wire *wire = module->addWire(NEW_ID, GetSize(w));
313 if (blackbox)
314 wire->set_bool_attribute(ID(abc9_padding));
315 rhs = wire;
316 cell->setPort(port_name, rhs);
317 }
318 }
319 }
320
321 box_list.emplace_back(cell);
322 }
323 log_assert(!box_list.empty());
324
325 RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str()));
326 log_assert(holes_module);
327 holes_module->set_bool_attribute("\\abc9_holes");
328
329 dict<IdString, Cell*> cell_cache;
330
331 int port_id = 1;
332 for (auto cell : box_list) {
333 RTLIL::Module* orig_box_module = design->module(cell->type);
334 log_assert(orig_box_module);
335 IdString derived_name = orig_box_module->derive(design, cell->parameters);
336 RTLIL::Module* box_module = design->module(derived_name);
337 if (box_module->has_processes())
338 Pass::call_on_module(design, box_module, "proc");
339
340 int box_inputs = 0;
341 auto r = cell_cache.insert(std::make_pair(derived_name, nullptr));
342 Cell *holes_cell = r.first->second;
343 if (r.second && box_module->get_bool_attribute("\\whitebox")) {
344 holes_cell = holes_module->addCell(cell->name, cell->type);
345 holes_cell->parameters = cell->parameters;
346 r.first->second = holes_cell;
347
348 // Since Module::derive() will create a new module, there
349 // is a chance that the ports will be alphabetically ordered
350 // again, which is a problem when carry-chains are involved.
351 // Inherit the port ordering from the original module here...
352 // (and set the port_id below, when iterating through those)
353 log_assert(GetSize(box_module->ports) == GetSize(orig_box_module->ports));
354 box_module->ports = orig_box_module->ports;
355 }
356
357 // NB: Assume box_module->ports are sorted alphabetically
358 // (as RTLIL::Module::fixup_ports() would do)
359 int box_port_id = 1;
360 for (const auto &port_name : box_module->ports) {
361 RTLIL::Wire *w = box_module->wire(port_name);
362 log_assert(w);
363 if (r.second)
364 w->port_id = box_port_id++;
365 RTLIL::Wire *holes_wire;
366 RTLIL::SigSpec port_sig;
367 if (w->port_input)
368 for (int i = 0; i < GetSize(w); i++) {
369 box_inputs++;
370 holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
371 if (!holes_wire) {
372 holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
373 holes_wire->port_input = true;
374 holes_wire->port_id = port_id++;
375 holes_module->ports.push_back(holes_wire->name);
376 }
377 if (holes_cell)
378 port_sig.append(holes_wire);
379 }
380 if (w->port_output)
381 for (int i = 0; i < GetSize(w); i++) {
382 if (GetSize(w) == 1)
383 holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(w->name)));
384 else
385 holes_wire = holes_module->addWire(stringf("$abc%s.%s[%d]", cell->name.c_str(), log_id(w->name), i));
386 holes_wire->port_output = true;
387 holes_wire->port_id = port_id++;
388 holes_module->ports.push_back(holes_wire->name);
389 if (holes_cell)
390 port_sig.append(holes_wire);
391 else
392 holes_module->connect(holes_wire, State::S0);
393 }
394 if (!port_sig.empty()) {
395 if (r.second)
396 holes_cell->setPort(w->name, port_sig);
397 else
398 holes_module->connect(holes_cell->getPort(w->name), port_sig);
399 }
400 }
401
402 // For flops only, create an extra 1-bit input that drives a new wire
403 // called "<cell>.$abc9_currQ" that is used below
404 if (box_module->get_bool_attribute("\\abc9_flop")) {
405 log_assert(holes_cell);
406
407 box_inputs++;
408 Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
409 if (!holes_wire) {
410 holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
411 holes_wire->port_input = true;
412 holes_wire->port_id = port_id++;
413 holes_module->ports.push_back(holes_wire->name);
414 }
415 Wire *w = holes_module->addWire(stringf("%s.$abc9_currQ", cell->name.c_str()));
416 holes_module->connect(w, holes_wire);
417 }
418 }
419 }
420
421 struct Abc9OpsPass : public Pass {
422 Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
423 void help() YS_OVERRIDE
424 {
425 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
426 log("\n");
427 log(" abc9_ops [options] [selection]\n");
428 log("\n");
429 }
430 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
431 {
432 log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n");
433 log_push();
434
435 bool break_scc_mode = false;
436 bool unbreak_scc_mode = false;
437 bool prep_dff_mode = false;
438 bool prep_holes_mode = false;
439
440 size_t argidx;
441 for (argidx = 1; argidx < args.size(); argidx++) {
442 std::string arg = args[argidx];
443 if (arg == "-break_scc") {
444 break_scc_mode = true;
445 continue;
446 }
447 if (arg == "-unbreak_scc") {
448 unbreak_scc_mode = true;
449 continue;
450 }
451 if (arg == "-prep_dff") {
452 prep_dff_mode = true;
453 continue;
454 }
455 if (arg == "-prep_holes") {
456 prep_holes_mode = true;
457 continue;
458 }
459 break;
460 }
461 extra_args(args, argidx, design);
462
463 for (auto mod : design->selected_modules()) {
464 if (mod->get_blackbox_attribute())
465 continue;
466 if (mod->get_bool_attribute("\\abc9_holes"))
467 continue;
468
469 if (mod->processes.size() > 0) {
470 log("Skipping module %s as it contains processes.\n", log_id(mod));
471 continue;
472 }
473
474 if (break_scc_mode)
475 break_scc(mod);
476 if (unbreak_scc_mode)
477 unbreak_scc(mod);
478 if (prep_dff_mode)
479 prep_dff(mod);
480 if (prep_holes_mode)
481 prep_holes(mod);
482 }
483 }
484 } Abc9OpsPass;
485
486 PRIVATE_NAMESPACE_END