diff options
| -rw-r--r-- | backends/aiger/xaiger.cc | 555 | ||||
| -rw-r--r-- | frontends/aiger/aigerparse.cc | 219 | ||||
| -rw-r--r-- | frontends/aiger/aigerparse.h | 1 | ||||
| -rw-r--r-- | passes/techmap/Makefile.inc | 2 | ||||
| -rw-r--r-- | passes/techmap/abc9.cc | 1008 | ||||
| -rw-r--r-- | passes/techmap/abc9_exe.cc | 531 | ||||
| -rw-r--r-- | passes/techmap/abc9_ops.cc | 825 | ||||
| -rw-r--r-- | techlibs/xilinx/abc9_map.v | 16 | ||||
| -rw-r--r-- | tests/simple_abc9/abc9.v | 16 | ||||
| -rw-r--r-- | tests/techmap/abc9.ys | 29 | ||||
| -rw-r--r-- | tests/various/abc9.ys | 15 | 
11 files changed, 1820 insertions, 1397 deletions
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index a6c87159d..b72dd6890 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -93,7 +93,6 @@ struct XAigerWriter  	dict<SigBit, int> ordered_outputs;  	vector<Cell*> box_list; -	dict<IdString, std::vector<IdString>> box_ports;  	int mkgate(int a0, int a1)  	{ @@ -157,7 +156,6 @@ struct XAigerWriter  			if (wire->get_bool_attribute(ID::keep))  				sigmap.add(wire); -  		for (auto wire : module->wires())  			for (int i = 0; i < GetSize(wire); i++)  			{ @@ -175,108 +173,96 @@ struct XAigerWriter  				undriven_bits.insert(bit);  				unused_bits.insert(bit); -				if (wire->port_input) +				bool keep = wire->get_bool_attribute(ID::keep); +				if (wire->port_input || keep)  					input_bits.insert(bit); -				if (wire->port_output) { +				if (wire->port_output || keep) {  					if (bit != wirebit)  						alias_map[wirebit] = bit;  					output_bits.insert(wirebit);  				}  			} -		// TODO: Speed up toposort -- ultimately we care about -		//       box ordering, but not individual AIG cells -		dict<SigBit, pool<IdString>> bit_drivers, bit_users; -		TopoSort<IdString, RTLIL::sort_by_id_str> toposort; -		bool abc9_box_seen = false; - -		for (auto cell : module->selected_cells()) { -			if (cell->type == "$_NOT_") -			{ -				SigBit A = sigmap(cell->getPort("\\A").as_bit()); -				SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); -				unused_bits.erase(A); -				undriven_bits.erase(Y); -				not_map[Y] = A; -				toposort.node(cell->name); -				bit_users[A].insert(cell->name); -				bit_drivers[Y].insert(cell->name); -				continue; -			} - -			if (cell->type == "$_AND_") -			{ -				SigBit A = sigmap(cell->getPort("\\A").as_bit()); -				SigBit B = sigmap(cell->getPort("\\B").as_bit()); -				SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); -				unused_bits.erase(A); -				unused_bits.erase(B); -				undriven_bits.erase(Y); -				and_map[Y] = make_pair(A, B); -				toposort.node(cell->name); -				bit_users[A].insert(cell->name); -				bit_users[B].insert(cell->name); -				bit_drivers[Y].insert(cell->name); -				continue; -			} +		dict<IdString,dict<IdString,int>> arrival_cache; +		for (auto cell : module->cells()) { +			RTLIL::Module* inst_module = module->design->module(cell->type); +			if (!cell->has_keep_attr()) { +				if (cell->type == "$_NOT_") +				{ +					SigBit A = sigmap(cell->getPort("\\A").as_bit()); +					SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); +					unused_bits.erase(A); +					undriven_bits.erase(Y); +					not_map[Y] = A; +					continue; +				} -			if (cell->type == "$__ABC9_FF_" && -                                        // The presence of an abc9_mergeability attribute indicates -                                        //   that we do want to pass this flop to ABC -                                        cell->attributes.count("\\abc9_mergeability")) -			{ -				SigBit D = sigmap(cell->getPort("\\D").as_bit()); -				SigBit Q = sigmap(cell->getPort("\\Q").as_bit()); -				unused_bits.erase(D); -				undriven_bits.erase(Q); -				alias_map[Q] = D; -				auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell)); -				log_assert(r.second); -				continue; -			} +				if (cell->type == "$_AND_") +				{ +					SigBit A = sigmap(cell->getPort("\\A").as_bit()); +					SigBit B = sigmap(cell->getPort("\\B").as_bit()); +					SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); +					unused_bits.erase(A); +					unused_bits.erase(B); +					undriven_bits.erase(Y); +					and_map[Y] = make_pair(A, B); +					continue; +				} -			RTLIL::Module* inst_module = module->design->module(cell->type); -			if (inst_module) { -				bool abc9_box = inst_module->attributes.count("\\abc9_box_id"); -				bool abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); -				if (abc9_box && cell->get_bool_attribute("\\abc9_keep")) -					abc9_box = false; - -				for (const auto &conn : cell->connections()) { -					auto port_wire = inst_module->wire(conn.first); - -					if (abc9_box) { -						// Ignore inout for the sake of topographical ordering -						if (port_wire->port_input && !port_wire->port_output) -							for (auto bit : sigmap(conn.second)) -								bit_users[bit].insert(cell->name); -						if (port_wire->port_output) -							for (auto bit : sigmap(conn.second)) -								bit_drivers[bit].insert(cell->name); +				if (cell->type == "$__ABC9_FF_" && +						// The presence of an abc9_mergeability attribute indicates +						//   that we do want to pass this flop to ABC +						cell->attributes.count("\\abc9_mergeability")) +				{ +					SigBit D = sigmap(cell->getPort("\\D").as_bit()); +					SigBit Q = sigmap(cell->getPort("\\Q").as_bit()); +					unused_bits.erase(D); +					undriven_bits.erase(Q); +					alias_map[Q] = D; +					auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell)); +					log_assert(r.second); +					if (input_bits.erase(Q)) +						log_assert(Q.wire->attributes.count(ID::keep)); +					continue; +				} +				if (inst_module) { +					bool abc9_flop = false; +					auto it = cell->attributes.find("\\abc9_box_seq"); +					if (it != cell->attributes.end()) { +						int abc9_box_seq = it->second.as_int(); +						if (GetSize(box_list) <= abc9_box_seq) +							box_list.resize(abc9_box_seq+1); +						box_list[abc9_box_seq] = cell; +						// Only flop boxes may have arrival times +						abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");  						if (!abc9_flop)  							continue;  					} -					if (port_wire->port_output) { -						int arrival = 0; -						auto it = port_wire->attributes.find("\\abc9_arrival"); -						if (it != port_wire->attributes.end()) { -							if (it->second.flags != 0) -								log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); -							arrival = it->second.as_int(); +					auto &cell_arrivals = arrival_cache[cell->type]; +					for (const auto &conn : cell->connections()) { +						auto r = cell_arrivals.insert(conn.first); +						auto &arrival = r.first->second; +						if (r.second) { +							auto port_wire = inst_module->wire(conn.first); +							if (port_wire->port_output) { +								auto it = port_wire->attributes.find("\\abc9_arrival"); +								if (it != port_wire->attributes.end()) { +									if (it->second.flags != 0) +										log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); +									arrival = it->second.as_int(); +								} +							}  						}  						if (arrival)  							for (auto bit : sigmap(conn.second))  								arrival_times[bit] = arrival;  					} -				} -				if (abc9_box) { -					abc9_box_seen = true; -					toposort.node(cell->name); -					continue; +					if (abc9_flop) +						continue;  				}  			} @@ -293,6 +279,9 @@ struct XAigerWriter  					for (auto b : c.second) {  						Wire *w = b.wire;  						if (!w) continue; +						// Do not add as PO if bit is already a PI +						if (input_bits.count(b)) +							continue;  						if (!w->port_output || !cell_known) {  							SigBit I = sigmap(b);  							if (I != b) @@ -305,138 +294,54 @@ struct XAigerWriter  			//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));  		} -		if (abc9_box_seen) { -			for (auto &it : bit_users) -				if (bit_drivers.count(it.first)) -					for (auto driver_cell : bit_drivers.at(it.first)) -					for (auto user_cell : it.second) -						toposort.edge(driver_cell, user_cell); - -#if 0 -			toposort.analyze_loops = true; -#endif -			bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); -#if 0 -			unsigned i = 0; -			for (auto &it : toposort.loops) { -				log("  loop %d\n", i++); -				for (auto cell_name : it) { -					auto cell = module->cell(cell_name); -					log_assert(cell); -					log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); -				} -			} -#endif -			log_assert(no_loops); - -			for (auto cell_name : toposort.sorted) { -				RTLIL::Cell *cell = module->cell(cell_name); -				log_assert(cell); - -				RTLIL::Module* box_module = module->design->module(cell->type); -				if (!box_module || !box_module->attributes.count("\\abc9_box_id")) -					continue; +		dict<IdString, std::vector<IdString>> box_ports; +		for (auto cell : box_list) { +			log_assert(cell); -				bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */); +			RTLIL::Module* box_module = module->design->module(cell->type); +			log_assert(box_module); +			log_assert(box_module->attributes.count("\\abc9_box_id")); -				auto r = box_ports.insert(cell->type); -				if (r.second) { -					// Make carry in the last PI, and carry out the last PO -					//   since ABC requires it this way -					IdString carry_in, carry_out; -					for (const auto &port_name : box_module->ports) { -						auto w = box_module->wire(port_name); -						log_assert(w); -						if (w->get_bool_attribute("\\abc9_carry")) { -							if (w->port_input) { -								if (carry_in != IdString()) -									log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module)); -								carry_in = port_name; -							} -							if (w->port_output) { -								if (carry_out != IdString()) -									log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module)); -								carry_out = port_name; -							} -						} -						else -							r.first->second.push_back(port_name); -					} - -					if (carry_in != IdString() && carry_out == IdString()) -						log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module)); -					if (carry_in == IdString() && carry_out != IdString()) -						log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module)); -					if (carry_in != IdString()) { -						r.first->second.push_back(carry_in); -						r.first->second.push_back(carry_out); -					} -				} - -				// Fully pad all unused input connections of this box cell with S0 -				// Fully pad all undriven output connections of this box cell with anonymous wires -				for (auto port_name : r.first->second) { +			auto r = box_ports.insert(cell->type); +			if (r.second) { +				// Make carry in the last PI, and carry out the last PO +				//   since ABC requires it this way +				IdString carry_in, carry_out; +				for (const auto &port_name : box_module->ports) {  					auto w = box_module->wire(port_name);  					log_assert(w); -					auto it = cell->connections_.find(port_name); -					if (w->port_input) { -						RTLIL::SigSpec rhs; -						if (it != cell->connections_.end()) { -							if (GetSize(it->second) < GetSize(w)) -								it->second.append(RTLIL::SigSpec(State::S0, GetSize(w)-GetSize(it->second))); -							rhs = it->second; -						} -						else { -							rhs = RTLIL::SigSpec(State::S0, GetSize(w)); -							cell->setPort(port_name, rhs); +					if (w->get_bool_attribute("\\abc9_carry")) { +						if (w->port_input) { +							if (carry_in != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module)); +							carry_in = port_name;  						} - -						for (auto b : rhs) { -							SigBit I = sigmap(b); -							if (b == RTLIL::Sx) -								b = State::S0; -							else if (I != b) { -								if (I == RTLIL::Sx) -									alias_map[b] = State::S0; -								else -									alias_map[b] = I; -							} -							co_bits.emplace_back(b); -							unused_bits.erase(I); -						} -					} -					if (w->port_output) { -						RTLIL::SigSpec rhs; -						auto it = cell->connections_.find(port_name); -						if (it != cell->connections_.end()) { -							if (GetSize(it->second) < GetSize(w)) -								it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second))); -							rhs = it->second; -						} -						else { -							Wire *wire = module->addWire(NEW_ID, GetSize(w)); -							if (blackbox) -								wire->set_bool_attribute(ID(abc9_padding)); -							rhs = wire; -							cell->setPort(port_name, rhs); -						} - -						for (const auto &b : rhs.bits()) { -							SigBit O = sigmap(b); -							if (O != b) -								alias_map[O] = b; -							ci_bits.emplace_back(b); -							undriven_bits.erase(O); +						if (w->port_output) { +							if (carry_out != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module)); +							carry_out = port_name;  						}  					} +					else +						r.first->second.push_back(port_name);  				} -				// Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box -				if (box_module->get_bool_attribute("\\abc9_flop")) { -					SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str())); -					if (rhs.empty()) -						log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); +				if (carry_in != IdString() && carry_out == IdString()) +					log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module)); +				if (carry_in == IdString() && carry_out != IdString()) +					log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module)); +				if (carry_in != IdString()) { +					r.first->second.push_back(carry_in); +					r.first->second.push_back(carry_out); +				} +			} +			for (auto port_name : r.first->second) { +				auto w = box_module->wire(port_name); +				log_assert(w); +				auto rhs = cell->connections_.at(port_name, SigSpec()); +				rhs.append(Const(State::Sx, GetSize(w)-GetSize(rhs))); +				if (w->port_input)  					for (auto b : rhs) {  						SigBit I = sigmap(b);  						if (b == RTLIL::Sx) @@ -450,12 +355,41 @@ struct XAigerWriter  						co_bits.emplace_back(b);  						unused_bits.erase(I);  					} -				} - -				box_list.emplace_back(cell); +				if (w->port_output) +					for (const auto &b : rhs) { +						SigBit O = sigmap(b); +						if (O != b) +							alias_map[O] = b; +						ci_bits.emplace_back(b); +						undriven_bits.erase(O); +						// If PI and CI, then must be a (* keep *) wire +						if (input_bits.erase(O)) { +							log_assert(output_bits.count(O)); +							log_assert(O.wire->get_bool_attribute(ID::keep)); +						} +					}  			} -			// TODO: Free memory from toposort, bit_drivers, bit_users +			// Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box +			if (box_module->get_bool_attribute("\\abc9_flop")) { +				SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str())); +				if (rhs.empty()) +					log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); + +				for (auto b : rhs) { +					SigBit I = sigmap(b); +					if (b == RTLIL::Sx) +						b = State::S0; +					else if (I != b) { +						if (I == RTLIL::Sx) +							alias_map[b] = State::S0; +						else +							alias_map[b] = I; +					} +					co_bits.emplace_back(b); +					unused_bits.erase(I); +				} +			}  		}  		for (auto bit : input_bits) @@ -501,6 +435,10 @@ struct XAigerWriter  		for (auto &bit : ci_bits) {  			aig_m++, aig_i++; +			// 1'bx may exist here due to a box output +			//   that has been padded to its full width +			if (bit == State::Sx) +				continue;  			log_assert(!aig_map.count(bit));  			aig_map[bit] = 2*aig_m;  		} @@ -512,7 +450,27 @@ struct XAigerWriter  		for (const auto &bit : output_bits) {  			ordered_outputs[bit] = aig_o++; -			aig_outputs.push_back(bit2aig(bit)); +			int aig; +			// Unlike bit2aig() which checks aig_map first, for +			//   inout/keep bits, since aig_map will point to +			//   the PI, first attempt to find the NOT/AND driver +			//   before resorting to an aig_map lookup (which +			//   could be another PO) +			if (input_bits.count(bit)) { +				if (not_map.count(bit)) { +					aig = bit2aig(not_map.at(bit)) ^ 1; +				} else if (and_map.count(bit)) { +					auto args = and_map.at(bit); +					int a0 = bit2aig(args.first); +					int a1 = bit2aig(args.second); +					aig = mkgate(a0, a1); +				} +				else +					aig = aig_map.at(bit); +			} +			else +				aig = bit2aig(bit); +			aig_outputs.push_back(aig);  		}  		for (auto &i : ff_bits) { @@ -612,106 +570,41 @@ struct XAigerWriter  		//	write_o_buffer(0);  		if (!box_list.empty() || !ff_bits.empty()) { -			RTLIL::Module *holes_module = module->design->addModule("$__holes__"); -			log_assert(holes_module); +			dict<IdString, std::tuple<int,int,int>> cell_cache; -			dict<IdString, std::tuple<Cell*,int,int,int>> cell_cache; - -			int port_id = 1;  			int box_count = 0;  			for (auto cell : box_list) { -				RTLIL::Module* orig_box_module = module->design->module(cell->type); -				log_assert(orig_box_module); -				IdString derived_name = orig_box_module->derive(module->design, cell->parameters); -				RTLIL::Module* box_module = module->design->module(derived_name); +				log_assert(cell); + +				RTLIL::Module* box_module = module->design->module(cell->type); +				log_assert(box_module); -				auto r = cell_cache.insert(derived_name); +				auto r = cell_cache.insert(cell->type);  				auto &v = r.first->second;  				if (r.second) { -					if (box_module->has_processes()) -						Pass::call_on_module(module->design, box_module, "proc"); -  					int box_inputs = 0, box_outputs = 0; -					if (box_module->get_bool_attribute("\\whitebox")) { -						auto holes_cell = holes_module->addCell(cell->name, derived_name); -						for (auto port_name : box_ports.at(cell->type)) { -							RTLIL::Wire *w = box_module->wire(port_name); -							log_assert(w); -							log_assert(!w->port_input || !w->port_output); -							auto &conn = holes_cell->connections_[port_name]; -							if (w->port_input) { -								for (int i = 0; i < GetSize(w); i++) { -									box_inputs++; -									RTLIL::Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); -									if (!holes_wire) { -										holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); -										holes_wire->port_input = true; -										holes_wire->port_id = port_id++; -										holes_module->ports.push_back(holes_wire->name); -									} -									conn.append(holes_wire); -								} -							} -							else if (w->port_output) { -								box_outputs += GetSize(w); -								conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w)); -							} -						} - -						// For flops only, create an extra 1-bit input that drives a new wire -						//   called "<cell>.abc9_ff.Q" that is used below -						if (box_module->get_bool_attribute("\\abc9_flop")) { -							box_inputs++; -							Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); -							if (!holes_wire) { -								holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); -								holes_wire->port_input = true; -								holes_wire->port_id = port_id++; -								holes_module->ports.push_back(holes_wire->name); -							} -							Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str())); -							holes_module->connect(Q, holes_wire); -						} - -						std::get<0>(v) = holes_cell; -					} -					else { -						for (auto port_name : box_ports.at(cell->type)) { -							RTLIL::Wire *w = box_module->wire(port_name); -							log_assert(w); -							log_assert(!w->port_input || !w->port_output); -							if (w->port_input) -								box_inputs += GetSize(w); -							else if (w->port_output) -								box_outputs += GetSize(w); -						} -						log_assert(std::get<0>(v) == nullptr); +					for (auto port_name : box_module->ports) { +						RTLIL::Wire *w = box_module->wire(port_name); +						log_assert(w); +						if (w->port_input) +							box_inputs += GetSize(w); +						if (w->port_output) +							box_outputs += GetSize(w);  					} -					std::get<1>(v) = box_inputs; -					std::get<2>(v) = box_outputs; -					std::get<3>(v) = box_module->attributes.at("\\abc9_box_id").as_int(); -				} +					// For flops only, create an extra 1-bit input that drives a new wire +					//   called "<cell>.abc9_ff.Q" that is used below +					if (box_module->get_bool_attribute("\\abc9_flop")) +						box_inputs++; -				auto holes_cell = std::get<0>(v); -				for (auto port_name : box_ports.at(cell->type)) { -					RTLIL::Wire *w = box_module->wire(port_name); -					log_assert(w); -					if (!w->port_output) -						continue; -					Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w)); -					holes_wire->port_output = true; -					holes_wire->port_id = port_id++; -					holes_module->ports.push_back(holes_wire->name); -					if (holes_cell) // whitebox -						holes_module->connect(holes_wire, holes_cell->getPort(port_name)); -					else // blackbox -						holes_module->connect(holes_wire, Const(State::S0, GetSize(w))); +					std::get<0>(v) = box_inputs; +					std::get<1>(v) = box_outputs; +					std::get<2>(v) = box_module->attributes.at("\\abc9_box_id").as_int();  				} +				write_h_buffer(std::get<0>(v));  				write_h_buffer(std::get<1>(v));  				write_h_buffer(std::get<2>(v)); -				write_h_buffer(std::get<3>(v));  				write_h_buffer(box_count++);  			} @@ -759,82 +652,17 @@ struct XAigerWriter  			f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));  			f.write(buffer_str.data(), buffer_str.size()); +			RTLIL::Module *holes_module = module->design->module(stringf("%s$holes", module->name.c_str()));  			if (holes_module) { -				log_push(); - -				// NB: fixup_ports() will sort ports by name -				//holes_module->fixup_ports(); -				holes_module->check(); - -				// Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger -				//   since boxes may contain parameters in which case `flatten` would have -				//   created a new $paramod ... -				Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap"); - -				SigMap holes_sigmap(holes_module); - -				dict<SigSpec, SigSpec> replace; -				for (auto it = holes_module->cells_.begin(); it != holes_module->cells_.end(); ) { -					auto cell = it->second; -					if (cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", -								"$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) { -						SigBit D = cell->getPort("\\D"); -						SigBit Q = cell->getPort("\\Q"); -						// Remove the $_DFF_* cell from what needs to be a combinatorial box -						it = holes_module->cells_.erase(it); -						Wire *port; -						if (GetSize(Q.wire) == 1) -							port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str())); -						else -							port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset)); -						log_assert(port); -						// Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;" -						//   in order to extract just the combinatorial control logic that feeds the box -						//   (i.e. clock enable, synchronous reset, etc.) -						replace.insert(std::make_pair(Q,D)); -						// Since `flatten` above would have created wires named "<cell>.Q", -						//   extract the pre-techmap cell name -						auto pos = Q.wire->name.str().rfind("."); -						log_assert(pos != std::string::npos); -						IdString driver = Q.wire->name.substr(0, pos); -						// And drive the signal that was previously driven by "DFF.Q" (typically -						//   used to implement clock-enable functionality) with the "<cell>.abc9_ff.Q" -						//   wire (which itself is driven by an input port) we inserted above -						Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str())); -						log_assert(currQ); -						holes_module->connect(Q, currQ); -						continue; -					} -					else if (!cell->type.in("$_NOT_", "$_AND_")) -						log_error("Whitebox contents cannot be represented as AIG. Please verify whiteboxes are synthesisable.\n"); -					++it; -				} - -				for (auto &conn : holes_module->connections_) { -					auto it = replace.find(sigmap(conn.second)); -					if (it != replace.end()) -						conn.second = it->second; -				} - -				// Move into a new (temporary) design so that "clean" will only -				// operate (and run checks on) this one module -				RTLIL::Design *holes_design = new RTLIL::Design; -				module->design->modules_.erase(holes_module->name); -				holes_design->add(holes_module); -				Pass::call(holes_design, "opt -purge"); -  				std::stringstream a_buffer;  				XAigerWriter writer(holes_module, true /* holes_mode */);  				writer.write_aiger(a_buffer, false /*ascii_mode*/); -				delete holes_design;  				f << "a";  				std::string buffer_str = a_buffer.str();  				int32_t buffer_size_be = to_big_endian(buffer_str.size());  				f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));  				f.write(buffer_str.data(), buffer_str.size()); - -				log_pop();  			}  		} @@ -917,7 +745,8 @@ struct XAigerBackend : public Backend {  		log("Write the top module (according to the (* top *) attribute or if only one module\n");  		log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, $_ABC9_FF_, or");  		log("non (* abc9_box_id *) cells will be converted into psuedo-inputs and\n"); -		log("pseudo-outputs.\n"); +		log("pseudo-outputs. Whitebox contents will be taken from the '<module-name>$holes'\n"); +		log("module, if it exists.\n");  		log("\n");  		log("    -ascii\n");  		log("        write ASCII version of AIGER format\n"); diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index a4b1e6fec..418fd722c 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -393,21 +393,6 @@ void AigerReader::parse_xaiger()  	if (f.peek() == '\n')  		f.get(); -	dict<int,IdString> box_lookup; -	for (auto m : design->modules()) { -		auto it = m->attributes.find(ID(abc9_box_id)); -		if (it == m->attributes.end()) -			continue; -		if (m->name.begins_with("$paramod")) -			continue; -		auto id = it->second.as_int(); -		auto r = box_lookup.insert(std::make_pair(id, m->name)); -		if (!r.second) -			log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n", -					log_id(m), id, log_id(r.first->second)); -		log_assert(r.second); -	} -  	// Parse footer (symbol table, comments, etc.)  	std::string s;  	for (int c = f.get(); c != EOF; c = f.get()) { @@ -429,6 +414,10 @@ void AigerReader::parse_xaiger()  				for (unsigned j = 0; j < cutLeavesM; ++j) {  					nodeID = parse_xaiger_literal(f);  					log_debug2("\t%u\n", nodeID); +					if (nodeID == 0) { +						log_debug("\tLUT '$lut$aiger%d$%d' input %d is constant!\n", aiger_autoidx, rootNodeID, cutLeavesM); +						continue; +					}  					RTLIL::Wire *wire = module->wire(stringf("$aiger%d$%d", aiger_autoidx, nodeID));  					log_assert(wire);  					input_sig.append(wire); @@ -436,10 +425,10 @@ void AigerReader::parse_xaiger()  				// TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())  				ce.clear();  				ce.compute_deps(output_sig, input_sig.to_sigbit_pool()); -				RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); -				for (int j = 0; j < (1 << cutLeavesM); ++j) { +				RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << GetSize(input_sig)); +				for (int j = 0; j < GetSize(lut_mask); ++j) {  					int gray = j ^ (j >> 1); -					ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)}); +					ce.set_incremental(input_sig, RTLIL::Const{gray, GetSize(input_sig)});  					RTLIL::SigBit o(output_sig);  					bool success YS_ATTRIBUTE(unused) = ce.eval(o);  					log_assert(success); @@ -453,11 +442,13 @@ void AigerReader::parse_xaiger()  			}  		}  		else if (c == 'r') { -			uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); +			uint32_t dataSize = parse_xaiger_literal(f);  			flopNum = parse_xaiger_literal(f);  			log_debug("flopNum = %u\n", flopNum);  			log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); -			f.ignore(flopNum * sizeof(uint32_t)); +			mergeability.reserve(flopNum); +			for (unsigned i = 0; i < flopNum; i++) +				mergeability.emplace_back(parse_xaiger_literal(f));  		}  		else if (c == 'n') {  			parse_xaiger_literal(f); @@ -479,11 +470,15 @@ void AigerReader::parse_xaiger()  			uint32_t boxNum = parse_xaiger_literal(f);  			log_debug("boxNum = %u\n", boxNum);  			for (unsigned i = 0; i < boxNum; i++) { -				f.ignore(2*sizeof(uint32_t)); +				uint32_t boxInputs = parse_xaiger_literal(f); +				uint32_t boxOutputs = parse_xaiger_literal(f);  				uint32_t boxUniqueId = parse_xaiger_literal(f);  				log_assert(boxUniqueId > 0);  				uint32_t oldBoxNum = parse_xaiger_literal(f); -				RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), box_lookup.at(boxUniqueId)); +				RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), stringf("$__boxid%u", boxUniqueId)); +				cell->setPort("\\i", SigSpec(State::S0, boxInputs)); +				cell->setPort("\\o", SigSpec(State::S0, boxOutputs)); +				cell->attributes["\\abc9_box_seq"] = oldBoxNum;  				boxes.emplace_back(cell);  			}  		} @@ -568,25 +563,18 @@ void AigerReader::parse_aiger_ascii()  	}  	// Parse outputs +	digits = ceil(log10(O));  	for (unsigned i = 0; i < O; ++i, ++line_count) {  		if (!(f >> l1))  			log_error("Line %u cannot be interpreted as an output!\n", line_count);  		log_debug2("%d is an output\n", l1); -		const unsigned variable = l1 >> 1; -		const bool invert = l1 & 1; -		RTLIL::IdString wire_name(stringf("$%d%s", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? -		RTLIL::Wire *wire = module->wire(wire_name); -		if (!wire) -			wire = createWireIfNotExists(module, l1); -		else if (wire->port_input || wire->port_output) { -			RTLIL::Wire *new_wire = module->addWire(NEW_ID); -			module->connect(new_wire, wire); -			wire = new_wire; -		} +		RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));  		wire->port_output = true; +		module->connect(wire, createWireIfNotExists(module, l1));  		outputs.push_back(wire);  	} +	//std::getline(f, line); // Ignore up to start of next line  	// Parse bad properties  	for (unsigned i = 0; i < B; ++i, ++line_count) { @@ -598,6 +586,8 @@ void AigerReader::parse_aiger_ascii()  		wire->port_output = true;  		bad_properties.push_back(wire);  	} +	//if (B > 0) +	//	std::getline(f, line); // Ignore up to start of next line  	// TODO: Parse invariant constraints  	for (unsigned i = 0; i < C; ++i, ++line_count) @@ -753,84 +743,41 @@ void AigerReader::parse_aiger_binary()  void AigerReader::post_process()  { -	dict<IdString, std::vector<IdString>> box_ports; -	unsigned ci_count = 0, co_count = 0, flop_count = 0; +	unsigned ci_count = 0, co_count = 0;  	for (auto cell : boxes) { -		RTLIL::Module* box_module = design->module(cell->type); -		log_assert(box_module); - -		auto r = box_ports.insert(cell->type); -		if (r.second) { -			// Make carry in the last PI, and carry out the last PO -			//   since ABC requires it this way -			IdString carry_in, carry_out; -			for (const auto &port_name : box_module->ports) { -				auto w = box_module->wire(port_name); -				log_assert(w); -				if (w->get_bool_attribute("\\abc9_carry")) { -					if (w->port_input) -						carry_in = port_name; -					if (w->port_output) -						carry_out = port_name; -				} -				else -					r.first->second.push_back(port_name); -			} -			if (carry_in != IdString()) { -				log_assert(carry_out != IdString()); -				r.first->second.push_back(carry_in); -				r.first->second.push_back(carry_out); -			} +		for (auto &bit : cell->connections_.at("\\i")) { +			log_assert(bit == State::S0); +			log_assert(co_count < outputs.size()); +			bit = outputs[co_count++]; +			log_assert(bit.wire && GetSize(bit.wire) == 1); +			log_assert(bit.wire->port_output); +			bit.wire->port_output = false;  		} - -		for (auto port_name : box_ports.at(cell->type)) { -			RTLIL::Wire* port = box_module->wire(port_name); -			log_assert(port); -			RTLIL::SigSpec rhs; -			for (int i = 0; i < GetSize(port); i++) { -				RTLIL::Wire* wire = nullptr; -				if (port->port_input) { -					log_assert(co_count < outputs.size()); -					wire = outputs[co_count++]; -					log_assert(wire); -					log_assert(wire->port_output); -					wire->port_output = false; -				} -				if (port->port_output) { -					log_assert((piNum + ci_count) < inputs.size()); -					wire = inputs[piNum + ci_count++]; -					log_assert(wire); -					log_assert(wire->port_input); -					wire->port_input = false; -				} -				rhs.append(wire); -			} -			cell->setPort(port_name, rhs); +		for (auto &bit : cell->connections_.at("\\o")) { +			log_assert(bit == State::S0); +			log_assert((piNum + ci_count) < inputs.size()); +			bit = inputs[piNum + ci_count++]; +			log_assert(bit.wire && GetSize(bit.wire) == 1); +			log_assert(bit.wire->port_input); +			bit.wire->port_input = false;  		} +	} -		if (box_module->attributes.count("\\abc9_flop")) { -			log_assert(co_count < outputs.size()); -			Wire *wire = outputs[co_count++]; -			log_assert(wire); -			log_assert(wire->port_output); -			wire->port_output = false; - -			RTLIL::Wire *d = outputs[outputs.size() - flopNum + flop_count]; -			log_assert(d); -			log_assert(d->port_output); -			d->port_output = false; - -			RTLIL::Wire *q = inputs[piNum - flopNum + flop_count]; -			log_assert(q); -			log_assert(q->port_input); -			q->port_input = false; - -			auto ff = module->addCell(NEW_ID, "$__ABC9_FF_"); -			ff->setPort("\\D", d); -			ff->setPort("\\Q", q); -			flop_count++; -			continue; -		} +	for (uint32_t i = 0; i < flopNum; i++) { +		RTLIL::Wire *d = outputs[outputs.size() - flopNum + i]; +		log_assert(d); +		log_assert(d->port_output); +		d->port_output = false; + +		RTLIL::Wire *q = inputs[piNum - flopNum + i]; +		log_assert(q); +		log_assert(q->port_input); +		q->port_input = false; + +		auto ff = module->addCell(NEW_ID, "$__ABC9_FF_"); +		ff->setPort("\\D", d); +		ff->setPort("\\Q", q); +		ff->attributes["\\abc9_mergeability"] = mergeability[i];  	}  	dict<RTLIL::IdString, int> wideports_cache; @@ -859,6 +806,7 @@ void AigerReader::post_process()  						wire->port_input = false;  						module->connect(wire, existing);  					} +					log_debug(" -> %s\n", log_id(escaped_s));  				}  				else if (index > 0) {  					std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); @@ -872,18 +820,14 @@ void AigerReader::post_process()  						module->connect(wire, existing);  						wire->port_input = false;  					} +					log_debug(" -> %s\n", log_id(indexed_name));  				} -				log_debug(" -> %s\n", log_id(wire));  			}  			else if (type == "output") {  				log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());  				RTLIL::Wire* wire = outputs[variable + co_count];  				log_assert(wire);  				log_assert(wire->port_output); -				if (escaped_s == "$__dummy__") { -					wire->port_output = false; -					continue; -				}  				log_debug("Renaming output %s", log_id(wire));  				if (index == 0) { @@ -896,9 +840,11 @@ void AigerReader::post_process()  					}  					else {  						wire->port_output = false; +						existing->port_output = true;  						module->connect(wire, existing);  						wire = existing;  					} +					log_debug(" -> %s\n", log_id(escaped_s));  				}  				else if (index > 0) {  					std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); @@ -909,11 +855,12 @@ void AigerReader::post_process()  							wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);  					}  					else { -						module->connect(wire, existing);  						wire->port_output = false; +						existing->port_output = true; +						module->connect(wire, existing);  					} +					log_debug(" -> %s\n", log_id(indexed_name));  				} -				log_debug(" -> %s\n", log_id(wire));  				int init;  				mf >> init;  				if (init < 2) @@ -921,26 +868,8 @@ void AigerReader::post_process()  			}  			else if (type == "box") {  				RTLIL::Cell* cell = module->cell(stringf("$box%d", variable)); -				if (cell) { // ABC could have optimised this box away +				if (cell) // ABC could have optimised this box away  					module->rename(cell, escaped_s); -					for (const auto &i : cell->connections()) { -						RTLIL::IdString port_name = i.first; -						RTLIL::SigSpec rhs = i.second; -						int index = 0; -						for (auto bit : rhs.bits()) { -							RTLIL::Wire* wire = bit.wire; -							RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name))); -							if (index == 0) -								module->rename(wire, escaped_s); -							else if (index > 0) { -								module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index)); -								if (wideports) -									wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); -							} -							index++; -						} -					} -				}  			}  			else  				log_error("Symbol type '%s' not recognised.\n", type.c_str()); @@ -1018,18 +947,21 @@ struct AigerFrontend : public Frontend {  		log("Load module from an AIGER file into the current design.\n");  		log("\n");  		log("    -module_name <module_name>\n"); -		log("        Name of module to be created (default: <filename>)\n"); +		log("        name of module to be created (default: <filename>)\n");  		log("\n");  		log("    -clk_name <wire_name>\n"); -		log("        If specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); -		log("        clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n"); +		log("        if specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); +		log("        clocked by wire of this name. otherwise, $_FF_ cells will be used\n");  		log("\n");  		log("    -map <filename>\n");  		log("        read file with port and latch symbols\n");  		log("\n");  		log("    -wideports\n"); -		log("        Merge ports that match the pattern 'name[int]' into a single\n"); -		log("        multi-bit port 'name'.\n"); +		log("        merge ports that match the pattern 'name[int]' into a single\n"); +		log("        multi-bit port 'name'\n"); +		log("\n"); +		log("    -xaiger\n"); +		log("        read XAIGER extensions\n");  		log("\n");  	}  	void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -1039,7 +971,7 @@ struct AigerFrontend : public Frontend {  		RTLIL::IdString clk_name;  		RTLIL::IdString module_name;  		std::string map_filename; -		bool wideports = false; +		bool wideports = false, xaiger = false;  		size_t argidx;  		for (argidx = 1; argidx < args.size(); argidx++) { @@ -1060,6 +992,10 @@ struct AigerFrontend : public Frontend {  				wideports = true;  				continue;  			} +			if (arg == "-xaiger") { +				xaiger = true; +				continue; +			}  			break;  		}  		extra_args(f, filename, args, argidx, true); @@ -1079,7 +1015,10 @@ struct AigerFrontend : public Frontend {  		}  		AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); -		reader.parse_aiger(); +		if (xaiger) +			reader.parse_xaiger(); +		else +			reader.parse_aiger();  	}  } AigerFrontend; diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 722f1e472..46ac81212 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -45,6 +45,7 @@ struct AigerReader      std::vector<RTLIL::Wire*> outputs;      std::vector<RTLIL::Wire*> bad_properties;      std::vector<RTLIL::Cell*> boxes; +    std::vector<int> mergeability;      AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports);      void parse_aiger(); diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index cd357d72a..369c9de64 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -8,6 +8,8 @@ OBJS += passes/techmap/libparse.o  ifeq ($(ENABLE_ABC),1)  OBJS += passes/techmap/abc.o  OBJS += passes/techmap/abc9.o +OBJS += passes/techmap/abc9_exe.o +OBJS += passes/techmap/abc9_ops.o  ifneq ($(ABCEXTERNAL),)  passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'  passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 2568a6cd1..2aeda16d6 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -2,7 +2,7 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *                2019  Eddie Hung <eddie@fpgeh.com> + *            (C) 2019  Eddie Hung    <eddie@fpgeh.com>   *   *  Permission to use, copy, modify, and/or distribute this software for any   *  purpose with or without fee is hereby granted, provided that the above @@ -23,714 +23,19 @@  // http://www.eecs.berkeley.edu/~alanmi/abc/  #include "kernel/register.h" -#include "kernel/sigtools.h"  #include "kernel/celltypes.h" -#include "kernel/cost.h" +#include "kernel/rtlil.h"  #include "kernel/log.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <cerrno> -#include <sstream> -#include <climits> -#ifndef _WIN32 -#  include <unistd.h> -#  include <dirent.h> -#endif - -#include "frontends/aiger/aigerparse.h" -#include "kernel/utils.h" - -#ifdef YOSYS_LINK_ABC -extern "C" int Abc_RealMain(int argc, char *argv[]); -#endif +// abc9_exe.cc +std::string fold_abc9_cmd(std::string str);  USING_YOSYS_NAMESPACE  PRIVATE_NAMESPACE_BEGIN -int map_autoidx; - -inline std::string remap_name(RTLIL::IdString abc9_name) -{ -	return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); -} - -void handle_loops(RTLIL::Design *design, RTLIL::Module *module) -{ -	Pass::call(design, "scc -set_attr abc9_scc_id {} % w:*"); - -	// For every unique SCC found, (arbitrarily) find the first -	// cell in the component, and select (and mark) all its output -	// wires -	pool<RTLIL::Const> ids_seen; -	for (auto cell : module->cells()) { -		auto it = cell->attributes.find(ID(abc9_scc_id)); -		if (it != cell->attributes.end()) { -			auto r = ids_seen.insert(it->second); -			if (r.second) { -				for (auto &c : cell->connections_) { -					if (c.second.is_fully_const()) continue; -					if (cell->output(c.first)) { -						SigBit b = c.second.as_bit(); -						Wire *w = b.wire; -						if (w->port_input) { -							// In this case, hopefully the loop break has been already created -							// Get the non-prefixed wire -							Wire *wo = module->wire(stringf("%s.abco", b.wire->name.c_str())); -							log_assert(wo != nullptr); -							log_assert(wo->port_output); -							log_assert(b.offset < GetSize(wo)); -							c.second = RTLIL::SigBit(wo, b.offset); -						} -						else { -							// Create a new output/input loop break -							w->port_input = true; -							w = module->wire(stringf("%s.abco", w->name.c_str())); -							if (!w) { -								w = module->addWire(stringf("%s.abco", b.wire->name.c_str()), GetSize(b.wire)); -								w->port_output = true; -							} -							else { -								log_assert(w->port_input); -								log_assert(b.offset < GetSize(w)); -							} -							w->set_bool_attribute(ID(abc9_scc_break)); -							c.second = RTLIL::SigBit(w, b.offset); -						} -					} -				} -			} -			cell->attributes.erase(it); -		} -	} - -	module->fixup_ports(); -} - -std::string add_echos_to_abc9_cmd(std::string str) -{ -	std::string new_str, token; -	for (size_t i = 0; i < str.size(); i++) { -		token += str[i]; -		if (str[i] == ';') { -			while (i+1 < str.size() && str[i+1] == ' ') -				i++; -			new_str += "echo + " + token + " " + token + " "; -			token.clear(); -		} -	} - -	if (!token.empty()) { -		if (!new_str.empty()) -			new_str += "echo + " + token + "; "; -		new_str += token; -	} - -	return new_str; -} - -std::string fold_abc9_cmd(std::string str) -{ -	std::string token, new_str = "          "; -	int char_counter = 10; - -	for (size_t i = 0; i <= str.size(); i++) { -		if (i < str.size()) -			token += str[i]; -		if (i == str.size() || str[i] == ';') { -			if (char_counter + token.size() > 75) -				new_str += "\n              ", char_counter = 14; -			new_str += token, char_counter += token.size(); -			token.clear(); -		} -	} - -	return new_str; -} - -std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) -{ -	if (show_tempdir) -		return text; - -	while (1) { -		size_t pos = text.find(tempdir_name); -		if (pos == std::string::npos) -			break; -		text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); -	} - -	std::string  selfdir_name = proc_self_dirname(); -	if (selfdir_name != "/") { -		while (1) { -			size_t pos = text.find(selfdir_name); -			if (pos == std::string::npos) -				break; -			text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); -		} -	} - -	return text; -} - -struct abc9_output_filter -{ -	bool got_cr; -	int escape_seq_state; -	std::string linebuf; -	std::string tempdir_name; -	bool show_tempdir; - -	abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) -	{ -		got_cr = false; -		escape_seq_state = 0; -	} - -	void next_char(char ch) -	{ -		if (escape_seq_state == 0 && ch == '\033') { -			escape_seq_state = 1; -			return; -		} -		if (escape_seq_state == 1) { -			escape_seq_state = ch == '[' ? 2 : 0; -			return; -		} -		if (escape_seq_state == 2) { -			if ((ch < '0' || '9' < ch) && ch != ';') -				escape_seq_state = 0; -			return; -		} -		escape_seq_state = 0; -		if (ch == '\r') { -			got_cr = true; -			return; -		} -		if (ch == '\n') { -			log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); -			got_cr = false, linebuf.clear(); -			return; -		} -		if (got_cr) -			got_cr = false, linebuf.clear(); -		linebuf += ch; -	} - -	void next_line(const std::string &line) -	{ -		//int pi, po; -		//if (sscanf(line.c_str(), "Start-point = pi%d.  End-point = po%d.", &pi, &po) == 2) { -		//	log("ABC: Start-point = pi%d (%s).  End-point = po%d (%s).\n", -		//			pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", -		//			po, po_map.count(po) ? po_map.at(po).c_str() : "???"); -		//	return; -		//} - -		for (char ch : line) -			next_char(ch); -	} -}; - -void abc9_module(RTLIL::Design *design, RTLIL::Module *module, std::string script_file, std::string exe_file, -		bool cleanup, vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, -		bool show_tempdir, std::string box_file, std::string lut_file, -		std::string wire_delay -) +struct Abc9Pass : public ScriptPass  { -	map_autoidx = autoidx++; - -	std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; -	if (!cleanup) -		tempdir_name[0] = tempdir_name[4] = '_'; -	tempdir_name = make_temp_dir(tempdir_name); -	log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n", -			module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); - -	std::string abc9_script; - -	if (!lut_costs.empty()) { -		abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); -		if (!box_file.empty()) -			abc9_script += stringf("read_box %s; ", box_file.c_str()); -	} -	else -	if (!lut_file.empty()) { -		abc9_script += stringf("read_lut %s; ", lut_file.c_str()); -		if (!box_file.empty()) -			abc9_script += stringf("read_box %s; ", box_file.c_str()); -	} -	else -		log_abort(); - -	abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); - -	if (!script_file.empty()) { -		if (script_file[0] == '+') { -			for (size_t i = 1; i < script_file.size(); i++) -				if (script_file[i] == '\'') -					abc9_script += "'\\''"; -				else if (script_file[i] == ',') -					abc9_script += " "; -				else -					abc9_script += script_file[i]; -		} else -			abc9_script += stringf("source %s", script_file.c_str()); -	} else if (!lut_costs.empty() || !lut_file.empty()) { -		abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos) -			: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos); -	} else -		log_abort(); - -	for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) -		abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); - -	//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos)) -	//	abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3); - -	for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) -		abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); - -	std::string C; -	if (design->scratchpad.count("abc9.if.C")) -		C = "-C " + design->scratchpad_get_string("abc9.if.C"); -	for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos)) -		abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3); - -	std::string R; -	if (design->scratchpad.count("abc9.if.R")) -		R = "-R " + design->scratchpad_get_string("abc9.if.R"); -	for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos)) -		abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3); - -	abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str()); -	if (design->scratchpad_get_bool("abc9.verify")) { -		if (dff_mode) -			abc9_script += "; &verify -s"; -		else -			abc9_script += "; &verify"; -	} -	abc9_script += "; time"; -	abc9_script = add_echos_to_abc9_cmd(abc9_script); - -	for (size_t i = 0; i+1 < abc9_script.size(); i++) -		if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') -			abc9_script[i+1] = '\n'; - -	FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); -	fprintf(f, "%s\n", abc9_script.c_str()); -	fclose(f); - -	log_push(); - -	handle_loops(design, module); - -	Pass::call(design, "aigmap -select"); - -	Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); - -	int count_outputs = design->scratchpad_get_int("write_xaiger.num_outputs"); -	log("Extracted %d AND gates and %d wires to a netlist network with %d inputs and %d outputs.\n", -			design->scratchpad_get_int("write_xaiger.num_ands"), -			design->scratchpad_get_int("write_xaiger.num_wires"), -			design->scratchpad_get_int("write_xaiger.num_inputs"), -			count_outputs); - -	if (count_outputs > 0) { -		std::string buffer; -		std::ifstream ifs; -#if 0 -		buffer = stringf("%s/%s", tempdir_name.c_str(), "input.xaig"); -		ifs.open(buffer); -		if (ifs.fail()) -			log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); -		buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); -		log_assert(!design->module(ID($__abc9__))); -		{ -			AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); -			reader.parse_xaiger(); -		} -		ifs.close(); -		Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected")); -		design->remove(design->module(ID($__abc9__))); -#endif - -		log_header(design, "Executing ABC9.\n"); - -		if (!lut_costs.empty()) { -			buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); -			f = fopen(buffer.c_str(), "wt"); -			if (f == NULL) -				log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); -			for (int i = 0; i < GetSize(lut_costs); i++) -				fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); -			fclose(f); -		} - -		buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); -		log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); - -#ifndef YOSYS_LINK_ABC -		abc9_output_filter filt(tempdir_name, show_tempdir); -		int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); -#else -		// These needs to be mutable, supposedly due to getopt -		char *abc9_argv[5]; -		string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); -		abc9_argv[0] = strdup(exe_file.c_str()); -		abc9_argv[1] = strdup("-s"); -		abc9_argv[2] = strdup("-f"); -		abc9_argv[3] = strdup(tmp_script_name.c_str()); -		abc9_argv[4] = 0; -		int ret = Abc_RealMain(4, abc9_argv); -		free(abc9_argv[0]); -		free(abc9_argv[1]); -		free(abc9_argv[2]); -		free(abc9_argv[3]); -#endif -		if (ret != 0) -			log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); - -		buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig"); -		ifs.open(buffer, std::ifstream::binary); -		if (ifs.fail()) -			log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - -		buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); -		log_assert(!design->module(ID($__abc9__))); - -		AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); -		reader.parse_xaiger(); -		ifs.close(); - -#if 0 -		Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected")); -#endif - -		log_header(design, "Re-integrating ABC9 results.\n"); -		RTLIL::Module *mapped_mod = design->module(ID($__abc9__)); -		if (mapped_mod == NULL) -			log_error("ABC output file does not contain a module `$__abc9__'.\n"); - -		for (auto w : mapped_mod->wires()) -			module->addWire(remap_name(w->name), GetSize(w)); - -		dict<IdString, bool> abc9_box; -		vector<RTLIL::Cell*> boxes; -		for (auto cell : module->cells().to_vector()) { -			if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) { -				module->remove(cell); -				continue; -			} -			RTLIL::Module* box_module = design->module(cell->type); -			auto jt = abc9_box.find(cell->type); -			if (jt == abc9_box.end()) -				jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first; -			if (jt->second) { -				if (box_module->get_bool_attribute("\\abc9_flop")) { -					if (dff_mode) -						boxes.emplace_back(cell); -					else -						box_module->set_bool_attribute("\\abc9_keep", false); -				} -				else -					boxes.emplace_back(cell); -			} -		} - -		dict<SigBit, pool<IdString>> bit_drivers, bit_users; -		TopoSort<IdString, RTLIL::sort_by_id_str> toposort; -		dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; -		dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; - -		std::map<IdString, int> cell_stats; -		for (auto mapped_cell : mapped_mod->cells()) -		{ -			toposort.node(mapped_cell->name); - -			RTLIL::Cell *cell = nullptr; -			if (mapped_cell->type == ID($_NOT_)) { -				RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A); -				RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); -				bit_users[a_bit].insert(mapped_cell->name); -				bit_drivers[y_bit].insert(mapped_cell->name); - -				if (!a_bit.wire) { -					mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); -					RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); -					log_assert(wire); -					module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); -				} -				else if (!lut_costs.empty() || !lut_file.empty()) { -					RTLIL::Cell* driver_lut = nullptr; -					// ABC can return NOT gates that drive POs -					if (!a_bit.wire->port_input) { -						// If it's not a NOT gate that that comes from a PI directly, -						// find the driver LUT and clone that to guarantee that we won't -						// increase the max logic depth -						// (TODO: Optimise by not cloning unless will increase depth) -						RTLIL::IdString driver_name; -						if (GetSize(a_bit.wire) == 1) -							driver_name = stringf("$lut%s", a_bit.wire->name.c_str()); -						else -							driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset); -						driver_lut = mapped_mod->cell(driver_name); -					} - -					if (!driver_lut) { -						// If a driver couldn't be found (could be from PI or box CI) -						// then implement using a LUT -						cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())), -								RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), -								RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), -								RTLIL::Const::from_string("01")); -						bit2sinks[cell->getPort(ID::A)].push_back(cell); -						cell_stats[ID($lut)]++; -					} -					else -						not2drivers[mapped_cell] = driver_lut; -					continue; -				} -				else -					log_abort(); -				continue; -			} -			cell_stats[mapped_cell->type]++; - -			RTLIL::Cell *existing_cell = nullptr; -			if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { -				if (mapped_cell->type == ID($lut) && -						GetSize(mapped_cell->getPort(ID::A)) == 1 && -						mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { -					SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name)); -					SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name)); -					module->connect(my_y, my_a); -					log_abort(); -					continue; -				} -				cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); -			} -			else { -				existing_cell = module->cell(mapped_cell->name); -				log_assert(existing_cell); -				cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); -			} - -			if (existing_cell) { -				cell->parameters = existing_cell->parameters; -				cell->attributes = existing_cell->attributes; -			} -			else { -				cell->parameters = mapped_cell->parameters; -				cell->attributes = mapped_cell->attributes; -			} - -			RTLIL::Module* box_module = design->module(mapped_cell->type); -			auto abc9_flop = box_module && box_module->get_bool_attribute("\\abc9_flop"); -			for (auto &conn : mapped_cell->connections()) { -				RTLIL::SigSpec newsig; -				for (auto c : conn.second.chunks()) { -					if (c.width == 0) -						continue; -					//log_assert(c.width == 1); -					if (c.wire) -						c.wire = module->wires_.at(remap_name(c.wire->name)); -					newsig.append(c); -				} -				cell->setPort(conn.first, newsig); - -				if (!abc9_flop) { -					if (cell->input(conn.first)) { -						for (auto i : newsig) -							bit2sinks[i].push_back(cell); -						for (auto i : conn.second) -							bit_users[i].insert(mapped_cell->name); -					} -					if (cell->output(conn.first)) -						for (auto i : conn.second) -							bit_drivers[i].insert(mapped_cell->name); -				} -			} -		} - -		for (auto existing_cell : boxes) { -			Cell *cell = module->cell(remap_name(existing_cell->name)); -			if (cell) { -				for (auto &conn : existing_cell->connections()) { -					if (!conn.second.is_wire()) -						continue; -					Wire *wire = conn.second.as_wire(); -					if (!wire->get_bool_attribute(ID(abc9_padding))) -						continue; -					cell->unsetPort(conn.first); -					log_debug("Dropping padded port connection for %s (%s) .%s (%s )\n", log_id(cell), cell->type.c_str(), log_id(conn.first), log_signal(conn.second)); -				} -				module->swap_names(cell, existing_cell); -			} -			module->remove(existing_cell); -		} - -		// Copy connections (and rename) from mapped_mod to module -		for (auto conn : mapped_mod->connections()) { -			if (!conn.first.is_fully_const()) { -				auto chunks = conn.first.chunks(); -				for (auto &c : chunks) -					c.wire = module->wires_.at(remap_name(c.wire->name)); -				conn.first = std::move(chunks); -			} -			if (!conn.second.is_fully_const()) { -				auto chunks = conn.second.chunks(); -				for (auto &c : chunks) -					if (c.wire) -						c.wire = module->wires_.at(remap_name(c.wire->name)); -				conn.second = std::move(chunks); -			} -			module->connect(conn); -		} - -		for (auto &it : cell_stats) -			log("ABC RESULTS:   %15s cells: %8d\n", it.first.c_str(), it.second); -		int in_wires = 0, out_wires = 0; - -		// Stitch in mapped_mod's inputs/outputs into module -		for (auto port : mapped_mod->ports) { -			RTLIL::Wire *w = mapped_mod->wire(port); -			RTLIL::Wire *wire = module->wire(port); -			log_assert(wire); -			RTLIL::Wire *remap_wire = module->wire(remap_name(port)); -			RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire)); -			log_assert(GetSize(signal) >= GetSize(remap_wire)); - -			RTLIL::SigSig conn; -			if (w->port_output) { -				conn.first = signal; -				conn.second = remap_wire; -				out_wires++; -				module->connect(conn); -			} -			else if (w->port_input) { -				conn.first = remap_wire; -				conn.second = signal; -				in_wires++; -				module->connect(conn); -			} -		} - -		for (auto &it : bit_users) -			if (bit_drivers.count(it.first)) -				for (auto driver_cell : bit_drivers.at(it.first)) -				for (auto user_cell : it.second) -					toposort.edge(driver_cell, user_cell); -		bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); -		log_assert(no_loops); - -		for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { -			RTLIL::Cell *not_cell = mapped_mod->cell(*ii); -			log_assert(not_cell); -			if (not_cell->type != ID($_NOT_)) -				continue; -			auto it = not2drivers.find(not_cell); -			if (it == not2drivers.end()) -				continue; -			RTLIL::Cell *driver_lut = it->second; -			RTLIL::SigBit a_bit = not_cell->getPort(ID::A); -			RTLIL::SigBit y_bit = not_cell->getPort(ID::Y); -			RTLIL::Const driver_mask; - -			a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); -			y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); - -			auto jt = bit2sinks.find(a_bit); -			if (jt == bit2sinks.end()) -				goto clone_lut; - -			for (auto sink_cell : jt->second) -				if (sink_cell->type != ID($lut)) -					goto clone_lut; - -			// Push downstream LUTs past inverter -			for (auto sink_cell : jt->second) { -				SigSpec A = sink_cell->getPort(ID::A); -				RTLIL::Const mask = sink_cell->getParam(ID(LUT)); -				int index = 0; -				for (; index < GetSize(A); index++) -					if (A[index] == a_bit) -						break; -				log_assert(index < GetSize(A)); -				int i = 0; -				while (i < GetSize(mask)) { -					for (int j = 0; j < (1 << index); j++) -						std::swap(mask[i+j], mask[i+j+(1 << index)]); -					i += 1 << (index+1); -				} -				A[index] = y_bit; -				sink_cell->setPort(ID::A, A); -				sink_cell->setParam(ID(LUT), mask); -			} - -			// Since we have rewritten all sinks (which we know -			// to be only LUTs) to be after the inverter, we can -			// go ahead and clone the LUT with the expectation -			// that the original driving LUT will become dangling -			// and get cleaned away -clone_lut: -			driver_mask = driver_lut->getParam(ID(LUT)); -			for (auto &b : driver_mask.bits) { -				if (b == RTLIL::State::S0) b = RTLIL::State::S1; -				else if (b == RTLIL::State::S1) b = RTLIL::State::S0; -			} -			auto cell = module->addLut(NEW_ID, -					driver_lut->getPort(ID::A), -					y_bit, -					driver_mask); -			for (auto &bit : cell->connections_.at(ID::A)) { -				bit.wire = module->wires_.at(remap_name(bit.wire->name)); -				bit2sinks[bit].push_back(cell); -			} -		} - -		// Now 'unexpose' those wires by undoing -		// the expose operation -- remove them from PO/PI -		// and re-connecting them back together -		for (auto wire : module->wires()) { -			auto it = wire->attributes.find(ID(abc9_scc_break)); -			if (it != wire->attributes.end()) { -				wire->attributes.erase(it); -				log_assert(wire->port_output); -				wire->port_output = false; -				std::string name = wire->name.str(); -				RTLIL::Wire *i_wire = module->wire(name.substr(0, GetSize(name) - 5)); -				log_assert(i_wire); -				log_assert(i_wire->port_input); -				i_wire->port_input = false; -				module->connect(i_wire, wire); -			} -		} -		module->fixup_ports(); - -		//log("ABC RESULTS:        internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); -		log("ABC RESULTS:           input signals: %8d\n", in_wires); -		log("ABC RESULTS:          output signals: %8d\n", out_wires); - -		design->remove(mapped_mod); -	} -	else -	{ -		log("Don't call ABC as there is nothing to map.\n"); -	} - -	if (cleanup) -	{ -		log("Removing temp directory.\n"); -		remove_directory(tempdir_name); -	} - -	log_pop(); -} - -struct Abc9Pass : public Pass { -	Abc9Pass() : Pass("abc9", "use ABC9 for technology mapping") { } +	Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { }  	void on_register() YS_OVERRIDE  	{  		RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs"; @@ -782,8 +87,14 @@ struct Abc9Pass : public Pass {  		log("\n");  		log("    abc9 [options] [selection]\n");  		log("\n"); -		log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n"); -		log("library to a target architecture. Only fully-selected modules are supported.\n"); +		log("This script pass performs a sequence of commands to facilitate the use of the ABC\n"); +		log("tool [1] for technology mapping of the current design to a target FPGA\n"); +		log("architecture. Only fully-selected modules are supported.\n"); +		log("\n"); +		log("    -run <from_label>:<to_label>\n"); +		log("        only run the commands between the labels (see below). an empty\n"); +		log("        from label is synonymous to 'begin', and empty to label is\n"); +		log("        synonymous to the end of the command list.\n");  		log("\n");  		log("    -exe <command>\n");  #ifdef ABCEXTERNAL @@ -802,15 +113,11 @@ struct Abc9Pass : public Pass {  		log("        replaced with blanks before the string is passed to ABC.\n");  		log("\n");  		log("        if no -script parameter is given, the following scripts are used:\n"); -		log("\n"); -		log("        for -lut/-luts:\n");  		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str());  		log("\n");  		log("    -fast\n");  		log("        use different default scripts that are slightly faster (at the cost\n");  		log("        of output quality):\n"); -		log("\n"); -		log("        for -lut/-luts:\n");  		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str());  		log("\n");  		log("    -D <picoseconds>\n"); @@ -851,7 +158,7 @@ struct Abc9Pass : public Pass {  		log("        command output is identical across runs.\n");  		log("\n");  		log("    -box <file>\n"); -		log("        pass this file with box library to ABC. Use with -lut.\n"); +		log("        pass this file with box library to ABC.\n");  		log("\n");  		log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");  		log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); @@ -862,234 +169,163 @@ struct Abc9Pass : public Pass {  		log("\n");  		log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");  		log("\n"); +		help_script(); +		log("\n");  	} -	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE -	{ -		log_header(design, "Executing ABC9 pass (technology mapping using ABC9).\n"); -		log_push(); -#ifdef ABCEXTERNAL -		std::string exe_file = ABCEXTERNAL; -#else -		std::string exe_file = proc_self_dirname() + "yosys-abc"; -#endif -		std::string script_file, clk_str, box_file, lut_file; -		std::string delay_target, lutin_shared = "-S 1", wire_delay; -		bool fast_mode = false, dff_mode = false, cleanup = true; -		bool show_tempdir = false; -		vector<int> lut_costs; +	std::stringstream exe_cmd; +	bool dff_mode, cleanup; -#if 0 -		cleanup = false; -		show_tempdir = true; -#endif +	void clear_flags() YS_OVERRIDE +	{ +		exe_cmd.str(""); +		exe_cmd << "abc9_exe"; +		dff_mode = false; +		cleanup = true; +	} -#ifdef _WIN32 -#ifndef ABCEXTERNAL -		if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) -			exe_file = proc_self_dirname() + "..\\yosys-abc"; -#endif -#endif +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		std::string run_from, run_to; +		clear_flags();  		// get arguments from scratchpad first, then override by command arguments -		std::string lut_arg, luts_arg; -		exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */); -		script_file = design->scratchpad_get_string("abc9.script", script_file); -		if (design->scratchpad.count("abc9.D")) { -			delay_target = "-D " + design->scratchpad_get_string("abc9.D"); -		} -		lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); -		luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); -		fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode);  		dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);  		cleanup = !design->scratchpad_get_bool("abc9.nocleanup", !cleanup); -		show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); -		box_file = design->scratchpad_get_string("abc9.box", box_file); -		if (design->scratchpad.count("abc9.W")) { -			wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); -		}  		if (design->scratchpad_get_bool("abc9.debug")) {  			cleanup = false; -			show_tempdir = true; +			exe_cmd << " -showtmp";  		}  		size_t argidx; -		char pwd [PATH_MAX]; -		if (!getcwd(pwd, sizeof(pwd))) { -			log_cmd_error("getcwd failed: %s\n", strerror(errno)); -			log_abort(); -		}  		for (argidx = 1; argidx < args.size(); argidx++) {  			std::string arg = args[argidx]; -			if (arg == "-exe" && argidx+1 < args.size()) { -				exe_file = args[++argidx]; -				continue; -			} -			if (arg == "-script" && argidx+1 < args.size()) { -				script_file = args[++argidx]; -				continue; -			} -			if (arg == "-D" && argidx+1 < args.size()) { -				delay_target = "-D " + args[++argidx]; -				continue; -			} -			//if (arg == "-S" && argidx+1 < args.size()) { -			//	lutin_shared = "-S " + args[++argidx]; -			//	continue; -			//} -			if (arg == "-lut" && argidx+1 < args.size()) { -				lut_arg = args[++argidx]; +			if ((arg == "-exe" || arg == "-script" || arg == "-D" || +						/* arg == "-S" || */ arg == "-lut" || arg == "-luts" || +						arg == "-box" || arg == "-W") && +					argidx+1 < args.size()) { +				exe_cmd << " " << arg << " " << args[++argidx];  				continue;  			} -			if (arg == "-luts" && argidx+1 < args.size()) { -				luts_arg = args[++argidx]; -				continue; -			} -			if (arg == "-fast") { -				fast_mode = true; +			if (arg == "-fast" || /* arg == "-dff" || */ +					/* arg == "-nocleanup" || */ arg == "-showtmp") { +				exe_cmd << " " << arg;  				continue;  			}  			if (arg == "-dff") {  				dff_mode = true; +				exe_cmd << " " << arg;  				continue;  			}  			if (arg == "-nocleanup") {  				cleanup = false;  				continue;  			} -			if (arg == "-showtmp") { -				show_tempdir = true; -				continue; -			} -			if (arg == "-box" && argidx+1 < args.size()) { -				box_file = args[++argidx]; -				continue; -			} -			if (arg == "-W" && argidx+1 < args.size()) { -				wire_delay = "-W " + args[++argidx]; +			if (arg == "-run" && argidx+1 < args.size()) { +				size_t pos = args[argidx+1].find(':'); +				if (pos == std::string::npos) +					break; +				run_from = args[++argidx].substr(0, pos); +				run_to = args[argidx].substr(pos+1);  				continue;  			}  			break;  		}  		extra_args(args, argidx, design); -		rewrite_filename(script_file); -		if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') -			script_file = std::string(pwd) + "/" + script_file; - -		// handle -lut / -luts args -		if (!lut_arg.empty()) { -			string arg = lut_arg; -			if (arg.find_first_not_of("0123456789:") == std::string::npos) { -				size_t pos = arg.find_first_of(':'); -				int lut_mode = 0, lut_mode2 = 0; -				if (pos != string::npos) { -					lut_mode = atoi(arg.substr(0, pos).c_str()); -					lut_mode2 = atoi(arg.substr(pos+1).c_str()); -				} else { -					lut_mode = atoi(arg.c_str()); -					lut_mode2 = lut_mode; -				} -				lut_costs.clear(); -				for (int i = 0; i < lut_mode; i++) -					lut_costs.push_back(1); -				for (int i = lut_mode; i < lut_mode2; i++) -					lut_costs.push_back(2 << (i - lut_mode)); -			} -			else { -				lut_file = arg; -				rewrite_filename(lut_file); -				if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+') -					lut_file = std::string(pwd) + "/" + lut_file; -			} -		} -		if (!luts_arg.empty()) { -			lut_costs.clear(); -			for (auto &tok : split_tokens(luts_arg, ",")) { -				auto parts = split_tokens(tok, ":"); -				if (GetSize(parts) == 0 && !lut_costs.empty()) -					lut_costs.push_back(lut_costs.back()); -				else if (GetSize(parts) == 1) -					lut_costs.push_back(atoi(parts.at(0).c_str())); -				else if (GetSize(parts) == 2) -					while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) -						lut_costs.push_back(atoi(parts.at(1).c_str())); -				else -					log_cmd_error("Invalid -luts syntax.\n"); -			} +		log_assert(design); +		if (design->selected_modules().empty()) { +			log_warning("No modules selected for ABC9 techmapping.\n"); +			return;  		} -		// ABC expects a box file for XAIG -		if (box_file.empty()) -		    box_file = "+/dummy.box"; +		log_header(design, "Executing ABC9 pass.\n"); +		log_push(); -		rewrite_filename(box_file); -		if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') -		    box_file = std::string(pwd) + "/" + box_file; +		run_script(design, run_from, run_to); -		SigMap assign_map; -		CellTypes ct(design); -		for (auto module : design->selected_modules()) -		{ -			if (module->processes.size() > 0) { -				log("Skipping module %s as it contains processes.\n", log_id(module)); -				continue; +		log_pop(); +	} + +	void script() YS_OVERRIDE +	{ +		if (check_label("pre")) { +			run("scc -set_attr abc9_scc_id {}"); +			if (help_mode) +				run("abc9_ops -mark_scc -prep_xaiger [-dff]", "(option for -dff)"); +			else +				run("abc9_ops -mark_scc -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)"); +			run("select -set abc9_holes A:abc9_holes"); +			run("flatten -wb @abc9_holes"); +			run("techmap @abc9_holes"); +			if (dff_mode || help_mode) +				run("abc9_ops -prep_dff", "(only if -dff)"); +			run("opt -purge @abc9_holes"); +			run("aigmap"); +			run("wbflip @abc9_holes"); +		} + +		if (check_label("map")) { +			if (help_mode) { +				run("foreach module in selection"); +				run("    write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig"); +				run("    abc9_exe -cwd <abc-temp-dir> [options]"); +				run("    read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig"); +				run("    abc9_ops -reintegrate");  			} -			log_assert(!module->attributes.count(ID(abc9_box_id))); +			else { +				auto selected_modules = active_design->selected_modules(); +				active_design->selection_stack.emplace_back(false); -			if (!design->selected_whole_module(module)) -				log_error("Can't handle partially selected module %s!\n", log_id(module)); +				for (auto mod : selected_modules) { +					if (mod->processes.size() > 0) { +						log("Skipping module %s as it contains processes.\n", log_id(mod)); +						continue; +					} +					log_assert(!mod->attributes.count(ID(abc9_box_id))); -			assign_map.set(module); +					log_push(); +					active_design->selection().select(mod); -			typedef SigSpec clkdomain_t; -			dict<clkdomain_t, int> clk_to_mergeability; +					if (!active_design->selected_whole_module(mod)) +						log_error("Can't handle partially selected module %s!\n", log_id(mod)); -			if (dff_mode) -				for (auto cell : module->cells()) { -					if (cell->type != "$__ABC9_FF_") -						continue; +					std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; +					if (!cleanup) +						tempdir_name[0] = tempdir_name[4] = '_'; +					tempdir_name = make_temp_dir(tempdir_name); -					Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str())); -					if (abc9_clock_wire == NULL) -						log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); -					SigSpec abc9_clock = assign_map(abc9_clock_wire); +					run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); -					clkdomain_t key(abc9_clock); +					int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs"); -					auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1)); -					auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second)); -					log_assert(r2.second); +					log("Extracted %d AND gates and %d wires from module `%s' to a netlist network with %d inputs and %d outputs.\n", +							active_design->scratchpad_get_int("write_xaiger.num_ands"), +							active_design->scratchpad_get_int("write_xaiger.num_wires"), +							log_id(mod), +							active_design->scratchpad_get_int("write_xaiger.num_inputs"), +							num_outputs); +					if (num_outputs) { +						run(stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str())); +						run(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str())); +						run("abc9_ops -reintegrate"); +					} +					else +						log("Don't call ABC as there is nothing to map.\n"); -					Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str())); -					if (abc9_init_wire == NULL) -						log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); -					log_assert(GetSize(abc9_init_wire) == 1); -					SigSpec abc9_init = assign_map(abc9_init_wire); -					if (!abc9_init.is_fully_const()) -						log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); -					if (abc9_init == State::S1) -						log_error("'%s.init' in module '%s' has value 1'b1 which is not supported by 'abc9 -dff'.\n", cell->name.c_str(), log_id(module)); -					r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const())); -					log_assert(r2.second); -				} -			else -				for (auto cell : module->cells()) { -					auto inst_module = design->module(cell->type); -					if (!inst_module || !inst_module->get_bool_attribute("\\abc9_flop")) -						continue; -					cell->set_bool_attribute("\\abc9_keep"); +					if (cleanup) { +						log("Removing temp directory.\n"); +						remove_directory(tempdir_name); +					} + +					active_design->selection().selected_modules.clear(); +					log_pop();  				} -			design->selected_active_module = module->name.str(); -			abc9_module(design, module, script_file, exe_file, cleanup, lut_costs, dff_mode, -					delay_target, lutin_shared, fast_mode, show_tempdir, -					box_file, lut_file, wire_delay); -			design->selected_active_module.clear(); +				active_design->selection_stack.pop_back(); +			}  		} - -		log_pop();  	}  } Abc9Pass; diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc new file mode 100644 index 000000000..01bf46539 --- /dev/null +++ b/passes/techmap/abc9_exe.cc @@ -0,0 +1,531 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *                2019  Eddie Hung <eddie@fpgeh.com> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// [[CITE]] ABC +// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification +// http://www.eecs.berkeley.edu/~alanmi/abc/ + +#include "kernel/register.h" +#include "kernel/log.h" + +#ifndef _WIN32 +#  include <unistd.h> +#  include <dirent.h> +#endif + +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + +std::string fold_abc9_cmd(std::string str) +{ +	std::string token, new_str = "          "; +	int char_counter = 10; + +	for (size_t i = 0; i <= str.size(); i++) { +		if (i < str.size()) +			token += str[i]; +		if (i == str.size() || str[i] == ';') { +			if (char_counter + token.size() > 75) +				new_str += "\n              ", char_counter = 14; +			new_str += token, char_counter += token.size(); +			token.clear(); +		} +	} + +	return new_str; +} + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +std::string add_echos_to_abc9_cmd(std::string str) +{ +	std::string new_str, token; +	for (size_t i = 0; i < str.size(); i++) { +		token += str[i]; +		if (str[i] == ';') { +			while (i+1 < str.size() && str[i+1] == ' ') +				i++; +			new_str += "echo + " + token + " " + token + " "; +			token.clear(); +		} +	} + +	if (!token.empty()) { +		if (!new_str.empty()) +			new_str += "echo + " + token + "; "; +		new_str += token; +	} + +	return new_str; +} + +std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +{ +	if (show_tempdir) +		return text; + +	while (1) { +		size_t pos = text.find(tempdir_name); +		if (pos == std::string::npos) +			break; +		text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); +	} + +	std::string  selfdir_name = proc_self_dirname(); +	if (selfdir_name != "/") { +		while (1) { +			size_t pos = text.find(selfdir_name); +			if (pos == std::string::npos) +				break; +			text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); +		} +	} + +	return text; +} + +struct abc9_output_filter +{ +	bool got_cr; +	int escape_seq_state; +	std::string linebuf; +	std::string tempdir_name; +	bool show_tempdir; + +	abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) +	{ +		got_cr = false; +		escape_seq_state = 0; +	} + +	void next_char(char ch) +	{ +		if (escape_seq_state == 0 && ch == '\033') { +			escape_seq_state = 1; +			return; +		} +		if (escape_seq_state == 1) { +			escape_seq_state = ch == '[' ? 2 : 0; +			return; +		} +		if (escape_seq_state == 2) { +			if ((ch < '0' || '9' < ch) && ch != ';') +				escape_seq_state = 0; +			return; +		} +		escape_seq_state = 0; +		if (ch == '\r') { +			got_cr = true; +			return; +		} +		if (ch == '\n') { +			log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); +			got_cr = false, linebuf.clear(); +			return; +		} +		if (got_cr) +			got_cr = false, linebuf.clear(); +		linebuf += ch; +	} + +	void next_line(const std::string &line) +	{ +		//int pi, po; +		//if (sscanf(line.c_str(), "Start-point = pi%d.  End-point = po%d.", &pi, &po) == 2) { +		//	log("ABC: Start-point = pi%d (%s).  End-point = po%d (%s).\n", +		//			pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", +		//			po, po_map.count(po) ? po_map.at(po).c_str() : "???"); +		//	return; +		//} + +		for (char ch : line) +			next_char(ch); +	} +}; + +void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file, +		vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, +		bool show_tempdir, std::string box_file, std::string lut_file, +		std::string wire_delay, std::string tempdir_name +) +{ +	std::string abc9_script; + +	if (!lut_costs.empty()) +		abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); +	else if (!lut_file.empty()) +		abc9_script += stringf("read_lut %s; ", lut_file.c_str()); +	else +		log_abort(); + +	log_assert(!box_file.empty()); +	abc9_script += stringf("read_box %s; ", box_file.c_str()); +	abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); + +	if (!script_file.empty()) { +		if (script_file[0] == '+') { +			for (size_t i = 1; i < script_file.size(); i++) +				if (script_file[i] == '\'') +					abc9_script += "'\\''"; +				else if (script_file[i] == ',') +					abc9_script += " "; +				else +					abc9_script += script_file[i]; +		} else +			abc9_script += stringf("source %s", script_file.c_str()); +	} else if (!lut_costs.empty() || !lut_file.empty()) { +		abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos) +			: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos); +	} else +		log_abort(); + +	for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) +		abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); + +	//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos)) +	//	abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3); + +	for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) +		abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); + +	std::string C; +	if (design->scratchpad.count("abc9.if.C")) +		C = "-C " + design->scratchpad_get_string("abc9.if.C"); +	for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos)) +		abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3); + +	std::string R; +	if (design->scratchpad.count("abc9.if.R")) +		R = "-R " + design->scratchpad_get_string("abc9.if.R"); +	for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos)) +		abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3); + +	abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str()); +	if (design->scratchpad_get_bool("abc9.verify")) { +		if (dff_mode) +			abc9_script += "; verify -s"; +		else +			abc9_script += "; verify"; +	} +	abc9_script += "; time"; +	abc9_script = add_echos_to_abc9_cmd(abc9_script); + +	for (size_t i = 0; i+1 < abc9_script.size(); i++) +		if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') +			abc9_script[i+1] = '\n'; + +	FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); +	fprintf(f, "%s\n", abc9_script.c_str()); +	fclose(f); + +	std::string buffer; + +	log_header(design, "Executing ABC9.\n"); + +	if (!lut_costs.empty()) { +		buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); +		f = fopen(buffer.c_str(), "wt"); +		if (f == NULL) +			log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); +		for (int i = 0; i < GetSize(lut_costs); i++) +			fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); +		fclose(f); +	} + +	buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); +	log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + +#ifndef YOSYS_LINK_ABC +	abc9_output_filter filt(tempdir_name, show_tempdir); +	int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); +#else +	// These needs to be mutable, supposedly due to getopt +	char *abc9_argv[5]; +	string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); +	abc9_argv[0] = strdup(exe_file.c_str()); +	abc9_argv[1] = strdup("-s"); +	abc9_argv[2] = strdup("-f"); +	abc9_argv[3] = strdup(tmp_script_name.c_str()); +	abc9_argv[4] = 0; +	int ret = Abc_RealMain(4, abc9_argv); +	free(abc9_argv[0]); +	free(abc9_argv[1]); +	free(abc9_argv[2]); +	free(abc9_argv[3]); +#endif +	if (ret != 0) +		log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); +} + +struct Abc9ExePass : public Pass { +	Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    abc9_exe [options]\n"); +		log("\n"); +		log(" \n"); +		log("This pass uses the ABC tool [1] for technology mapping of the top module\n"); +		log("(according to the (* top *) attribute or if only one module is currently selected)\n"); +		log("to a target FPGA architecture.\n"); +		log("\n"); +		log("    -exe <command>\n"); +#ifdef ABCEXTERNAL +		log("        use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else +		log("        use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif +		log("        This can e.g. be used to call a specific version of ABC or a wrapper.\n"); +		log("\n"); +		log("    -script <file>\n"); +		log("        use the specified ABC script file instead of the default script.\n"); +		log("\n"); +		log("        if <file> starts with a plus sign (+), then the rest of the filename\n"); +		log("        string is interpreted as the command string to be passed to ABC. The\n"); +		log("        leading plus sign is removed and all commas (,) in the string are\n"); +		log("        replaced with blanks before the string is passed to ABC.\n"); +		log("\n"); +		log("        if no -script parameter is given, the following scripts are used:\n"); +		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str()); +		log("\n"); +		log("    -fast\n"); +		log("        use different default scripts that are slightly faster (at the cost\n"); +		log("        of output quality):\n"); +		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str()); +		log("\n"); +		log("    -D <picoseconds>\n"); +		log("        set delay target. the string {D} in the default scripts above is\n"); +		log("        replaced by this option when used, and an empty string otherwise\n"); +		log("        (indicating best possible delay).\n"); +		log("\n"); +//		log("    -S <num>\n"); +//		log("        maximum number of LUT inputs shared.\n"); +//		log("        (replaces {S} in the default scripts above, default: -S 1)\n"); +//		log("\n"); +		log("    -lut <width>\n"); +		log("        generate netlist using luts of (max) the specified width.\n"); +		log("\n"); +		log("    -lut <w1>:<w2>\n"); +		log("        generate netlist using luts of (max) the specified width <w2>. All\n"); +		log("        luts with width <= <w1> have constant cost. for luts larger than <w1>\n"); +		log("        the area cost doubles with each additional input bit. the delay cost\n"); +		log("        is still constant for all lut widths.\n"); +		log("\n"); +		log("    -lut <file>\n"); +		log("        pass this file with lut library to ABC.\n"); +		log("\n"); +		log("    -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n"); +		log("        generate netlist using luts. Use the specified costs for luts with 1,\n"); +		log("        2, 3, .. inputs.\n"); +		log("\n"); +		log("    -showtmp\n"); +		log("        print the temp dir name in log. usually this is suppressed so that the\n"); +		log("        command output is identical across runs.\n"); +		log("\n"); +		log("    -box <file>\n"); +		log("        pass this file with box library to ABC.\n"); +		log("\n"); +		log("    -cwd <dir>\n"); +		log("        use this as the current working directory, inside which the 'input.xaig'\n"); +		log("        file is expected. temporary files will be created in this directory, and\n"); +		log("        the mapped result will be written to 'output.aig'.\n"); +		log("\n"); +		log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); +		log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); +		log("ABC on logic snippets extracted from your design. You will not get any useful\n"); +		log("output when passing an ABC script that writes a file. Instead write your full\n"); +		log("design as BLIF file with write_blif and then load that into ABC externally if\n"); +		log("you want to use ABC to convert your design into another format.\n"); +		log("\n"); +		log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n"); + +#ifdef ABCEXTERNAL +		std::string exe_file = ABCEXTERNAL; +#else +		std::string exe_file = proc_self_dirname() + "yosys-abc"; +#endif +		std::string script_file, clk_str, box_file, lut_file; +		std::string delay_target, lutin_shared = "-S 1", wire_delay; +		std::string tempdir_name; +		bool fast_mode = false, dff_mode = false; +		bool show_tempdir = false; +		vector<int> lut_costs; + +#if 0 +		cleanup = false; +		show_tempdir = true; +#endif + +#ifdef _WIN32 +#ifndef ABCEXTERNAL +		if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) +			exe_file = proc_self_dirname() + "..\\yosys-abc"; +#endif +#endif + +		std::string lut_arg, luts_arg; +		exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */); +		script_file = design->scratchpad_get_string("abc9.script", script_file); +		if (design->scratchpad.count("abc9.D")) { +			delay_target = "-D " + design->scratchpad_get_string("abc9.D"); +		} +		lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); +		luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); +		fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode); +		dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode); +		show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); +		box_file = design->scratchpad_get_string("abc9.box", box_file); +		if (design->scratchpad.count("abc9.W")) { +			wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); +		} + +		size_t argidx; +		char pwd [PATH_MAX]; +		if (!getcwd(pwd, sizeof(pwd))) { +			log_cmd_error("getcwd failed: %s\n", strerror(errno)); +			log_abort(); +		} +		for (argidx = 1; argidx < args.size(); argidx++) { +			std::string arg = args[argidx]; +			if (arg == "-exe" && argidx+1 < args.size()) { +				exe_file = args[++argidx]; +				continue; +			} +			if (arg == "-script" && argidx+1 < args.size()) { +				script_file = args[++argidx]; +				continue; +			} +			if (arg == "-D" && argidx+1 < args.size()) { +				delay_target = "-D " + args[++argidx]; +				continue; +			} +			//if (arg == "-S" && argidx+1 < args.size()) { +			//	lutin_shared = "-S " + args[++argidx]; +			//	continue; +			//} +			if (arg == "-lut" && argidx+1 < args.size()) { +				lut_arg = args[++argidx]; +				continue; +			} +			if (arg == "-luts" && argidx+1 < args.size()) { +				lut_arg = args[++argidx]; +				continue; +			} +			if (arg == "-fast") { +				fast_mode = true; +				continue; +			} +			if (arg == "-dff") { +				dff_mode = true; +				continue; +			} +			if (arg == "-showtmp") { +				show_tempdir = true; +				continue; +			} +			if (arg == "-box" && argidx+1 < args.size()) { +				box_file = args[++argidx]; +				continue; +			} +			if (arg == "-W" && argidx+1 < args.size()) { +				wire_delay = "-W " + args[++argidx]; +				continue; +			} +			if (arg == "-cwd" && argidx+1 < args.size()) { +				tempdir_name = args[++argidx]; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		rewrite_filename(script_file); +		if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') +			script_file = std::string(pwd) + "/" + script_file; + +		// handle -lut / -luts args +		if (!lut_arg.empty()) { +			string arg = lut_arg; +			if (arg.find_first_not_of("0123456789:") == std::string::npos) { +				size_t pos = arg.find_first_of(':'); +				int lut_mode = 0, lut_mode2 = 0; +				if (pos != string::npos) { +					lut_mode = atoi(arg.substr(0, pos).c_str()); +					lut_mode2 = atoi(arg.substr(pos+1).c_str()); +				} else { +					lut_mode = atoi(arg.c_str()); +					lut_mode2 = lut_mode; +				} +				lut_costs.clear(); +				for (int i = 0; i < lut_mode; i++) +					lut_costs.push_back(1); +				for (int i = lut_mode; i < lut_mode2; i++) +					lut_costs.push_back(2 << (i - lut_mode)); +			} +			else { +				lut_file = arg; +				rewrite_filename(lut_file); +				if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+') +					lut_file = std::string(pwd) + "/" + lut_file; +			} +		} +		if (!luts_arg.empty()) { +			lut_costs.clear(); +			for (auto &tok : split_tokens(luts_arg, ",")) { +				auto parts = split_tokens(tok, ":"); +				if (GetSize(parts) == 0 && !lut_costs.empty()) +					lut_costs.push_back(lut_costs.back()); +				else if (GetSize(parts) == 1) +					lut_costs.push_back(atoi(parts.at(0).c_str())); +				else if (GetSize(parts) == 2) +					while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) +						lut_costs.push_back(atoi(parts.at(1).c_str())); +				else +					log_cmd_error("Invalid -luts syntax.\n"); +			} +		} + +		// ABC expects a box file for XAIG +		if (box_file.empty()) +			box_file = "+/dummy.box"; + +		rewrite_filename(box_file); +		if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') +			box_file = std::string(pwd) + "/" + box_file; + +		if (tempdir_name.empty()) +			log_cmd_error("abc9_exe '-cwd' option is mandatory.\n"); + + +		abc9_module(design, script_file, exe_file, lut_costs, dff_mode, +				delay_target, lutin_shared, fast_mode, show_tempdir, +				box_file, lut_file, wire_delay, tempdir_name); +	} +} Abc9ExePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc new file mode 100644 index 000000000..9ad29a8f6 --- /dev/null +++ b/passes/techmap/abc9_ops.cc @@ -0,0 +1,825 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *                2019  Eddie Hung <eddie@fpgeh.com> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" +#include "kernel/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +int map_autoidx; + +inline std::string remap_name(RTLIL::IdString abc9_name) +{ +	return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); +} + +void mark_scc(RTLIL::Module *module) +{ +	// For every unique SCC found, (arbitrarily) find the first +	//   cell in the component, and convert all wires driven by +	//   its output ports into a new PO, and drive its previous +	//   sinks with a new PI +	pool<RTLIL::Const> ids_seen; +	for (auto cell : module->cells()) { +		auto it = cell->attributes.find(ID(abc9_scc_id)); +		if (it == cell->attributes.end()) +			continue; +		auto id = it->second; +		auto r = ids_seen.insert(id); +		cell->attributes.erase(it); +		if (!r.second) +			continue; +		for (auto &c : cell->connections_) { +			if (c.second.is_fully_const()) continue; +			if (cell->output(c.first)) { +				SigBit b = c.second.as_bit(); +				Wire *w = b.wire; +				w->set_bool_attribute(ID::keep); +				w->attributes[ID(abc9_scc_id)] = id.as_int(); +			} +		} +	} + +	module->fixup_ports(); +} + +void prep_dff(RTLIL::Module *module) +{ +	auto design = module->design; +	log_assert(design); + +	SigMap assign_map(module); + +	typedef SigSpec clkdomain_t; +	dict<clkdomain_t, int> clk_to_mergeability; + +	for (auto cell : module->cells()) { +		if (cell->type != "$__ABC9_FF_") +			continue; + +		Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str())); +		if (abc9_clock_wire == NULL) +			log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		SigSpec abc9_clock = assign_map(abc9_clock_wire); + +		clkdomain_t key(abc9_clock); + +		auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1)); +		auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second)); +		log_assert(r2.second); + +		Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str())); +		if (abc9_init_wire == NULL) +			log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		log_assert(GetSize(abc9_init_wire) == 1); +		SigSpec abc9_init = assign_map(abc9_init_wire); +		if (!abc9_init.is_fully_const()) +			log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		if (abc9_init == State::S1) +			log_error("'%s.init' in module '%s' has value 1'b1 which is not supported by 'abc9 -dff'.\n", cell->name.c_str(), log_id(module)); +		r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const())); +		log_assert(r2.second); +	} + +	RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str())); +	if (holes_module) { +		SigMap sigmap(holes_module); + +		dict<SigSpec, SigSpec> replace; +		for (auto cell : holes_module->cells().to_vector()) { +			if (!cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", +						"$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) +				continue; +			SigBit D = cell->getPort("\\D"); +			SigBit Q = cell->getPort("\\Q"); +			// Emulate async control embedded inside $_DFF_* cell with mux in front of D +			if (cell->type.in("$_DFF_NN0_", "$_DFF_PN0_")) +				D = holes_module->MuxGate(NEW_ID, State::S0, D, cell->getPort("\\R")); +			else if (cell->type.in("$_DFF_NN1_", "$_DFF_PN1_")) +				D = holes_module->MuxGate(NEW_ID, State::S1, D, cell->getPort("\\R")); +			else if (cell->type.in("$_DFF_NP0_", "$_DFF_PP0_")) +				D = holes_module->MuxGate(NEW_ID, D, State::S0, cell->getPort("\\R")); +			else if (cell->type.in("$_DFF_NP1_", "$_DFF_PP1_")) +				D = holes_module->MuxGate(NEW_ID, D, State::S1, cell->getPort("\\R")); +			// Remove the $_DFF_* cell from what needs to be a combinatorial box +			holes_module->remove(cell); +			Wire *port; +			if (GetSize(Q.wire) == 1) +				port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str())); +			else +				port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset)); +			log_assert(port); +			// Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;" +			//   in order to extract just the combinatorial control logic that feeds the box +			//   (i.e. clock enable, synchronous reset, etc.) +			replace.insert(std::make_pair(Q,D)); +			// Since `flatten` above would have created wires named "<cell>.Q", +			//   extract the pre-techmap cell name +			auto pos = Q.wire->name.str().rfind("."); +			log_assert(pos != std::string::npos); +			IdString driver = Q.wire->name.substr(0, pos); +			// And drive the signal that was previously driven by "DFF.Q" (typically +			//   used to implement clock-enable functionality) with the "<cell>.$abc9_currQ" +			//   wire (which itself is driven an by input port) we inserted above +			Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str())); +			log_assert(currQ); +			holes_module->connect(Q, currQ); +		} + +		for (auto &conn : holes_module->connections_) +			conn.second = replace.at(sigmap(conn.second), conn.second); +	} +} + +void prep_xaiger(RTLIL::Module *module, bool dff) +{ +	auto design = module->design; +	log_assert(design); + +	SigMap sigmap(module); + +	dict<SigBit, pool<IdString>> bit_drivers, bit_users; +	TopoSort<IdString, RTLIL::sort_by_id_str> toposort; +	dict<IdString, std::vector<IdString>> box_ports; + +	for (auto cell : module->cells()) { +		if (cell->type == "$__ABC9_FF_") +			continue; +		if (cell->has_keep_attr()) +			continue; + +		auto inst_module = module->design->module(cell->type); +		bool abc9_box = inst_module && inst_module->attributes.count("\\abc9_box_id"); +		bool abc9_flop = false; +		if (abc9_box) { +			abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); +			if (abc9_flop && !dff) +				continue; + +			auto r = box_ports.insert(cell->type); +			if (r.second) { +				// Make carry in the last PI, and carry out the last PO +				//   since ABC requires it this way +				IdString carry_in, carry_out; +				for (const auto &port_name : inst_module->ports) { +					auto w = inst_module->wire(port_name); +					log_assert(w); +					if (w->get_bool_attribute("\\abc9_carry")) { +						if (w->port_input) { +							if (carry_in != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(inst_module)); +							carry_in = port_name; +						} +						if (w->port_output) { +							if (carry_out != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(inst_module)); +							carry_out = port_name; +						} +					} +					else +						r.first->second.push_back(port_name); +				} + +				if (carry_in != IdString() && carry_out == IdString()) +					log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(inst_module)); +				if (carry_in == IdString() && carry_out != IdString()) +					log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(inst_module)); +				if (carry_in != IdString()) { +					r.first->second.push_back(carry_in); +					r.first->second.push_back(carry_out); +				} +			} +		} +		else if (!yosys_celltypes.cell_known(cell->type)) +			continue; + +		// TODO: Speed up toposort -- we care about box ordering only +		for (auto conn : cell->connections()) { +			if (cell->input(conn.first)) +				for (auto bit : sigmap(conn.second)) +					bit_users[bit].insert(cell->name); + +			if (cell->output(conn.first) && !abc9_flop) +				for (auto bit : sigmap(conn.second)) +					bit_drivers[bit].insert(cell->name); +		} +		toposort.node(cell->name); +	} + +	if (box_ports.empty()) +		return; + +	for (auto &it : bit_users) +		if (bit_drivers.count(it.first)) +			for (auto driver_cell : bit_drivers.at(it.first)) +			for (auto user_cell : it.second) +				toposort.edge(driver_cell, user_cell); + +	if (ys_debug(1)) +		toposort.analyze_loops = true; + +	bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); + +	if (ys_debug(1)) { +		unsigned i = 0; +		for (auto &it : toposort.loops) { +			log("  loop %d\n", i++); +			for (auto cell_name : it) { +				auto cell = module->cell(cell_name); +				log_assert(cell); +				log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); +			} +		} +	} + +	log_assert(no_loops); + +	RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str())); +	log_assert(holes_module); +	holes_module->set_bool_attribute("\\abc9_holes"); + +	dict<IdString, Cell*> cell_cache; + +	int port_id = 1, box_count = 0; +	for (auto cell_name : toposort.sorted) { +		RTLIL::Cell *cell = module->cell(cell_name); +		log_assert(cell); + +		RTLIL::Module* box_module = design->module(cell->type); +		if (!box_module || !box_module->attributes.count("\\abc9_box_id")) +			continue; + +		cell->attributes["\\abc9_box_seq"] = box_count++; + +		IdString derived_name = box_module->derive(design, cell->parameters); +		box_module = design->module(derived_name); + +		auto r = cell_cache.insert(derived_name); +		auto &holes_cell = r.first->second; +		if (r.second) { +			if (box_module->has_processes()) +				Pass::call_on_module(design, box_module, "proc"); + +			if (box_module->get_bool_attribute("\\whitebox")) { +				holes_cell = holes_module->addCell(cell->name, derived_name); + +				int box_inputs = 0; +				for (auto port_name : box_ports.at(cell->type)) { +					RTLIL::Wire *w = box_module->wire(port_name); +					log_assert(w); +					log_assert(!w->port_input || !w->port_output); +					auto &conn = holes_cell->connections_[port_name]; +					if (w->port_input) { +						for (int i = 0; i < GetSize(w); i++) { +							box_inputs++; +							RTLIL::Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); +							if (!holes_wire) { +								holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); +								holes_wire->port_input = true; +								holes_wire->port_id = port_id++; +								holes_module->ports.push_back(holes_wire->name); +							} +							conn.append(holes_wire); +						} +					} +					else if (w->port_output) +						conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w)); +				} + +				// For flops only, create an extra 1-bit input that drives a new wire +				//   called "<cell>.abc9_ff.Q" that is used below +				if (box_module->get_bool_attribute("\\abc9_flop")) { +					box_inputs++; +					Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); +					if (!holes_wire) { +						holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); +						holes_wire->port_input = true; +						holes_wire->port_id = port_id++; +						holes_module->ports.push_back(holes_wire->name); +					} +					Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str())); +					holes_module->connect(Q, holes_wire); +				} +			} +			else // box_module is a blackbox +				log_assert(holes_cell == nullptr); +		} + +		for (auto port_name : box_ports.at(cell->type)) { +			RTLIL::Wire *w = box_module->wire(port_name); +			log_assert(w); +			if (!w->port_output) +				continue; +			Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w)); +			holes_wire->port_output = true; +			holes_wire->port_id = port_id++; +			holes_module->ports.push_back(holes_wire->name); +			if (holes_cell) // whitebox +				holes_module->connect(holes_wire, holes_cell->getPort(port_name)); +			else // blackbox +				holes_module->connect(holes_wire, Const(State::S0, GetSize(w))); +		} +	} +} + +void reintegrate(RTLIL::Module *module) +{ +	auto design = module->design; +	log_assert(design); + +	map_autoidx = autoidx++; + +	RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str())); +	if (mapped_mod == NULL) +		log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module)); + +	for (auto w : mapped_mod->wires()) +		module->addWire(remap_name(w->name), GetSize(w)); + +	dict<IdString,std::vector<IdString>> box_ports; + +	for (auto m : design->modules()) { +		if (!m->attributes.count(ID(abc9_box_id))) +			continue; + +		auto r = box_ports.insert(m->name); +		if (r.second) { +			// Make carry in the last PI, and carry out the last PO +			//   since ABC requires it this way +			IdString carry_in, carry_out; +			for (const auto &port_name : m->ports) { +				auto w = m->wire(port_name); +				log_assert(w); +				if (w->get_bool_attribute("\\abc9_carry")) { +					if (w->port_input) { +						if (carry_in != IdString()) +							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); +						carry_in = port_name; +					} +					if (w->port_output) { +						if (carry_out != IdString()) +							log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(m)); +						carry_out = port_name; +					} +				} +				else +					r.first->second.push_back(port_name); +			} + +			if (carry_in != IdString() && carry_out == IdString()) +				log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m)); +			if (carry_in == IdString() && carry_out != IdString()) +				log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m)); +			if (carry_in != IdString()) { +				r.first->second.push_back(carry_in); +				r.first->second.push_back(carry_out); +			} +		} +	} + +	std::vector<Cell*> boxes; +	for (auto cell : module->cells().to_vector()) { +		if (cell->has_keep_attr()) +			continue; +		if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) +			module->remove(cell); +		else if (cell->attributes.erase("\\abc9_box_seq")) +			boxes.emplace_back(cell); +	} + +	dict<SigBit, pool<IdString>> bit_drivers, bit_users; +	TopoSort<IdString, RTLIL::sort_by_id_str> toposort; +	dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; +	dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; + +	std::map<IdString, int> cell_stats; +	for (auto mapped_cell : mapped_mod->cells()) +	{ +		// TODO: Speed up toposort -- we care about NOT ordering only +		toposort.node(mapped_cell->name); + +		if (mapped_cell->type == ID($_NOT_)) { +			RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A); +			RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); +			bit_users[a_bit].insert(mapped_cell->name); +			// Ignore inouts for topo ordering +			if (y_bit.wire && !(y_bit.wire->port_input && y_bit.wire->port_output)) +				bit_drivers[y_bit].insert(mapped_cell->name); + +			if (!a_bit.wire) { +				mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); +				RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); +				log_assert(wire); +				module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); +			} +			else { +				RTLIL::Cell* driver_lut = nullptr; +				// ABC can return NOT gates that drive POs +				if (!a_bit.wire->port_input) { +					// If it's not a NOT gate that that comes from a PI directly, +					// find the driver LUT and clone that to guarantee that we won't +					// increase the max logic depth +					// (TODO: Optimise by not cloning unless will increase depth) +					RTLIL::IdString driver_name; +					if (GetSize(a_bit.wire) == 1) +						driver_name = stringf("$lut%s", a_bit.wire->name.c_str()); +					else +						driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset); +					driver_lut = mapped_mod->cell(driver_name); +				} + +				if (!driver_lut) { +					// If a driver couldn't be found (could be from PI or box CI) +					// then implement using a LUT +					RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())), +							RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), +							RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), +							RTLIL::Const::from_string("01")); +					bit2sinks[cell->getPort(ID::A)].push_back(cell); +					cell_stats[ID($lut)]++; +				} +				else +					not2drivers[mapped_cell] = driver_lut; +			} +			continue; +		} + +		if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { +			// Convert buffer into direct connection +			if (mapped_cell->type == ID($lut) && +					GetSize(mapped_cell->getPort(ID::A)) == 1 && +					mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { +				SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name)); +				SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name)); +				module->connect(my_y, my_a); +				log_abort(); +				continue; +			} +			RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); +			cell->parameters = mapped_cell->parameters; +			cell->attributes = mapped_cell->attributes; + +			for (auto &mapped_conn : mapped_cell->connections()) { +				RTLIL::SigSpec newsig; +				for (auto c : mapped_conn.second.chunks()) { +					if (c.width == 0) +						continue; +					//log_assert(c.width == 1); +					if (c.wire) +						c.wire = module->wires_.at(remap_name(c.wire->name)); +					newsig.append(c); +				} +				cell->setPort(mapped_conn.first, newsig); + +				if (cell->input(mapped_conn.first)) { +					for (auto i : newsig) +						bit2sinks[i].push_back(cell); +					for (auto i : mapped_conn.second) +						bit_users[i].insert(mapped_cell->name); +				} +				if (cell->output(mapped_conn.first)) +					for (auto i : mapped_conn.second) +						// Ignore inouts for topo ordering +						if (i.wire && !(i.wire->port_input && i.wire->port_output)) +							bit_drivers[i].insert(mapped_cell->name); +			} +		} +		else { +			RTLIL::Cell *existing_cell = module->cell(mapped_cell->name); +			log_assert(existing_cell); + +			RTLIL::Module* box_module = design->module(existing_cell->type); +			auto it = box_module->attributes.find(ID(abc9_box_id)); +			log_assert(it != box_module->attributes.end()); +			log_assert(mapped_cell->type == stringf("$__boxid%d", it->second.as_int())); +			mapped_cell->type = existing_cell->type; + +			RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); +			cell->parameters = existing_cell->parameters; +			cell->attributes = existing_cell->attributes; +			module->swap_names(cell, existing_cell); + +			auto jt = mapped_cell->connections_.find("\\i"); +			log_assert(jt != mapped_cell->connections_.end()); +			SigSpec inputs = std::move(jt->second); +			mapped_cell->connections_.erase(jt); +			jt = mapped_cell->connections_.find("\\o"); +			log_assert(jt != mapped_cell->connections_.end()); +			SigSpec outputs = std::move(jt->second); +			mapped_cell->connections_.erase(jt); + +			auto abc9_flop = box_module->attributes.count("\\abc9_flop"); +			if (!abc9_flop) { +				for (const auto &i : inputs) +					bit_users[i].insert(mapped_cell->name); +				for (const auto &i : outputs) +					// Ignore inouts for topo ordering +					if (i.wire && !(i.wire->port_input && i.wire->port_output)) +						bit_drivers[i].insert(mapped_cell->name); +			} + +			int input_count = 0, output_count = 0; +			for (const auto &port_name : box_ports.at(cell->type)) { +				RTLIL::Wire *w = box_module->wire(port_name); +				log_assert(w); + +				SigSpec sig; +				if (w->port_input) { +					sig = inputs.extract(input_count, GetSize(w)); +					input_count += GetSize(w); +				} +				if (w->port_output) { +					sig = outputs.extract(output_count, GetSize(w)); +					output_count += GetSize(w); +				} + +				SigSpec newsig; +				for (auto c : sig.chunks()) { +					if (c.width == 0) +						continue; +					//log_assert(c.width == 1); +					if (c.wire) +						c.wire = module->wires_.at(remap_name(c.wire->name)); +					newsig.append(c); +				} +				cell->setPort(port_name, newsig); + +				if (w->port_input && !abc9_flop) +					for (const auto &i : newsig) +						bit2sinks[i].push_back(cell); +			} +		} + +		cell_stats[mapped_cell->type]++; +	} + +	for (auto cell : boxes) +		module->remove(cell); + +	// Copy connections (and rename) from mapped_mod to module +	for (auto conn : mapped_mod->connections()) { +		if (!conn.first.is_fully_const()) { +			auto chunks = conn.first.chunks(); +			for (auto &c : chunks) +				c.wire = module->wires_.at(remap_name(c.wire->name)); +			conn.first = std::move(chunks); +		} +		if (!conn.second.is_fully_const()) { +			auto chunks = conn.second.chunks(); +			for (auto &c : chunks) +				if (c.wire) +					c.wire = module->wires_.at(remap_name(c.wire->name)); +			conn.second = std::move(chunks); +		} +		module->connect(conn); +	} + +	for (auto &it : cell_stats) +		log("ABC RESULTS:   %15s cells: %8d\n", it.first.c_str(), it.second); +	int in_wires = 0, out_wires = 0; + +	// Stitch in mapped_mod's inputs/outputs into module +	for (auto port : mapped_mod->ports) { +		RTLIL::Wire *mapped_wire = mapped_mod->wire(port); +		RTLIL::Wire *wire = module->wire(port); +		log_assert(wire); +		if (wire->attributes.erase(ID(abc9_scc_id))) { +			auto r YS_ATTRIBUTE(unused) = wire->attributes.erase(ID::keep); +			log_assert(r); +		} +		RTLIL::Wire *remap_wire = module->wire(remap_name(port)); +		RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire)); +		log_assert(GetSize(signal) >= GetSize(remap_wire)); + +		RTLIL::SigSig conn; +		if (mapped_wire->port_output) { +			conn.first = signal; +			conn.second = remap_wire; +			out_wires++; +			module->connect(conn); +		} +		else if (mapped_wire->port_input) { +			conn.first = remap_wire; +			conn.second = signal; +			in_wires++; +			module->connect(conn); +		} +	} + +	// ABC9 will return $_NOT_ gates in its mapping (since they are +	//   treated as being "free"), in particular driving primary +	//   outputs (real primary outputs, or cells treated as blackboxes) +	//   or driving box inputs. +	// Instead of just mapping those $_NOT_ gates into 2-input $lut-s +	//   at an area and delay cost, see if it is possible to push +	//   this $_NOT_ into the driving LUT, or into all sink LUTs. +	// When this is not possible, (i.e. this signal drives two primary +	//   outputs, only one of which is complemented) and when the driver +	//   is a LUT, then clone the LUT so that it can be inverted without +	//   increasing depth/delay. +	for (auto &it : bit_users) +		if (bit_drivers.count(it.first)) +			for (auto driver_cell : bit_drivers.at(it.first)) +			for (auto user_cell : it.second) +				toposort.edge(driver_cell, user_cell); +	bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); +	log_assert(no_loops); + +	for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { +		RTLIL::Cell *not_cell = mapped_mod->cell(*ii); +		log_assert(not_cell); +		if (not_cell->type != ID($_NOT_)) +			continue; +		auto it = not2drivers.find(not_cell); +		if (it == not2drivers.end()) +			continue; +		RTLIL::Cell *driver_lut = it->second; +		RTLIL::SigBit a_bit = not_cell->getPort(ID::A); +		RTLIL::SigBit y_bit = not_cell->getPort(ID::Y); +		RTLIL::Const driver_mask; + +		a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); +		y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); + +		auto jt = bit2sinks.find(a_bit); +		if (jt == bit2sinks.end()) +			goto clone_lut; + +		for (auto sink_cell : jt->second) +			if (sink_cell->type != ID($lut)) +				goto clone_lut; + +		// Push downstream LUTs past inverter +		for (auto sink_cell : jt->second) { +			SigSpec A = sink_cell->getPort(ID::A); +			RTLIL::Const mask = sink_cell->getParam(ID(LUT)); +			int index = 0; +			for (; index < GetSize(A); index++) +				if (A[index] == a_bit) +					break; +			log_assert(index < GetSize(A)); +			int i = 0; +			while (i < GetSize(mask)) { +				for (int j = 0; j < (1 << index); j++) +					std::swap(mask[i+j], mask[i+j+(1 << index)]); +				i += 1 << (index+1); +			} +			A[index] = y_bit; +			sink_cell->setPort(ID::A, A); +			sink_cell->setParam(ID(LUT), mask); +		} + +		// Since we have rewritten all sinks (which we know +		// to be only LUTs) to be after the inverter, we can +		// go ahead and clone the LUT with the expectation +		// that the original driving LUT will become dangling +		// and get cleaned away +clone_lut: +		driver_mask = driver_lut->getParam(ID(LUT)); +		for (auto &b : driver_mask.bits) { +			if (b == RTLIL::State::S0) b = RTLIL::State::S1; +			else if (b == RTLIL::State::S1) b = RTLIL::State::S0; +		} +		auto cell = module->addLut(NEW_ID, +				driver_lut->getPort(ID::A), +				y_bit, +				driver_mask); +		for (auto &bit : cell->connections_.at(ID::A)) { +			bit.wire = module->wires_.at(remap_name(bit.wire->name)); +			bit2sinks[bit].push_back(cell); +		} +	} + +	//log("ABC RESULTS:        internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); +	log("ABC RESULTS:           input signals: %8d\n", in_wires); +	log("ABC RESULTS:          output signals: %8d\n", out_wires); + +	design->remove(mapped_mod); +} + +struct Abc9OpsPass : public Pass { +	Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    abc9_ops [options] [selection]\n"); +		log("\n"); +		log("This pass contains a set of supporting operations for use during ABC technology\n"); +		log("mapping, and is expected to be called in conjunction with other operations from\n"); +		log("the `abc9' script pass. Only fully-selected modules are supported.\n"); +		log("\n"); +		log("    -mark_scc\n"); +		log("        for an arbitrarily chosen cell in each unique SCC of each selected module\n"); +		log("        (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n"); +		log("        wires driven by this cell's outputs with a (* keep *) attribute in order\n"); +		log("        to break the SCC. this temporary attribute will be removed on -reintegrate.\n"); +		log("\n"); +		log("    -prep_xaiger\n"); +		log("        prepare the design for XAIGER output. this includes computing the\n"); +		log("        topological ordering of ABC9 boxes, as well as preparing the\n"); +		log("        '<module-name>$holes' module that contains the logic behaviour of ABC9\n"); +		log("        whiteboxes.\n"); +		log("\n"); +		log("    -dff\n"); +		log("        consider flop cells (those instantiating modules marked with (* abc9_flop *)\n"); +		log("        during -prep_xaiger.\n"); +		log("\n"); +		log("    -prep_dff\n"); +		log("        compute the clock domain and initial value of each flop in the design.\n"); +		log("        process the '$holes' module to support clock-enable functionality.\n"); +		log("\n"); +		log("    -reintegrate\n"); +		log("        for each selected module, re-intergrate the module '<module-name>$abc9'\n"); +		log("        by first recovering ABC9 boxes, and then stitching in the remaining primary\n"); +		log("        inputs and outputs.\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n"); + +		bool mark_scc_mode = false; +		bool prep_dff_mode = false; +		bool prep_xaiger_mode = false; +		bool reintegrate_mode = false; +		bool dff_mode = false; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			std::string arg = args[argidx]; +			if (arg == "-mark_scc") { +				mark_scc_mode = true; +				continue; +			} +			if (arg == "-prep_dff") { +				prep_dff_mode = true; +				continue; +			} +			if (arg == "-prep_xaiger") { +				prep_xaiger_mode = true; +				continue; +			} +			if (arg == "-reintegrate") { +				reintegrate_mode = true; +				continue; +			} +			if (arg == "-dff") { +				dff_mode = true; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		if (!(mark_scc_mode || prep_dff_mode || reintegrate_mode)) +			log_cmd_error("At least one of -mark_scc, -prep_{xaiger,dff}, -reintegrate must be specified.\n"); + +		if (dff_mode && !prep_xaiger_mode) +			log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n"); + +		for (auto mod : design->selected_modules()) { +			if (mod->get_bool_attribute("\\abc9_holes")) +				continue; + +			if (mod->processes.size() > 0) { +				log("Skipping module %s as it contains processes.\n", log_id(mod)); +				continue; +			} + +			if (!design->selected_whole_module(mod)) +				log_error("Can't handle partially selected module %s!\n", log_id(mod)); + +			if (mark_scc_mode) +				mark_scc(mod); +			if (prep_dff_mode) +				prep_dff(mod); +			if (prep_xaiger_mode) +				prep_xaiger(mod, dff_mode); +			if (reintegrate_mode) +				reintegrate(mod); +		} +	} +} Abc9OpsPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/xilinx/abc9_map.v b/techlibs/xilinx/abc9_map.v index 0652064cb..7dc027176 100644 --- a/techlibs/xilinx/abc9_map.v +++ b/techlibs/xilinx/abc9_map.v @@ -74,7 +74,7 @@  // (e) a special _TECHMAP_REPLACE_.abc9_ff.Q wire that will be used for feedback  //     into the (combinatorial) FD* cell to facilitate clock-enable behaviour -module FDRE (output Q, input C, CE, D, R); +module FDRE (output Q, (* techmap_autopurge *) input C, CE, D, R);    parameter [0:0] INIT = 1'b0;    parameter [0:0] IS_C_INVERTED = 1'b0;    parameter [0:0] IS_D_INVERTED = 1'b0; @@ -110,7 +110,7 @@ module FDRE (output Q, input C, CE, D, R);    wire [0:0] abc9_ff.init = 1'b0;    wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ;  endmodule -module FDRE_1 (output Q, input C, CE, D, R); +module FDRE_1 (output Q, (* techmap_autopurge *) input C, CE, D, R);    parameter [0:0] INIT = 1'b0;    wire QQ, $Q;    generate if (INIT == 1'b1) begin @@ -138,7 +138,7 @@ module FDRE_1 (output Q, input C, CE, D, R);    wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ;  endmodule -module FDSE (output Q, input C, CE, D, S); +module FDSE (output Q, (* techmap_autopurge *) input C, CE, D, S);    parameter [0:0] INIT = 1'b1;    parameter [0:0] IS_C_INVERTED = 1'b0;    parameter [0:0] IS_D_INVERTED = 1'b0; @@ -173,7 +173,7 @@ module FDSE (output Q, input C, CE, D, S);    wire [0:0] abc9_ff.init = 1'b0;    wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ;  endmodule -module FDSE_1 (output Q, input C, CE, D, S); +module FDSE_1 (output Q, (* techmap_autopurge *) input C, CE, D, S);    parameter [0:0] INIT = 1'b1;    wire QQ, $Q;    generate if (INIT == 1'b1) begin @@ -200,7 +200,7 @@ module FDSE_1 (output Q, input C, CE, D, S);    wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ;  endmodule -module FDCE (output Q, input C, CE, D, CLR); +module FDCE (output Q, (* techmap_autopurge *) input C, CE, D, CLR);    parameter [0:0] INIT = 1'b0;    parameter [0:0] IS_C_INVERTED = 1'b0;    parameter [0:0] IS_D_INVERTED = 1'b0; @@ -249,7 +249,7 @@ module FDCE (output Q, input C, CE, D, CLR);    wire [0:0] abc9_ff.init = 1'b0;    wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = $QQ;  endmodule -module FDCE_1 (output Q, input C, CE, D, CLR); +module FDCE_1 (output Q, (* techmap_autopurge *) input C, CE, D, CLR);    parameter [0:0] INIT = 1'b0;    wire QQ, $Q, $QQ;    generate if (INIT == 1'b1) begin @@ -288,7 +288,7 @@ module FDCE_1 (output Q, input C, CE, D, CLR);    wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = $QQ;  endmodule -module FDPE (output Q, input C, CE, D, PRE); +module FDPE (output Q, (* techmap_autopurge *) input C, CE, D, PRE);    parameter [0:0] INIT = 1'b1;    parameter [0:0] IS_C_INVERTED = 1'b0;    parameter [0:0] IS_D_INVERTED = 1'b0; @@ -335,7 +335,7 @@ module FDPE (output Q, input C, CE, D, PRE);    wire [0:0] abc9_ff.init = 1'b0;    wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = $QQ;  endmodule -module FDPE_1 (output Q, input C, CE, D, PRE); +module FDPE_1 (output Q, (* techmap_autopurge *) input C, CE, D, PRE);    parameter [0:0] INIT = 1'b1;    wire QQ, $Q, $QQ;    generate if (INIT == 1'b1) begin diff --git a/tests/simple_abc9/abc9.v b/tests/simple_abc9/abc9.v index 4dc5ad689..e5837d480 100644 --- a/tests/simple_abc9/abc9.v +++ b/tests/simple_abc9/abc9.v @@ -291,3 +291,19 @@ module abc9_test035(input clk, d, output reg [1:0] q);  always @(posedge clk) q[0] <= d;  always @(negedge clk) q[1] <= q[0];  endmodule + +module abc9_test036(input A, B, S, output [1:0] O); +  (* keep *) +  MUXF8 m  ( +    .I0(I0), +    .I1(I1), +    .O(O[0]), +    .S(S) +  ); +  MUXF8 m2  ( +    .I0(I0), +    .I1(I1), +    .O(O[1]), +    .S(S) +  ); +endmodule diff --git a/tests/techmap/abc9.ys b/tests/techmap/abc9.ys index 62b5dfef6..2140dde26 100644 --- a/tests/techmap/abc9.ys +++ b/tests/techmap/abc9.ys @@ -39,6 +39,35 @@ design -load gold  scratchpad -copy abc9.script.flow3 abc9.script  abc9 -lut 4 +design -reset +read_verilog <<EOT +module top(input a, b, output o); +(* keep *) wire w = a & b; +assign o = ~w; +endmodule +EOT + +simplemap +equiv_opt -assert abc9 -lut 4 +design -load postopt +select -assert-count 2 t:$lut + + +design -reset +read_verilog -icells <<EOT +module top(input a, b, output o); +wire w; +(* keep *) $_AND_ gate (.Y(w), .A(a), .B(b)); +assign o = ~w; +endmodule +EOT + +simplemap +equiv_opt -assert abc9 -lut 4 +design -load postopt +select -assert-count 1 t:$lut +select -assert-count 1 t:$_AND_ +  design -reset  read_verilog -icells <<EOT diff --git a/tests/various/abc9.ys b/tests/various/abc9.ys index 81d0afd1b..0c7695089 100644 --- a/tests/various/abc9.ys +++ b/tests/various/abc9.ys @@ -14,6 +14,7 @@ design -import gate -as gate  miter -equiv -flatten -make_assert -make_outputs gold gate miter  sat -verify -prove-asserts -show-ports miter +  design -load read  hierarchy -top abc9_test028  proc @@ -23,6 +24,7 @@ select -assert-count 1 t:$lut r:LUT=2'b01 r:WIDTH=1 %i %i  select -assert-count 1 t:unknown  select -assert-none t:$lut t:unknown %% t: %D +  design -load read  hierarchy -top abc9_test032  proc @@ -38,3 +40,16 @@ design -import gate -as gate  miter -equiv -flatten -make_assert -make_outputs gold gate miter  sat -seq 10 -verify -prove-asserts -show-ports miter + + +design -reset +read_verilog -icells <<EOT +module abc9_test036(input clk, d, output q); +(* keep *) reg w; +$__ABC9_FF_ ff(.D(d), .Q(w)); +wire \ff.clock = clk; +wire \ff.init = 1'b0; +assign q = w; +endmodule +EOT +abc9 -lut 4 -dff  | 
