abc9_ops: still emit delay table even box has no timing
[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 #include "kernel/celltypes.h"
25 #include "kernel/timinginfo.h"
26
27 USING_YOSYS_NAMESPACE
28 PRIVATE_NAMESPACE_BEGIN
29
30 int map_autoidx;
31
32 inline std::string remap_name(RTLIL::IdString abc9_name)
33 {
34 return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
35 }
36
37 void check(RTLIL::Design *design)
38 {
39 dict<IdString,IdString> box_lookup;
40 for (auto m : design->modules()) {
41 if (m->name.begins_with("$paramod"))
42 continue;
43
44 auto flop = m->get_bool_attribute(ID(abc9_flop));
45 auto it = m->attributes.find(ID(abc9_box_id));
46 if (!flop) {
47 if (it == m->attributes.end())
48 continue;
49 auto id = it->second.as_int();
50 auto r = box_lookup.insert(std::make_pair(stringf("$__boxid%d", id), m->name));
51 if (!r.second)
52 log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n",
53 log_id(m), id, log_id(r.first->second));
54 }
55
56 // Make carry in the last PI, and carry out the last PO
57 // since ABC requires it this way
58 IdString carry_in, carry_out;
59 for (const auto &port_name : m->ports) {
60 auto w = m->wire(port_name);
61 log_assert(w);
62 if (w->get_bool_attribute("\\abc9_carry")) {
63 if (w->port_input) {
64 if (carry_in != IdString())
65 log_error("Module '%s' contains more than one (* abc9_carry *) input port.\n", log_id(m));
66 carry_in = port_name;
67 }
68 if (w->port_output) {
69 if (carry_out != IdString())
70 log_error("Module '%s' contains more than one (* abc9_carry *) output port.\n", log_id(m));
71 carry_out = port_name;
72 }
73 }
74 }
75
76 if (carry_in != IdString() && carry_out == IdString())
77 log_error("Module '%s' contains an (* abc9_carry *) input port but no output port.\n", log_id(m));
78 if (carry_in == IdString() && carry_out != IdString())
79 log_error("Module '%s' contains an (* abc9_carry *) output port but no input port.\n", log_id(m));
80
81 if (flop) {
82 int num_outputs = 0;
83 for (auto port_name : m->ports) {
84 auto wire = m->wire(port_name);
85 if (wire->port_output) num_outputs++;
86 }
87 if (num_outputs != 1)
88 log_error("Module '%s' with (* abc9_flop *) has %d outputs (expect 1).\n", log_id(m), num_outputs);
89 }
90 }
91 }
92
93 void mark_scc(RTLIL::Module *module)
94 {
95 // For every unique SCC found, (arbitrarily) find the first
96 // cell in the component, and convert all wires driven by
97 // its output ports into a new PO, and drive its previous
98 // sinks with a new PI
99 pool<RTLIL::Const> ids_seen;
100 for (auto cell : module->cells()) {
101 auto it = cell->attributes.find(ID(abc9_scc_id));
102 if (it == cell->attributes.end())
103 continue;
104 auto id = it->second;
105 auto r = ids_seen.insert(id);
106 cell->attributes.erase(it);
107 if (!r.second)
108 continue;
109 for (auto &c : cell->connections_) {
110 if (c.second.is_fully_const()) continue;
111 if (cell->output(c.first)) {
112 SigBit b = c.second.as_bit();
113 Wire *w = b.wire;
114 w->set_bool_attribute(ID::keep);
115 w->attributes[ID(abc9_scc_id)] = id.as_int();
116 }
117 }
118 }
119
120 module->fixup_ports();
121 }
122
123 void prep_dff(RTLIL::Module *module)
124 {
125 auto design = module->design;
126 log_assert(design);
127
128 SigMap assign_map(module);
129
130 typedef SigSpec clkdomain_t;
131 dict<clkdomain_t, int> clk_to_mergeability;
132
133 for (auto cell : module->cells()) {
134 if (cell->type != "$__ABC9_FF_")
135 continue;
136
137 Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str()));
138 if (abc9_clock_wire == NULL)
139 log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
140 SigSpec abc9_clock = assign_map(abc9_clock_wire);
141
142 clkdomain_t key(abc9_clock);
143
144 auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1));
145 auto r2 = cell->attributes.insert(ID(abc9_mergeability));;
146 log_assert(r2.second);
147 r2.first->second = r.first->second;
148 }
149
150 RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str()));
151 if (holes_module) {
152 SigMap sigmap(holes_module);
153
154 dict<SigSpec, SigSpec> replace;
155 for (auto cell : holes_module->cells().to_vector()) {
156 if (!cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
157 "$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_"))
158 continue;
159 SigBit D = cell->getPort("\\D");
160 SigBit Q = cell->getPort("\\Q");
161 // Emulate async control embedded inside $_DFF_* cell with mux in front of D
162 if (cell->type.in("$_DFF_NN0_", "$_DFF_PN0_"))
163 D = holes_module->MuxGate(NEW_ID, State::S0, D, cell->getPort("\\R"));
164 else if (cell->type.in("$_DFF_NN1_", "$_DFF_PN1_"))
165 D = holes_module->MuxGate(NEW_ID, State::S1, D, cell->getPort("\\R"));
166 else if (cell->type.in("$_DFF_NP0_", "$_DFF_PP0_"))
167 D = holes_module->MuxGate(NEW_ID, D, State::S0, cell->getPort("\\R"));
168 else if (cell->type.in("$_DFF_NP1_", "$_DFF_PP1_"))
169 D = holes_module->MuxGate(NEW_ID, D, State::S1, cell->getPort("\\R"));
170 // Remove the $_DFF_* cell from what needs to be a combinatorial box
171 holes_module->remove(cell);
172 Wire *port;
173 if (GetSize(Q.wire) == 1)
174 port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str()));
175 else
176 port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset));
177 log_assert(port);
178 // Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;"
179 // in order to extract just the combinatorial control logic that feeds the box
180 // (i.e. clock enable, synchronous reset, etc.)
181 replace.insert(std::make_pair(Q,D));
182 // Since `flatten` above would have created wires named "<cell>.Q",
183 // extract the pre-techmap cell name
184 auto pos = Q.wire->name.str().rfind(".");
185 log_assert(pos != std::string::npos);
186 IdString driver = Q.wire->name.substr(0, pos);
187 // And drive the signal that was previously driven by "DFF.Q" (typically
188 // used to implement clock-enable functionality) with the "<cell>.$abc9_currQ"
189 // wire (which itself is driven an by input port) we inserted above
190 Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str()));
191 log_assert(currQ);
192 holes_module->connect(Q, currQ);
193 }
194
195 for (auto &conn : holes_module->connections_)
196 conn.second = replace.at(sigmap(conn.second), conn.second);
197 }
198 }
199
200 void prep_xaiger(RTLIL::Module *module, bool dff)
201 {
202 auto design = module->design;
203 log_assert(design);
204
205 SigMap sigmap(module);
206
207 dict<SigBit, pool<IdString>> bit_drivers, bit_users;
208 TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
209 dict<IdString, std::vector<IdString>> box_ports;
210
211 for (auto cell : module->cells()) {
212 if (cell->type == "$__ABC9_FF_")
213 continue;
214 if (cell->has_keep_attr())
215 continue;
216
217 auto inst_module = module->design->module(cell->type);
218 bool abc9_flop = inst_module && inst_module->get_bool_attribute("\\abc9_flop");
219 if (abc9_flop && !dff)
220 continue;
221
222 if ((inst_module && inst_module->get_bool_attribute("\\abc9_box")) || abc9_flop) {
223 auto r = box_ports.insert(cell->type);
224 if (r.second) {
225 // Make carry in the last PI, and carry out the last PO
226 // since ABC requires it this way
227 IdString carry_in, carry_out;
228 for (const auto &port_name : inst_module->ports) {
229 auto w = inst_module->wire(port_name);
230 log_assert(w);
231 if (w->get_bool_attribute("\\abc9_carry")) {
232 log_assert(w->port_input != w->port_output);
233 if (w->port_input)
234 carry_in = port_name;
235 else if (w->port_output)
236 carry_out = port_name;
237 }
238 else
239 r.first->second.push_back(port_name);
240 }
241 if (carry_in != IdString()) {
242 r.first->second.push_back(carry_in);
243 r.first->second.push_back(carry_out);
244 }
245 }
246 }
247 else if (!yosys_celltypes.cell_known(cell->type))
248 continue;
249
250 // TODO: Speed up toposort -- we care about box ordering only
251 for (auto conn : cell->connections()) {
252 if (cell->input(conn.first))
253 for (auto bit : sigmap(conn.second))
254 bit_users[bit].insert(cell->name);
255
256 if (cell->output(conn.first) && !abc9_flop)
257 for (auto bit : sigmap(conn.second))
258 bit_drivers[bit].insert(cell->name);
259 }
260 toposort.node(cell->name);
261 }
262
263 if (box_ports.empty())
264 return;
265
266 for (auto &it : bit_users)
267 if (bit_drivers.count(it.first))
268 for (auto driver_cell : bit_drivers.at(it.first))
269 for (auto user_cell : it.second)
270 toposort.edge(driver_cell, user_cell);
271
272 if (ys_debug(1))
273 toposort.analyze_loops = true;
274
275 bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
276
277 if (ys_debug(1)) {
278 unsigned i = 0;
279 for (auto &it : toposort.loops) {
280 log(" loop %d\n", i++);
281 for (auto cell_name : it) {
282 auto cell = module->cell(cell_name);
283 log_assert(cell);
284 log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
285 }
286 }
287 }
288
289 log_assert(no_loops);
290
291 RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str()));
292 log_assert(holes_module);
293 holes_module->set_bool_attribute("\\abc9_holes");
294
295 dict<IdString, Cell*> cell_cache;
296 TimingInfo timing;
297
298 int port_id = 1, box_count = 0;
299 for (auto cell_name : toposort.sorted) {
300 RTLIL::Cell *cell = module->cell(cell_name);
301 log_assert(cell);
302
303 RTLIL::Module* box_module = design->module(cell->type);
304 if (!box_module || (!box_module->get_bool_attribute("\\abc9_box") && !box_module->get_bool_attribute("\\abc9_flop")))
305 continue;
306
307 cell->attributes["\\abc9_box_seq"] = box_count++;
308
309 IdString derived_type = box_module->derive(design, cell->parameters);
310 box_module = design->module(derived_type);
311
312 auto r = cell_cache.insert(derived_type);
313 auto &holes_cell = r.first->second;
314 if (r.second) {
315 if (box_module->has_processes())
316 Pass::call_on_module(design, box_module, "proc");
317
318 if (box_module->get_bool_attribute("\\whitebox")) {
319 holes_cell = holes_module->addCell(cell->name, derived_type);
320
321 if (box_module->has_processes())
322 Pass::call_on_module(design, box_module, "proc");
323
324 int box_inputs = 0;
325 for (auto port_name : box_ports.at(cell->type)) {
326 RTLIL::Wire *w = box_module->wire(port_name);
327 log_assert(w);
328 log_assert(!w->port_input || !w->port_output);
329 auto &conn = holes_cell->connections_[port_name];
330 if (w->port_input) {
331 for (int i = 0; i < GetSize(w); i++) {
332 box_inputs++;
333 RTLIL::Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
334 if (!holes_wire) {
335 holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
336 holes_wire->port_input = true;
337 holes_wire->port_id = port_id++;
338 holes_module->ports.push_back(holes_wire->name);
339 }
340 conn.append(holes_wire);
341 }
342 }
343 else if (w->port_output)
344 conn = holes_module->addWire(stringf("%s.%s", derived_type.c_str(), log_id(port_name)), GetSize(w));
345 }
346
347 // For flops only, create an extra 1-bit input that drives a new wire
348 // called "<cell>.abc9_ff.Q" that is used below
349 if (box_module->get_bool_attribute("\\abc9_flop")) {
350 box_inputs++;
351 Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
352 if (!holes_wire) {
353 holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
354 holes_wire->port_input = true;
355 holes_wire->port_id = port_id++;
356 holes_module->ports.push_back(holes_wire->name);
357 }
358 Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
359 holes_module->connect(Q, holes_wire);
360 }
361 }
362 else // box_module is a blackbox
363 log_assert(holes_cell == nullptr);
364 }
365
366 for (auto port_name : box_ports.at(cell->type)) {
367 RTLIL::Wire *w = box_module->wire(port_name);
368 log_assert(w);
369 if (!w->port_output)
370 continue;
371 Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w));
372 holes_wire->port_output = true;
373 holes_wire->port_id = port_id++;
374 holes_module->ports.push_back(holes_wire->name);
375 if (holes_cell) // whitebox
376 holes_module->connect(holes_wire, holes_cell->getPort(port_name));
377 else // blackbox
378 holes_module->connect(holes_wire, Const(State::S0, GetSize(w)));
379 }
380 }
381 }
382
383 void prep_delays(RTLIL::Design *design, bool dff_mode)
384 {
385 TimingInfo timing;
386
387 // Derive all Yosys blackbox modules that are not combinatorial abc9 boxes
388 // (e.g. DSPs, RAMs, etc.) nor abc9 flops and collect all such instantiations
389 pool<Module*> flops;
390 std::vector<Cell*> cells;
391 for (auto module : design->selected_modules()) {
392 if (module->processes.size() > 0) {
393 log("Skipping module %s as it contains processes.\n", log_id(module));
394 continue;
395 }
396
397 for (auto cell : module->cells()) {
398 if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_), ID($__ABC9_DELAY)))
399 continue;
400
401 RTLIL::Module* inst_module = module->design->module(cell->type);
402 if (!inst_module)
403 continue;
404 if (!inst_module->get_blackbox_attribute())
405 continue;
406 if (inst_module->attributes.count(ID(abc9_box)))
407 continue;
408 IdString derived_type = inst_module->derive(design, cell->parameters);
409 inst_module = design->module(derived_type);
410 log_assert(inst_module);
411
412 if (dff_mode && inst_module->get_bool_attribute(ID(abc9_flop))) {
413 flops.insert(inst_module);
414 continue; // do not add $__ABC9_DELAY boxes to flops
415 // as delays will be captured in the flop box
416 }
417
418 if (!timing.count(derived_type))
419 timing.setup_module(inst_module);
420
421 cells.emplace_back(cell);
422 }
423 }
424
425 // Insert $__ABC9_DELAY cells on all cells that instantiate blackboxes
426 // with required times
427 for (auto cell : cells) {
428 auto module = cell->module;
429 RTLIL::Module* inst_module = module->design->module(cell->type);
430 log_assert(inst_module);
431 IdString derived_type = inst_module->derive(design, cell->parameters);
432 inst_module = design->module(derived_type);
433 log_assert(inst_module);
434
435 auto &t = timing.at(derived_type).required;
436 for (auto &conn : cell->connections_) {
437 auto port_wire = inst_module->wire(conn.first);
438 if (!port_wire->port_input)
439 continue;
440
441 SigSpec O = module->addWire(NEW_ID, GetSize(conn.second));
442 for (int i = 0; i < GetSize(conn.second); i++) {
443 auto d = t.at(SigBit(port_wire,i), 0);
444 if (d == 0)
445 continue;
446
447 #ifndef NDEBUG
448 if (ys_debug(1)) {
449 static std::set<std::tuple<IdString,IdString,int>> seen;
450 if (seen.emplace(derived_type, conn.first, i).second) log("%s.%s[%d] abc9_required = %d\n",
451 log_id(cell->type), log_id(conn.first), i, d);
452 }
453 #endif
454 auto box = module->addCell(NEW_ID, ID($__ABC9_DELAY));
455 box->setPort(ID(I), conn.second[i]);
456 box->setPort(ID(O), O[i]);
457 box->setParam(ID(DELAY), d);
458 conn.second[i] = O[i];
459 }
460 }
461 }
462 }
463
464 void prep_lut(RTLIL::Design *design, int maxlut)
465 {
466 TimingInfo timing;
467
468 std::vector<std::tuple<int, IdString, int, std::vector<int>>> table;
469 for (auto module : design->modules()) {
470 auto it = module->attributes.find(ID(abc9_lut));
471 if (it == module->attributes.end())
472 continue;
473
474 auto &t = timing.setup_module(module);
475
476 SigBit o;
477 std::vector<int> specify;
478 for (const auto &i : t.comb) {
479 auto &d = i.first.second;
480 if (o == SigBit())
481 o = d;
482 else if (o != d)
483 log_error("(* abc9_lut *) module '%s' with has more than one output.\n", log_id(module));
484 specify.push_back(i.second);
485 }
486
487 if (maxlut && GetSize(specify) > maxlut)
488 continue;
489 // ABC requires non-decreasing LUT input delays
490 std::sort(specify.begin(), specify.end());
491 table.emplace_back(GetSize(specify), module->name, it->second.as_int(), std::move(specify));
492 }
493 // ABC requires ascending size
494 std::sort(table.begin(), table.end());
495
496 std::stringstream ss;
497 const auto &first = table.front();
498 // If the first entry does not start from a 1-input LUT,
499 // (as ABC requires) crop the first entry to do so
500 for (int i = 1; i < std::get<0>(first); i++) {
501 ss << "# $__ABC9_LUT" << i << std::endl;
502 ss << i << " " << std::get<2>(first);
503 for (int j = 0; j < i; j++)
504 ss << " " << std::get<3>(first)[j];
505 ss << std::endl;
506 }
507 for (const auto &i : table) {
508 ss << "# " << log_id(std::get<1>(i)) << std::endl;
509 ss << std::get<0>(i) << " " << std::get<2>(i);
510 for (const auto &j : std::get<3>(i))
511 ss << " " << j;
512 ss << std::endl;
513 }
514 design->scratchpad_set_string("abc9_ops.lut_library", ss.str());
515 }
516
517 void write_lut(RTLIL::Module *module, const std::string &dst) {
518 std::ofstream ofs(dst);
519 log_assert(ofs.is_open());
520 ofs << module->design->scratchpad_get_string("abc9_ops.lut_library");
521 ofs.close();
522 }
523
524 void prep_box(RTLIL::Design *design, bool dff_mode)
525 {
526 TimingInfo timing;
527
528 std::stringstream ss;
529 int abc9_box_id = 1;
530 for (auto module : design->modules()) {
531 auto it = module->attributes.find(ID(abc9_box_id));
532 if (it == module->attributes.end())
533 continue;
534 abc9_box_id = std::max(abc9_box_id, it->second.as_int());
535 }
536
537 dict<IdString,std::vector<IdString>> box_ports;
538 for (auto module : design->modules()) {
539 auto abc9_flop = module->get_bool_attribute(ID(abc9_flop));
540 if (abc9_flop) {
541 auto r = module->attributes.insert(ID(abc9_box_id));
542 if (!r.second)
543 continue;
544 r.first->second = abc9_box_id++;
545
546 if (dff_mode) {
547 int num_inputs = 0, num_outputs = 0;
548 for (auto port_name : module->ports) {
549 auto wire = module->wire(port_name);
550 log_assert(GetSize(wire) == 1);
551 if (wire->port_input) num_inputs++;
552 if (wire->port_output) num_outputs++;
553 }
554 log_assert(num_outputs == 1);
555
556 ss << log_id(module) << " " << r.first->second.as_int();
557 ss << " " << (module->get_bool_attribute(ID::whitebox) ? "1" : "0");
558 ss << " " << num_inputs+1 << " " << num_outputs << std::endl;
559
560 ss << "#";
561 bool first = true;
562 for (auto port_name : module->ports) {
563 auto wire = module->wire(port_name);
564 if (!wire->port_input)
565 continue;
566 if (first)
567 first = false;
568 else
569 ss << " ";
570 ss << log_id(wire);
571 }
572 ss << " abc9_ff.Q" << std::endl;
573
574 auto &t = timing.setup_module(module).required;
575 first = true;
576 for (auto port_name : module->ports) {
577 auto wire = module->wire(port_name);
578 if (!wire->port_input)
579 continue;
580 if (first)
581 first = false;
582 else
583 ss << " ";
584 auto it = t.find(wire);
585 if (it == t.end())
586 // Assume that no setup time means zero
587 ss << 0;
588 else {
589 ss << it->second;
590
591 #ifndef NDEBUG
592 if (ys_debug(1)) {
593 static std::set<std::pair<IdString,IdString>> seen;
594 if (seen.emplace(module->name, port_name).second) log("%s.%s abc9_required = %d\n", log_id(module),
595 log_id(port_name), it->second);
596 }
597 #endif
598 }
599
600 }
601 // Last input is 'abc9_ff.Q'
602 ss << " 0" << std::endl << std::endl;
603 continue;
604 }
605 }
606 else {
607 if (!module->attributes.erase(ID(abc9_box)))
608 continue;
609
610 auto r = module->attributes.insert(ID(abc9_box_id));
611 if (!r.second)
612 continue;
613 r.first->second = abc9_box_id++;
614 }
615
616 auto r = box_ports.insert(module->name);
617 if (r.second) {
618 // Make carry in the last PI, and carry out the last PO
619 // since ABC requires it this way
620 IdString carry_in, carry_out;
621 for (const auto &port_name : module->ports) {
622 auto w = module->wire(port_name);
623 log_assert(w);
624 if (w->get_bool_attribute("\\abc9_carry")) {
625 log_assert(w->port_input != w->port_output);
626 if (w->port_input)
627 carry_in = port_name;
628 else if (w->port_output)
629 carry_out = port_name;
630 }
631 else
632 r.first->second.push_back(port_name);
633 }
634
635 if (carry_in != IdString()) {
636 r.first->second.push_back(carry_in);
637 r.first->second.push_back(carry_out);
638 }
639 }
640
641 std::vector<SigBit> inputs;
642 std::vector<SigBit> outputs;
643 for (auto port_name : r.first->second) {
644 auto wire = module->wire(port_name);
645 if (wire->port_input)
646 for (int i = 0; i < GetSize(wire); i++)
647 inputs.emplace_back(wire, i);
648 if (wire->port_output)
649 for (int i = 0; i < GetSize(wire); i++)
650 outputs.emplace_back(wire, i);
651 }
652
653 ss << log_id(module) << " " << module->attributes.at(ID(abc9_box_id)).as_int();
654 ss << " " << (module->get_bool_attribute(ID::whitebox) ? "1" : "0");
655 ss << " " << GetSize(inputs) << " " << GetSize(outputs) << std::endl;
656
657 bool first = true;
658 ss << "#";
659 for (const auto &i : inputs) {
660 if (first)
661 first = false;
662 else
663 ss << " ";
664 if (GetSize(i.wire) == 1)
665 ss << log_id(i.wire);
666 else
667 ss << log_id(i.wire) << "[" << i.offset << "]";
668 }
669 ss << std::endl;
670
671 auto &t = timing.setup_module(module).comb;
672 if (t.empty())
673 log_warning("(* abc9_box *) module '%s' has no timing (and thus no connectivity) information.\n", log_id(module));
674
675 for (const auto &o : outputs) {
676 first = true;
677 for (const auto &i : inputs) {
678 if (first)
679 first = false;
680 else
681 ss << " ";
682 auto jt = t.find(std::make_pair(i,o));
683 if (jt == t.end())
684 ss << "-";
685 else
686 ss << jt->second;
687 }
688 ss << " # ";
689 if (GetSize(o.wire) == 1)
690 ss << log_id(o.wire);
691 else
692 ss << log_id(o.wire) << "[" << o.offset << "]";
693 ss << std::endl;
694
695 }
696 ss << std::endl;
697 }
698
699 // ABC expects at least one box
700 if (ss.tellp() == 0)
701 ss << "(dummy) 1 0 0 0";
702
703 design->scratchpad_set_string("abc9_ops.box_library", ss.str());
704 }
705
706 void write_box(RTLIL::Module *module, const std::string &dst) {
707 std::ofstream ofs(dst);
708 log_assert(ofs.is_open());
709 ofs << module->design->scratchpad_get_string("abc9_ops.box_library");
710 ofs.close();
711 }
712
713 void reintegrate(RTLIL::Module *module)
714 {
715 auto design = module->design;
716 log_assert(design);
717
718 map_autoidx = autoidx++;
719
720 RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str()));
721 if (mapped_mod == NULL)
722 log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module));
723
724 for (auto w : mapped_mod->wires())
725 module->addWire(remap_name(w->name), GetSize(w));
726
727 dict<IdString,std::vector<IdString>> box_ports;
728
729 for (auto m : design->modules()) {
730 if (!m->attributes.count(ID(abc9_box_id)))
731 continue;
732
733 auto r = box_ports.insert(m->name);
734 if (!r.second)
735 continue;
736
737 // Make carry in the last PI, and carry out the last PO
738 // since ABC requires it this way
739 IdString carry_in, carry_out;
740 for (const auto &port_name : m->ports) {
741 auto w = m->wire(port_name);
742 log_assert(w);
743 if (w->get_bool_attribute("\\abc9_carry")) {
744 log_assert(w->port_input != w->port_output);
745 if (w->port_input)
746 carry_in = port_name;
747 else if (w->port_output)
748 carry_out = port_name;
749 }
750 else
751 r.first->second.push_back(port_name);
752 }
753
754 if (carry_in != IdString()) {
755 r.first->second.push_back(carry_in);
756 r.first->second.push_back(carry_out);
757 }
758 }
759
760 std::vector<Cell*> boxes;
761 for (auto cell : module->cells().to_vector()) {
762 if (cell->has_keep_attr())
763 continue;
764 if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_)))
765 module->remove(cell);
766 else if (cell->attributes.erase("\\abc9_box_seq"))
767 boxes.emplace_back(cell);
768 }
769
770 dict<SigBit, pool<IdString>> bit_drivers, bit_users;
771 TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
772 dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers;
773 dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks;
774
775 std::map<IdString, int> cell_stats;
776 for (auto mapped_cell : mapped_mod->cells())
777 {
778 // TODO: Speed up toposort -- we care about NOT ordering only
779 toposort.node(mapped_cell->name);
780
781 if (mapped_cell->type == ID($_NOT_)) {
782 RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
783 RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
784 bit_users[a_bit].insert(mapped_cell->name);
785 // Ignore inouts for topo ordering
786 if (y_bit.wire && !(y_bit.wire->port_input && y_bit.wire->port_output))
787 bit_drivers[y_bit].insert(mapped_cell->name);
788
789 if (!a_bit.wire) {
790 mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
791 RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name));
792 log_assert(wire);
793 module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
794 }
795 else {
796 RTLIL::Cell* driver_lut = nullptr;
797 // ABC can return NOT gates that drive POs
798 if (!a_bit.wire->port_input) {
799 // If it's not a NOT gate that that comes from a PI directly,
800 // find the driver LUT and clone that to guarantee that we won't
801 // increase the max logic depth
802 // (TODO: Optimise by not cloning unless will increase depth)
803 RTLIL::IdString driver_name;
804 if (GetSize(a_bit.wire) == 1)
805 driver_name = stringf("$lut%s", a_bit.wire->name.c_str());
806 else
807 driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset);
808 driver_lut = mapped_mod->cell(driver_name);
809 }
810
811 if (!driver_lut) {
812 // If a driver couldn't be found (could be from PI or box CI)
813 // then implement using a LUT
814 RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())),
815 RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset),
816 RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset),
817 RTLIL::Const::from_string("01"));
818 bit2sinks[cell->getPort(ID::A)].push_back(cell);
819 cell_stats[ID($lut)]++;
820 }
821 else
822 not2drivers[mapped_cell] = driver_lut;
823 }
824 continue;
825 }
826
827 if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) {
828 RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
829 cell->parameters = mapped_cell->parameters;
830 cell->attributes = mapped_cell->attributes;
831
832 for (auto &mapped_conn : mapped_cell->connections()) {
833 RTLIL::SigSpec newsig;
834 for (auto c : mapped_conn.second.chunks()) {
835 if (c.width == 0)
836 continue;
837 //log_assert(c.width == 1);
838 if (c.wire)
839 c.wire = module->wires_.at(remap_name(c.wire->name));
840 newsig.append(c);
841 }
842 cell->setPort(mapped_conn.first, newsig);
843
844 if (cell->input(mapped_conn.first)) {
845 for (auto i : newsig)
846 bit2sinks[i].push_back(cell);
847 for (auto i : mapped_conn.second)
848 bit_users[i].insert(mapped_cell->name);
849 }
850 if (cell->output(mapped_conn.first))
851 for (auto i : mapped_conn.second)
852 // Ignore inouts for topo ordering
853 if (i.wire && !(i.wire->port_input && i.wire->port_output))
854 bit_drivers[i].insert(mapped_cell->name);
855 }
856 }
857 else {
858 RTLIL::Cell *existing_cell = module->cell(mapped_cell->name);
859 if (!existing_cell)
860 log_error("Cannot find existing box cell with name '%s' in original design.\n", log_id(mapped_cell));
861
862 if (existing_cell->type == ID($__ABC9_DELAY)) {
863 SigBit I = mapped_cell->getPort(ID(i));
864 SigBit O = mapped_cell->getPort(ID(o));
865 if (I.wire)
866 I.wire = module->wires_.at(remap_name(I.wire->name));
867 log_assert(O.wire);
868 O.wire = module->wires_.at(remap_name(O.wire->name));
869 module->connect(O, I);
870 continue;
871 }
872
873 RTLIL::Module* box_module = design->module(existing_cell->type);
874 IdString derived_type = box_module->derive(design, existing_cell->parameters);
875 RTLIL::Module* derived_module = design->module(derived_type);
876 log_assert(derived_module);
877 log_assert(mapped_cell->type == stringf("$__boxid%d", derived_module->attributes.at("\\abc9_box_id").as_int()));
878 mapped_cell->type = existing_cell->type;
879
880 RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
881 cell->parameters = existing_cell->parameters;
882 cell->attributes = existing_cell->attributes;
883 module->swap_names(cell, existing_cell);
884
885 auto jt = mapped_cell->connections_.find("\\i");
886 log_assert(jt != mapped_cell->connections_.end());
887 SigSpec inputs = std::move(jt->second);
888 mapped_cell->connections_.erase(jt);
889 jt = mapped_cell->connections_.find("\\o");
890 log_assert(jt != mapped_cell->connections_.end());
891 SigSpec outputs = std::move(jt->second);
892 mapped_cell->connections_.erase(jt);
893
894 auto abc9_flop = box_module->attributes.count("\\abc9_flop");
895 if (!abc9_flop) {
896 for (const auto &i : inputs)
897 bit_users[i].insert(mapped_cell->name);
898 for (const auto &i : outputs)
899 // Ignore inouts for topo ordering
900 if (i.wire && !(i.wire->port_input && i.wire->port_output))
901 bit_drivers[i].insert(mapped_cell->name);
902 }
903
904 int input_count = 0, output_count = 0;
905 for (const auto &port_name : box_ports.at(derived_type)) {
906 RTLIL::Wire *w = box_module->wire(port_name);
907 log_assert(w);
908
909 SigSpec sig;
910 if (w->port_input) {
911 sig = inputs.extract(input_count, GetSize(w));
912 input_count += GetSize(w);
913 }
914 if (w->port_output) {
915 sig = outputs.extract(output_count, GetSize(w));
916 output_count += GetSize(w);
917 }
918
919 SigSpec newsig;
920 for (auto c : sig.chunks()) {
921 if (c.width == 0)
922 continue;
923 //log_assert(c.width == 1);
924 if (c.wire)
925 c.wire = module->wires_.at(remap_name(c.wire->name));
926 newsig.append(c);
927 }
928 cell->setPort(port_name, newsig);
929
930 if (w->port_input && !abc9_flop)
931 for (const auto &i : newsig)
932 bit2sinks[i].push_back(cell);
933 }
934 }
935
936 cell_stats[mapped_cell->type]++;
937 }
938
939 for (auto cell : boxes)
940 module->remove(cell);
941
942 // Copy connections (and rename) from mapped_mod to module
943 for (auto conn : mapped_mod->connections()) {
944 if (!conn.first.is_fully_const()) {
945 auto chunks = conn.first.chunks();
946 for (auto &c : chunks)
947 c.wire = module->wires_.at(remap_name(c.wire->name));
948 conn.first = std::move(chunks);
949 }
950 if (!conn.second.is_fully_const()) {
951 auto chunks = conn.second.chunks();
952 for (auto &c : chunks)
953 if (c.wire)
954 c.wire = module->wires_.at(remap_name(c.wire->name));
955 conn.second = std::move(chunks);
956 }
957 module->connect(conn);
958 }
959
960 for (auto &it : cell_stats)
961 log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
962 int in_wires = 0, out_wires = 0;
963
964 // Stitch in mapped_mod's inputs/outputs into module
965 for (auto port : mapped_mod->ports) {
966 RTLIL::Wire *mapped_wire = mapped_mod->wire(port);
967 RTLIL::Wire *wire = module->wire(port);
968 log_assert(wire);
969 if (wire->attributes.erase(ID(abc9_scc_id))) {
970 auto r YS_ATTRIBUTE(unused) = wire->attributes.erase(ID::keep);
971 log_assert(r);
972 }
973 RTLIL::Wire *remap_wire = module->wire(remap_name(port));
974 RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire));
975 log_assert(GetSize(signal) >= GetSize(remap_wire));
976
977 RTLIL::SigSig conn;
978 if (mapped_wire->port_output) {
979 conn.first = signal;
980 conn.second = remap_wire;
981 out_wires++;
982 module->connect(conn);
983 }
984 else if (mapped_wire->port_input) {
985 conn.first = remap_wire;
986 conn.second = signal;
987 in_wires++;
988 module->connect(conn);
989 }
990 }
991
992 // ABC9 will return $_NOT_ gates in its mapping (since they are
993 // treated as being "free"), in particular driving primary
994 // outputs (real primary outputs, or cells treated as blackboxes)
995 // or driving box inputs.
996 // Instead of just mapping those $_NOT_ gates into 2-input $lut-s
997 // at an area and delay cost, see if it is possible to push
998 // this $_NOT_ into the driving LUT, or into all sink LUTs.
999 // When this is not possible, (i.e. this signal drives two primary
1000 // outputs, only one of which is complemented) and when the driver
1001 // is a LUT, then clone the LUT so that it can be inverted without
1002 // increasing depth/delay.
1003 for (auto &it : bit_users)
1004 if (bit_drivers.count(it.first))
1005 for (auto driver_cell : bit_drivers.at(it.first))
1006 for (auto user_cell : it.second)
1007 toposort.edge(driver_cell, user_cell);
1008 bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
1009 log_assert(no_loops);
1010
1011 for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) {
1012 RTLIL::Cell *not_cell = mapped_mod->cell(*ii);
1013 log_assert(not_cell);
1014 if (not_cell->type != ID($_NOT_))
1015 continue;
1016 auto it = not2drivers.find(not_cell);
1017 if (it == not2drivers.end())
1018 continue;
1019 RTLIL::Cell *driver_lut = it->second;
1020 RTLIL::SigBit a_bit = not_cell->getPort(ID::A);
1021 RTLIL::SigBit y_bit = not_cell->getPort(ID::Y);
1022 RTLIL::Const driver_mask;
1023
1024 a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name));
1025 y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name));
1026
1027 auto jt = bit2sinks.find(a_bit);
1028 if (jt == bit2sinks.end())
1029 goto clone_lut;
1030
1031 for (auto sink_cell : jt->second)
1032 if (sink_cell->type != ID($lut))
1033 goto clone_lut;
1034
1035 // Push downstream LUTs past inverter
1036 for (auto sink_cell : jt->second) {
1037 SigSpec A = sink_cell->getPort(ID::A);
1038 RTLIL::Const mask = sink_cell->getParam(ID(LUT));
1039 int index = 0;
1040 for (; index < GetSize(A); index++)
1041 if (A[index] == a_bit)
1042 break;
1043 log_assert(index < GetSize(A));
1044 int i = 0;
1045 while (i < GetSize(mask)) {
1046 for (int j = 0; j < (1 << index); j++)
1047 std::swap(mask[i+j], mask[i+j+(1 << index)]);
1048 i += 1 << (index+1);
1049 }
1050 A[index] = y_bit;
1051 sink_cell->setPort(ID::A, A);
1052 sink_cell->setParam(ID(LUT), mask);
1053 }
1054
1055 // Since we have rewritten all sinks (which we know
1056 // to be only LUTs) to be after the inverter, we can
1057 // go ahead and clone the LUT with the expectation
1058 // that the original driving LUT will become dangling
1059 // and get cleaned away
1060 clone_lut:
1061 driver_mask = driver_lut->getParam(ID(LUT));
1062 for (auto &b : driver_mask.bits) {
1063 if (b == RTLIL::State::S0) b = RTLIL::State::S1;
1064 else if (b == RTLIL::State::S1) b = RTLIL::State::S0;
1065 }
1066 auto cell = module->addLut(NEW_ID,
1067 driver_lut->getPort(ID::A),
1068 y_bit,
1069 driver_mask);
1070 for (auto &bit : cell->connections_.at(ID::A)) {
1071 bit.wire = module->wires_.at(remap_name(bit.wire->name));
1072 bit2sinks[bit].push_back(cell);
1073 }
1074 }
1075
1076 //log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
1077 log("ABC RESULTS: input signals: %8d\n", in_wires);
1078 log("ABC RESULTS: output signals: %8d\n", out_wires);
1079
1080 design->remove(mapped_mod);
1081 }
1082
1083 struct Abc9OpsPass : public Pass {
1084 Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
1085 void help() YS_OVERRIDE
1086 {
1087 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
1088 log("\n");
1089 log(" abc9_ops [options] [selection]\n");
1090 log("\n");
1091 log("This pass contains a set of supporting operations for use during ABC technology\n");
1092 log("mapping, and is expected to be called in conjunction with other operations from\n");
1093 log("the `abc9' script pass. Only fully-selected modules are supported.\n");
1094 log("\n");
1095 log(" -check\n");
1096 log(" check that the design is valid, e.g. (* abc9_box_id *) values are unique,\n");
1097 log(" (* abc9_carry *) is only given for one input/output port, etc.\n");
1098 log("\n");
1099 log(" -prep_delays\n");
1100 log(" insert `$__ABC9_DELAY' blackbox cells into the design to account for\n");
1101 log(" certain required times.\n");
1102 log("\n");
1103 log(" -mark_scc\n");
1104 log(" for an arbitrarily chosen cell in each unique SCC of each selected module\n");
1105 log(" (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n");
1106 log(" wires driven by this cell's outputs with a (* keep *) attribute in order\n");
1107 log(" to break the SCC. this temporary attribute will be removed on -reintegrate.\n");
1108 log("\n");
1109 log(" -prep_xaiger\n");
1110 log(" prepare the design for XAIGER output. this includes computing the\n");
1111 log(" topological ordering of ABC9 boxes, as well as preparing the\n");
1112 log(" '<module-name>$holes' module that contains the logic behaviour of ABC9\n");
1113 log(" whiteboxes.\n");
1114 log("\n");
1115 log(" -dff\n");
1116 log(" consider flop cells (those instantiating modules marked with (* abc9_flop *))\n");
1117 log(" during -prep_{delays,xaiger,box}.\n");
1118 log("\n");
1119 log(" -prep_dff\n");
1120 log(" compute the clock domain and initial value of each flop in the design.\n");
1121 log(" process the '$holes' module to support clock-enable functionality.\n");
1122 log("\n");
1123 log(" -prep_lut <maxlut>\n");
1124 log(" pre-compute the lut library by analysing all modules marked with\n");
1125 log(" (* abc9_lut=<area> *).\n");
1126 log("\n");
1127 log(" -write_lut <dst>\n");
1128 log(" write the pre-computed lut library to <dst>.\n");
1129 log("\n");
1130 log(" -prep_box\n");
1131 log(" pre-compute the box library by analysing all modules marked with\n");
1132 log(" (* abc9_box *).\n");
1133 log("\n");
1134 log(" -write_box <dst>\n");
1135 log(" write the pre-computed box library to <dst>.\n");
1136 log("\n");
1137 log(" -reintegrate\n");
1138 log(" for each selected module, re-intergrate the module '<module-name>$abc9'\n");
1139 log(" by first recovering ABC9 boxes, and then stitching in the remaining primary\n");
1140 log(" inputs and outputs.\n");
1141 log("\n");
1142 }
1143 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
1144 {
1145 log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n");
1146
1147 bool check_mode = false;
1148 bool prep_delays_mode = false;
1149 bool mark_scc_mode = false;
1150 bool prep_dff_mode = false;
1151 bool prep_xaiger_mode = false;
1152 bool prep_lut_mode = false;
1153 bool prep_box_mode = false;
1154 bool reintegrate_mode = false;
1155 bool dff_mode = false;
1156 std::string write_lut_dst;
1157 int maxlut = 0;
1158 std::string write_box_dst;
1159
1160 size_t argidx;
1161 for (argidx = 1; argidx < args.size(); argidx++) {
1162 std::string arg = args[argidx];
1163 if (arg == "-check") {
1164 check_mode = true;
1165 continue;
1166 }
1167 if (arg == "-mark_scc") {
1168 mark_scc_mode = true;
1169 continue;
1170 }
1171 if (arg == "-prep_dff") {
1172 prep_dff_mode = true;
1173 continue;
1174 }
1175 if (arg == "-prep_xaiger") {
1176 prep_xaiger_mode = true;
1177 continue;
1178 }
1179 if (arg == "-prep_delays") {
1180 prep_delays_mode = true;
1181 continue;
1182 }
1183 if (arg == "-prep_lut" && argidx+1 < args.size()) {
1184 prep_lut_mode = true;
1185 maxlut = atoi(args[++argidx].c_str());
1186 continue;
1187 }
1188 if (arg == "-maxlut" && argidx+1 < args.size()) {
1189 continue;
1190 }
1191 if (arg == "-write_lut" && argidx+1 < args.size()) {
1192 write_lut_dst = args[++argidx];
1193 rewrite_filename(write_lut_dst);
1194 continue;
1195 }
1196 if (arg == "-prep_box") {
1197 prep_box_mode = true;
1198 continue;
1199 }
1200 if (arg == "-write_box" && argidx+1 < args.size()) {
1201 write_box_dst = args[++argidx];
1202 rewrite_filename(write_box_dst);
1203 continue;
1204 }
1205 if (arg == "-reintegrate") {
1206 reintegrate_mode = true;
1207 continue;
1208 }
1209 if (arg == "-dff") {
1210 dff_mode = true;
1211 continue;
1212 }
1213 break;
1214 }
1215 extra_args(args, argidx, design);
1216
1217 if (!(check_mode || mark_scc_mode || prep_delays_mode || prep_xaiger_mode || prep_dff_mode || prep_lut_mode || prep_box_mode || !write_lut_dst.empty() || !write_box_dst.empty() || reintegrate_mode))
1218 log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff,lut,box}, -write_{lut,box}, -reintegrate must be specified.\n");
1219
1220 if (dff_mode && !prep_delays_mode && !prep_xaiger_mode && !prep_box_mode)
1221 log_cmd_error("'-dff' option is only relevant for -prep_{delay,xaiger,box}.\n");
1222
1223 if (check_mode)
1224 check(design);
1225 if (prep_delays_mode)
1226 prep_delays(design, dff_mode);
1227 if (prep_lut_mode)
1228 prep_lut(design, maxlut);
1229 if (prep_box_mode)
1230 prep_box(design, dff_mode);
1231
1232 for (auto mod : design->selected_modules()) {
1233 if (mod->get_bool_attribute("\\abc9_holes"))
1234 continue;
1235
1236 if (mod->processes.size() > 0) {
1237 log("Skipping module %s as it contains processes.\n", log_id(mod));
1238 continue;
1239 }
1240
1241 if (!design->selected_whole_module(mod))
1242 log_error("Can't handle partially selected module %s!\n", log_id(mod));
1243
1244 if (!write_lut_dst.empty())
1245 write_lut(mod, write_lut_dst);
1246 if (!write_box_dst.empty())
1247 write_box(mod, write_box_dst);
1248 if (mark_scc_mode)
1249 mark_scc(mod);
1250 if (prep_dff_mode)
1251 prep_dff(mod);
1252 if (prep_xaiger_mode)
1253 prep_xaiger(mod, dff_mode);
1254 if (reintegrate_mode)
1255 reintegrate(mod);
1256 }
1257 }
1258 } Abc9OpsPass;
1259
1260 PRIVATE_NAMESPACE_END