diff options
Diffstat (limited to 'passes/techmap')
| -rw-r--r-- | passes/techmap/Makefile.inc | 2 | ||||
| -rw-r--r-- | passes/techmap/abc9.cc | 1226 | ||||
| -rw-r--r-- | passes/techmap/abc9_map.cc | 981 | ||||
| -rw-r--r-- | passes/techmap/abc9_ops.cc | 486 | 
4 files changed, 1544 insertions, 1151 deletions
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index cd357d72a..734d6c10f 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_map.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 d03e5da8e..f44e3df06 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 @@ -18,802 +18,21 @@   *   */ -// [[CITE]] ABC -// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification -// http://www.eecs.berkeley.edu/~alanmi/abc/ - -#if 0 -// Based on &flow3 - better QoR but more experimental -#define ABC_COMMAND_LUT "&st; &ps -l; &sweep -v; &scorr; " \ -						"&st; &if {W}; &save; &st; &syn2; &if {W} -v; &save; &load; "\ -						"&st; &if -g -K 6; &dch -f; &if {W} -v; &save; &load; "\ -						"&st; &if -g -K 6; &synch2; &if {W} -v; &save; &load; "\ -						"&mfs; &ps -l" -#else -#define ABC_COMMAND_LUT "&st; &scorr; &sweep; &dc2; &st; &dch -f; &ps; &if {W} {D} -v; &mfs; &ps -l" -#endif - - -#define ABC_FAST_COMMAND_LUT "&st; &if {W} {D}" -  #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  USING_YOSYS_NAMESPACE  PRIVATE_NAMESPACE_BEGIN -bool markgroups; -int map_autoidx; -SigMap assign_map; -RTLIL::Module *module; - -bool clk_polarity, en_polarity; -RTLIL::SigSpec clk_sig, en_sig; - -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) -{ -	Pass::call(design, "scc -set_attr abc9_scc_id {}"); - -	// 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; -} +#define XC7_WIRE_DELAY 300 // Number with which ABC will map a 6-input gate +                           // to one LUT6 (instead of a LUT5 + LUT2) -std::string fold_abc9_cmd(std::string str) +struct Abc9Pass : public ScriptPass  { -	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 *current_module, std::string script_file, std::string exe_file, -		bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, -		bool /*keepff*/, 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, const dict<int,IdString> &box_lookup, bool nomfs -) -{ -	module = current_module; -	map_autoidx = autoidx++; - -	if (clk_str != "$") -	{ -		clk_polarity = true; -		clk_sig = RTLIL::SigSpec(); - -		en_polarity = true; -		en_sig = RTLIL::SigSpec(); -	} - -	if (!clk_str.empty() && clk_str != "$") -	{ -		if (clk_str.find(',') != std::string::npos) { -			int pos = clk_str.find(','); -			std::string en_str = clk_str.substr(pos+1); -			clk_str = clk_str.substr(0, pos); -			if (en_str[0] == '!') { -				en_polarity = false; -				en_str = en_str.substr(1); -			} -			if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) -				en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); -		} -		if (clk_str[0] == '!') { -			clk_polarity = false; -			clk_str = clk_str.substr(1); -		} -		if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) -			clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); -	} - -	if (dff_mode && clk_sig.empty()) -		log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); - -	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 -v %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 -v %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()) { -		//bool all_luts_cost_same = true; -		//for (int this_cost : lut_costs) -		//	if (this_cost != lut_costs.front()) -		//		all_luts_cost_same = false; -		abc9_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; -		//if (all_luts_cost_same && !fast_mode) -		//	abc9_script += "; lutpack {S}"; -	} else -		log_abort(); - -	//if (script_file.empty() && !delay_target.empty()) -	//	for (size_t pos = abc9_script.find("dretime;"); pos != std::string::npos; pos = abc9_script.find("dretime;", pos+1)) -	//		abc9_script = abc9_script.substr(0, pos) + "dretime; retime -o {D};" + abc9_script.substr(pos+8); - -	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); - -	if (nomfs) -		for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos)) -			abc9_script = abc9_script.erase(pos, strlen("&mfs")); - -	abc9_script += stringf("; &write %s/output.aig", tempdir_name.c_str()); -	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); - -	if (dff_mode || !clk_str.empty()) -	{ -		if (clk_sig.size() == 0) -			log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching"); -		else { -			log("Found%s %s clock domain: %s", clk_str.empty() ? "" : " matching", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig)); -			if (en_sig.size() != 0) -				log(", enabled by %s%s", en_polarity ? "" : "!", log_signal(en_sig)); -			log("\n"); -		} -	} - -	bool count_output = false; -	for (auto port_name : module->ports) { -		RTLIL::Wire *port_wire = module->wire(port_name); -		log_assert(port_wire); -		if (port_wire->port_output) { -			count_output = true; -			break; -		} -	} - -	log_push(); - -	if (count_output) -	{ -		design->selection_stack.emplace_back(false); -		RTLIL::Selection& sel = design->selection_stack.back(); -		sel.select(module); - -		handle_loops(design); - -		Pass::call(design, "aigmap"); - -		//log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", -		//		count_gates, GetSize(signal_list), count_input, count_output); - -		Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); - -		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(design, stringf("write_verilog -noexpr -norename")); -		design->remove(design->module(ID($__abc9__))); -#endif - -		design->selection_stack.pop_back(); - -		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(box_lookup); -		ifs.close(); - -#if 0 -		Pass::call(design, stringf("write_verilog -noexpr -norename")); -#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"); - -		pool<RTLIL::SigBit> output_bits; -		for (auto &it : mapped_mod->wires_) { -			RTLIL::Wire *w = it.second; -			RTLIL::Wire *remap_wire = module->addWire(remap_name(w->name), GetSize(w)); -			if (markgroups) remap_wire->attributes[ID(abcgroup)] = map_autoidx; -			if (w->port_output) { -				RTLIL::Wire *wire = module->wire(w->name); -				log_assert(wire); -				for (int i = 0; i < GetSize(w); i++) -					output_bits.insert({wire, i}); -			} -		} - -		for (auto &it : module->connections_) { -			auto &signal = it.first; -			auto bits = signal.bits(); -			for (auto &b : bits) -				if (output_bits.count(b)) -					b = module->addWire(NEW_ID); -			signal = std::move(bits); -		} - -		dict<IdString, bool> abc9_box; -		vector<RTLIL::Cell*> boxes; -		for (const auto &it : module->cells_) { -			auto cell = it.second; -			if (cell->type.in(ID($_AND_), ID($_NOT_))) { -				module->remove(cell); -				continue; -			} -			auto jt = abc9_box.find(cell->type); -			if (jt == abc9_box.end()) { -				RTLIL::Module* box_module = design->module(cell->type); -				jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first; -			} -			if (jt->second) -				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 c : mapped_mod->cells()) -		{ -			toposort.node(c->name); - -			RTLIL::Cell *cell = nullptr; -			if (c->type == ID($_NOT_)) { -				RTLIL::SigBit a_bit = c->getPort(ID::A); -				RTLIL::SigBit y_bit = c->getPort(ID::Y); -				bit_users[a_bit].insert(c->name); -				bit_drivers[y_bit].insert(c->name); - -				if (!a_bit.wire) { -					c->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("%s$lut", a_bit.wire->name.c_str()); -						else -							driver_name = stringf("%s[%d]$lut", 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("%s$lut", c->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[c] = driver_lut; -					continue; -				} -				else -					log_abort(); -				if (cell && markgroups) cell->attributes[ID(abcgroup)] = map_autoidx; -				continue; -			} -			cell_stats[c->type]++; - -			RTLIL::Cell *existing_cell = nullptr; -			if (c->type == ID($lut)) { -				if (GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { -					SigSpec my_a = module->wires_.at(remap_name(c->getPort(ID::A).as_wire()->name)); -					SigSpec my_y = module->wires_.at(remap_name(c->getPort(ID::Y).as_wire()->name)); -					module->connect(my_y, my_a); -					if (markgroups) c->attributes[ID(abcgroup)] = map_autoidx; -					log_abort(); -					continue; -				} -				cell = module->addCell(remap_name(c->name), c->type); -			} -			else { -				existing_cell = module->cell(c->name); -				log_assert(existing_cell); -				cell = module->addCell(remap_name(c->name), c->type); -			} - -			if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx; -			if (existing_cell) { -				cell->parameters = existing_cell->parameters; -				cell->attributes = existing_cell->attributes; -			} -			else { -				cell->parameters = c->parameters; -				cell->attributes = c->attributes; -			} -			for (auto &conn : c->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 (cell->input(conn.first)) { -					for (auto i : newsig) -						bit2sinks[i].push_back(cell); -					for (auto i : conn.second) -						bit_users[i].insert(c->name); -				} -				if (cell->output(conn.first)) -					for (auto i : conn.second) -						bit_drivers[i].insert(c->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); -			} -		} +	Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { } -		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") { }  	void help() YS_OVERRIDE  	{  		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -842,24 +61,25 @@ struct Abc9Pass : public Pass {  		log("        if no -script parameter is given, the following scripts are used:\n");  		log("\n");  		log("        for -lut/-luts (only one LUT size):\n"); -		log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str()); +		// FIXME +		//log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str());  		log("\n");  		log("        for -lut/-luts (different LUT sizes):\n"); -		log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str()); +		// FIXME +		//log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).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(ABC_FAST_COMMAND_LUT).c_str()); +		// FIXME +		//log("%s\n", fold_abc9_cmd(ABC_FAST_COMMAND_LUT).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("        This also replaces 'dretime' with 'dretime; retime -o {D}' in the\n"); -//		log("        default scripts above.\n");  		log("\n");  //		log("    -S <num>\n");  //		log("        maximum number of LUT inputs shared.\n"); @@ -881,19 +101,6 @@ struct Abc9Pass : public Pass {  		log("        generate netlist using luts. Use the specified costs for luts with 1,\n");  		log("        2, 3, .. inputs.\n");  		log("\n"); -//		log("    -dff\n"); -//		log("        also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); -//		log("        clock domains are automatically partitioned in clock domains and each\n"); -//		log("        domain is passed through ABC independently.\n"); -//		log("\n"); -//		log("    -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n"); -//		log("        use only the specified clock domain. this is like -dff, but only FF\n"); -//		log("        cells that belong to the specified clock domain are used.\n"); -//		log("\n"); -//		log("    -keepff\n"); -//		log("        set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n"); -//		log("        them, for example for equivalence checking.)\n"); -//		log("\n");  		log("    -nocleanup\n");  		log("        when this option is used, the temporary files created by this pass\n");  		log("        are not removed. this is useful for debugging.\n"); @@ -914,230 +121,76 @@ struct Abc9Pass : public Pass {  		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("design as an XAIGER file with write_xaiger and then load that into ABC externally\n"); +		log("if 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"); +		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(); -		assign_map.clear(); +	std::stringstream map_cmd; +	bool cleanup; -#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, keepff = false, cleanup = true; -		bool show_tempdir = false; -		bool nomfs = false; -		vector<int> lut_costs; -		markgroups = false; - -#if 0 -		cleanup = false; -		show_tempdir = true; -#endif +	void clear_flags() YS_OVERRIDE +	{ +		map_cmd.str(""); +		map_cmd << "abc9_map"; +		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();  		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]; -				rewrite_filename(script_file); -				if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') -					script_file = std::string(pwd) + "/" + script_file; -				continue; -			} -			if (arg == "-D" && argidx+1 < args.size()) { -				delay_target = "-D " + args[++argidx]; +			if ((arg == "-exe" || arg == "-script" || arg == "-D" || +						/* arg == "-S" || */ arg == "-lut" || arg == "-luts" || +						arg == "-box" || arg == "-W") && +					argidx+1 < args.size()) { +				map_cmd << " " << arg << " " << args[++argidx];  				continue;  			} -			//if (arg == "-S" && argidx+1 < args.size()) { -			//	lutin_shared = "-S " + args[++argidx]; -			//	continue; -			//} -			if (arg == "-lut" && argidx+1 < args.size()) { -				string arg = args[++argidx]; -				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 (arg == "-fast" +					/*|| arg == "-nocleanup"*/ || arg == "-showtmp" || arg == "-markgroups" +					|| arg == "-nomfs") { +				map_cmd << " " << arg;  				continue;  			} -			if (arg == "-luts" && argidx+1 < args.size()) { -				lut_costs.clear(); -				for (auto &tok : split_tokens(args[++argidx], ",")) { -					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"); -				} -				continue; -			} -			if (arg == "-fast") { -				fast_mode = true; -				continue; -			} -			//if (arg == "-dff") { -			//	dff_mode = true; -			//	continue; -			//} -			//if (arg == "-clk" && argidx+1 < args.size()) { -			//	clk_str = args[++argidx]; -			//	dff_mode = true; -			//	continue; -			//} -			//if (arg == "-keepff") { -			//	keepff = true; -			//	continue; -			//}  			if (arg == "-nocleanup") {  				cleanup = false;  				continue;  			} -			if (arg == "-showtmp") { -				show_tempdir = true; -				continue; -			} -			if (arg == "-markgroups") { -				markgroups = 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 == "-nomfs") { -				nomfs = true; -				continue; -			}  			break;  		}  		extra_args(args, argidx, design); -		// ABC expects a box file for XAIG -		if (box_file.empty()) -		    box_file = "+/dummy.box"; +		log_header(design, "Executing ABC9 pass.\n"); -		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); +	} -		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); +	void script() YS_OVERRIDE +	{ +		run("abc9_ops -prep_holes"); +		run("select -set abc9_holes A:abc9_holes"); +		run("flatten -wb @abc9_holes"); +		run("techmap @abc9_holes"); +		run("aigmap @abc9_holes"); +		run("select -list @abc9_holes"); +		run("abc9_ops -prep_dff"); +		run("opt -purge @abc9_holes"); +		run("setattr -mod -set whitebox 1 @abc9_holes"); -			RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; -			for (auto p : m->ports) { -				auto w = m->wire(p); -				log_assert(w); -				if (w->attributes.count(ID(abc9_carry))) { -					if (w->port_input) { -						if (carry_in) -							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); -						carry_in = w; -					} -					else if (w->port_output) { -						if (carry_out) -							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); -						carry_out = w; -					} -				} -			} -			if (carry_in || carry_out) { -				if (carry_in && !carry_out) -					log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m)); -				if (!carry_in && carry_out) -					log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m)); -				// Make carry_in the last PI, and carry_out the last PO -				//   since ABC requires it this way -				auto &ports = m->ports; -				for (auto it = ports.begin(); it != ports.end(); ) { -					RTLIL::Wire* w = m->wire(*it); -					log_assert(w); -					if (w == carry_in || w == carry_out) { -						it = ports.erase(it); -						continue; -					} -					if (w->port_id > carry_in->port_id) -						--w->port_id; -					if (w->port_id > carry_out->port_id) -						--w->port_id; -					log_assert(w->port_input || w->port_output); -					log_assert(ports[w->port_id-1] == w->name); -					++it; -				} -				ports.push_back(carry_in->name); -				carry_in->port_id = ports.size(); -				ports.push_back(carry_out->name); -				carry_out->port_id = ports.size(); -			} -		} +		auto selected_modules = active_design->selected_modules(); +		active_design->selection_stack.emplace_back(false); -		for (auto mod : design->selected_modules()) -		{ -			if (mod->attributes.count(ID(abc9_box_id))) +		for (auto mod : selected_modules) { +			if (mod->get_blackbox_attribute())  				continue;  			if (mod->processes.size() > 0) { @@ -1145,165 +198,36 @@ struct Abc9Pass : public Pass {  				continue;  			} -			assign_map.set(mod); - -			if (!dff_mode || !clk_str.empty()) { -				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff, -						delay_target, lutin_shared, fast_mode, show_tempdir, -						box_file, lut_file, wire_delay, box_lookup, nomfs); -				continue; -			} - -			CellTypes ct(design); +			log_push(); -			std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); -			std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); +			active_design->selection().select(mod); -			std::set<RTLIL::Cell*> expand_queue, next_expand_queue; -			std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; -			std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; +			std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; +			if (!cleanup) +				tempdir_name[0] = tempdir_name[4] = '_'; +			tempdir_name = make_temp_dir(tempdir_name); -			typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; -			std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; -			std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; +			run("scc -set_attr abc9_scc_id {}"); +			run("abc9_ops -break_scc"); +			run("aigmap"); +			run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()), +					"write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig"); +			run(stringf("%s -tempdir %s", map_cmd.str().c_str(), tempdir_name.c_str()), +					"abc9_map [options] -tempdir <abc-temp-dir>"); +			run("abc9_ops -unbreak_scc"); -			std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; -			std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; - -			for (auto cell : all_cells) +			if (cleanup)  			{ -				clkdomain_t key; - -				for (auto &conn : cell->connections()) -				for (auto bit : conn.second) { -					bit = assign_map(bit); -					if (bit.wire != nullptr) { -						cell_to_bit[cell].insert(bit); -						bit_to_cell[bit].insert(cell); -						if (ct.cell_input(cell->type, conn.first)) { -							cell_to_bit_up[cell].insert(bit); -							bit_to_cell_down[bit].insert(cell); -						} -						if (ct.cell_output(cell->type, conn.first)) { -							cell_to_bit_down[cell].insert(bit); -							bit_to_cell_up[bit].insert(cell); -						} -					} -				} - -				if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_))) -				{ -					key = clkdomain_t(cell->type == ID($_DFF_P_), assign_map(cell->getPort(ID(C))), true, RTLIL::SigSpec()); -				} -				else -				if (cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) -				{ -					bool this_clk_pol = cell->type.in(ID($_DFFE_PN_), ID($_DFFE_PP_)); -					bool this_en_pol = cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)); -					key = clkdomain_t(this_clk_pol, assign_map(cell->getPort(ID(C))), this_en_pol, assign_map(cell->getPort(ID(E)))); -				} -				else -					continue; - -				unassigned_cells.erase(cell); -				expand_queue.insert(cell); -				expand_queue_up.insert(cell); -				expand_queue_down.insert(cell); - -				assigned_cells[key].push_back(cell); -				assigned_cells_reverse[cell] = key; +				log("Removing temp directory.\n"); +				remove_directory(tempdir_name);  			} -			while (!expand_queue_up.empty() || !expand_queue_down.empty()) -			{ -				if (!expand_queue_up.empty()) -				{ -					RTLIL::Cell *cell = *expand_queue_up.begin(); -					clkdomain_t key = assigned_cells_reverse.at(cell); -					expand_queue_up.erase(cell); +			active_design->selection().selected_modules.clear(); -					for (auto bit : cell_to_bit_up[cell]) -					for (auto c : bit_to_cell_up[bit]) -						if (unassigned_cells.count(c)) { -							unassigned_cells.erase(c); -							next_expand_queue_up.insert(c); -							assigned_cells[key].push_back(c); -							assigned_cells_reverse[c] = key; -							expand_queue.insert(c); -						} -				} - -				if (!expand_queue_down.empty()) -				{ -					RTLIL::Cell *cell = *expand_queue_down.begin(); -					clkdomain_t key = assigned_cells_reverse.at(cell); -					expand_queue_down.erase(cell); - -					for (auto bit : cell_to_bit_down[cell]) -					for (auto c : bit_to_cell_down[bit]) -						if (unassigned_cells.count(c)) { -							unassigned_cells.erase(c); -							next_expand_queue_up.insert(c); -							assigned_cells[key].push_back(c); -							assigned_cells_reverse[c] = key; -							expand_queue.insert(c); -						} -				} - -				if (expand_queue_up.empty() && expand_queue_down.empty()) { -					expand_queue_up.swap(next_expand_queue_up); -					expand_queue_down.swap(next_expand_queue_down); -				} -			} - -			while (!expand_queue.empty()) -			{ -				RTLIL::Cell *cell = *expand_queue.begin(); -				clkdomain_t key = assigned_cells_reverse.at(cell); -				expand_queue.erase(cell); - -				for (auto bit : cell_to_bit.at(cell)) { -					for (auto c : bit_to_cell[bit]) -						if (unassigned_cells.count(c)) { -							unassigned_cells.erase(c); -							next_expand_queue.insert(c); -							assigned_cells[key].push_back(c); -							assigned_cells_reverse[c] = key; -						} -					bit_to_cell[bit].clear(); -				} - -				if (expand_queue.empty()) -					expand_queue.swap(next_expand_queue); -			} - -			clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); -			for (auto cell : unassigned_cells) { -				assigned_cells[key].push_back(cell); -				assigned_cells_reverse[cell] = key; -			} - -			log_header(design, "Summary of detected clock domains:\n"); -			for (auto &it : assigned_cells) -				log("  %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), -						std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), -						std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); - -			for (auto &it : assigned_cells) { -				clk_polarity = std::get<0>(it.first); -				clk_sig = assign_map(std::get<1>(it.first)); -				en_polarity = std::get<2>(it.first); -				en_sig = assign_map(std::get<3>(it.first)); -				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$", -						keepff, delay_target, lutin_shared, fast_mode, show_tempdir, -						box_file, lut_file, wire_delay, box_lookup, nomfs); -				assign_map.set(mod); -			} +			log_pop();  		} -		assign_map.clear(); - -		log_pop(); +		active_design->selection_stack.pop_back();  	}  } Abc9Pass; diff --git a/passes/techmap/abc9_map.cc b/passes/techmap/abc9_map.cc new file mode 100644 index 000000000..9764d057c --- /dev/null +++ b/passes/techmap/abc9_map.cc @@ -0,0 +1,981 @@ +/* + *  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/ + +#if 0 +// Based on &flow3 - better QoR but more experimental +#define ABC_COMMAND_LUT "&st; &ps -l; &sweep -v; &scorr; " \ +						"&st; &if {W}; &save; &st; &syn2; &if {W} -v; &save; &load; "\ +						"&st; &if -g -K 6; &dch -f; &if {W} -v; &save; &load; "\ +						"&st; &if -g -K 6; &synch2; &if {W} -v; &save; &load; "\ +						"&mfs; &ps -l" +#else +#define ABC_COMMAND_LUT "&st; &scorr; &sweep; &dc2; &st; &dch -f; &ps; &if {W} {D} -v; &mfs; &ps -l" +#endif + + +#define ABC_FAST_COMMAND_LUT "&st; &if {W} {D}" + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/cost.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 + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool markgroups; +int map_autoidx; + +inline std::string remap_name(RTLIL::IdString abc9_name) +{ +	return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); +} + +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, +		vector<int> lut_costs, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, +		const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, std::string box_file, std::string lut_file, +		std::string wire_delay, const dict<int,IdString> &box_lookup, bool nomfs, std::string tempdir_name +) +{ +	map_autoidx = autoidx++; + +	//FIXME: +	//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 ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; +	} 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); + +	if (nomfs) +		for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos)) +			abc9_script = abc9_script.erase(pos, strlen("&mfs")); + +	abc9_script += stringf("; &write -n %s/output.aig", tempdir_name.c_str()); +	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(); + +	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(box_lookup); +		} +		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(box_lookup); +		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 &it : mapped_mod->wires_) { +			RTLIL::Wire *w = it.second; +			RTLIL::Wire *remap_wire = module->addWire(remap_name(w->name), GetSize(w)); +			if (markgroups) remap_wire->attributes[ID(abcgroup)] = map_autoidx; +		} + +		dict<IdString, bool> abc9_box; +		vector<RTLIL::Cell*> boxes; +		for (auto cell : cells) { +			if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) { +				module->remove(cell); +				continue; +			} +			auto jt = abc9_box.find(cell->type); +			if (jt == abc9_box.end()) { +				RTLIL::Module* box_module = design->module(cell->type); +				jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first; +			} +			if (jt->second) { +				auto kt = cell->attributes.find("\\abc9_keep"); +				bool abc9_keep = false; +				if (kt != cell->attributes.end()) { +					abc9_keep = kt->second.as_bool(); +					cell->attributes.erase(kt); +				} +				if (!abc9_keep) +					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("%s$lut", a_bit.wire->name.c_str()); +						else +							driver_name = stringf("%s[%d]$lut", 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("%s$lut", 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(); +				if (cell && markgroups) cell->attributes[ID(abcgroup)] = map_autoidx; +				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); +					if (markgroups) mapped_cell->attributes[ID(abcgroup)] = map_autoidx; +					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 (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx; +			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->attributes.count("\\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); +			} +		} + +		//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"); +	//} + +	log_pop(); +} + +struct Abc9MapPass : public Pass { +	Abc9MapPass() : Pass("abc9_map", "use ABC9 for technology mapping") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    abc9_map [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.\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("\n"); +		log("        for -lut/-luts (only one LUT size):\n"); +		log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str()); +		log("\n"); +		log("        for -lut/-luts (different LUT sizes):\n"); +		log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).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(ABC_FAST_COMMAND_LUT).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("        This also replaces 'dretime' with 'dretime; retime -o {D}' in the\n"); +//		log("        default scripts above.\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("    -dff\n"); +//		log("        also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); +//		log("        clock domains are automatically partitioned in clock domains and each\n"); +//		log("        domain is passed through ABC independently.\n"); +//		log("\n"); +//		log("    -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n"); +//		log("        use only the specified clock domain. this is like -dff, but only FF\n"); +//		log("        cells that belong to the specified clock domain are used.\n"); +//		log("\n"); +//		log("    -keepff\n"); +//		log("        set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n"); +//		log("        them, for example for equivalence checking.)\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("    -markgroups\n"); +		log("        set a 'abcgroup' attribute on all objects created by ABC. The value of\n"); +		log("        this attribute is a unique integer for each ABC process started. This\n"); +		log("        is useful for debugging the partitioning of clock domains.\n"); +		log("\n"); +		log("    -box <file>\n"); +		log("        pass this file with box library to ABC. Use with -lut.\n"); +		log("\n"); +		log("    -tempdir <dir>\n"); +		log("        use this as the temp dir.\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"); +		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; +		std::string tempdir_name; +		bool fast_mode = false; +		bool show_tempdir = false; +		bool nomfs = false; +		vector<int> lut_costs; +		markgroups = false; + +#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 + +		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]; +				rewrite_filename(script_file); +				if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') +					script_file = std::string(pwd) + "/" + script_file; +				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()) { +				string arg = args[++argidx]; +				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; +				} +				continue; +			} +			if (arg == "-luts" && argidx+1 < args.size()) { +				lut_costs.clear(); +				for (auto &tok : split_tokens(args[++argidx], ",")) { +					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"); +				} +				continue; +			} +			if (arg == "-fast") { +				fast_mode = true; +				continue; +			} +			if (arg == "-showtmp") { +				show_tempdir = true; +				continue; +			} +			if (arg == "-markgroups") { +				markgroups = 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 == "-nomfs") { +				nomfs = true; +				continue; +			} +			if (arg == "-tempdir" && argidx+1 < args.size()) { +				tempdir_name = args[++argidx]; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		// 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_map '-tempdir' option is mandatory.\n"); + +		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); + +			RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; +			for (auto p : m->ports) { +				auto w = m->wire(p); +				log_assert(w); +				if (w->attributes.count(ID(abc9_carry))) { +					if (w->port_input) { +						if (carry_in) +							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); +						carry_in = w; +					} +					else if (w->port_output) { +						if (carry_out) +							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); +						carry_out = w; +					} +				} +			} +			if (carry_in || carry_out) { +				if (carry_in && !carry_out) +					log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m)); +				if (!carry_in && carry_out) +					log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m)); +				// Make carry_in the last PI, and carry_out the last PO +				//   since ABC requires it this way +				auto &ports = m->ports; +				for (auto it = ports.begin(); it != ports.end(); ) { +					RTLIL::Wire* w = m->wire(*it); +					log_assert(w); +					if (w == carry_in || w == carry_out) { +						it = ports.erase(it); +						continue; +					} +					if (w->port_id > carry_in->port_id) +						--w->port_id; +					if (w->port_id > carry_out->port_id) +						--w->port_id; +					log_assert(w->port_input || w->port_output); +					log_assert(ports[w->port_id-1] == w->name); +					++it; +				} +				ports.push_back(carry_in->name); +				carry_in->port_id = ports.size(); +				ports.push_back(carry_out->name); +				carry_out->port_id = ports.size(); +			} +		} + +		for (auto mod : design->selected_modules()) +		{ +			if (mod->processes.size() > 0) { +				log("Skipping module %s as it contains processes.\n", log_id(mod)); +				continue; +			} + +			const std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); + +			abc9_module(design, mod, script_file, exe_file, lut_costs, +					delay_target, lutin_shared, fast_mode, all_cells, show_tempdir, +					box_file, lut_file, wire_delay, box_lookup, nomfs, tempdir_name); +		} + +		log_pop(); +	} +} Abc9MapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc new file mode 100644 index 000000000..019868adb --- /dev/null +++ b/passes/techmap/abc9_ops.cc @@ -0,0 +1,486 @@ +/* + *  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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void break_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->selected_cells()) { +		auto it = cell->attributes.find(ID(abc9_scc_id)); +		if (it == cell->attributes.end()) +			continue; +		auto r = ids_seen.insert(it->second); +		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; +				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); +				} +			} +		} +	} + +	module->fixup_ports(); +} + +void unbreak_scc(RTLIL::Module *module) +{ +	// 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(); +} + +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->selected_cells()) { +		auto inst_module = design->module(cell->type); +		if (!inst_module || !inst_module->attributes.count("\\abc9_flop") +				|| cell->get_bool_attribute("\\abc9_keep")) +			continue; + +		Wire *abc9_clock_wire = module->wire(stringf("%s.$abc9_clock", cell->name.c_str())); +		if (abc9_clock_wire == NULL) +			log_error("'%s$abc9_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.$abc9_init", cell->name.c_str())); +		if (abc9_init_wire == NULL) +		    log_error("'%s.$abc9_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.$abc9_init' is not a constant wire present in module '%s'.\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) { +		dict<SigSig, SigSig> 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 the combinatorial control logic that feeds the box +				//   (i.e. clock enable, synchronous reset, etc.) +				replace.insert(std::make_pair(SigSig(port,Q), SigSig(port,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 input port) we inserted above +				Wire *currQ = holes_module->wire(stringf("%s.$abc9_currQ", driver.c_str())); +				log_assert(currQ); +				holes_module->connect(Q, currQ); +			} +			else +				++it; +		} + +		for (auto &conn : holes_module->connections_) { +			auto it = replace.find(conn); +			if (it != replace.end()) +				conn = it->second; +		} +	} +} + +void prep_holes(RTLIL::Module *module) +{ +	auto design = module->design; +	log_assert(design); + +	SigMap sigmap(module); + +	// 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()); +			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()); +			toposort.node(cell->name); +			bit_users[A].insert(cell->name); +			bit_users[B].insert(cell->name); +			bit_drivers[Y].insert(cell->name); +			continue; +		} + +		if (cell->type == "$__ABC9_FF_") +			continue; + +		RTLIL::Module* inst_module = design->module(cell->type); +		if (inst_module) { +			if (!inst_module->attributes.count("\\abc9_box_id") || cell->get_bool_attribute("\\abc9_keep")) +				continue; + +			for (const auto &conn : cell->connections()) { +				auto port_wire = inst_module->wire(conn.first); +				// 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); +			} + +                        abc9_box_seen = true; + +                        toposort.node(cell->name); +		} +	} + +	if (!abc9_box_seen) +		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 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); + +	vector<Cell*> box_list; +	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") +				|| cell->get_bool_attribute("\\abc9_keep")) +			continue; + +		bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */); + +		// 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 +		// NB: Assume box_module->ports are sorted alphabetically +		//     (as RTLIL::Module::fixup_ports() would do) +		for (const auto &port_name : box_module->ports) { +			RTLIL::Wire* 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->port_output) { +				RTLIL::SigSpec rhs; +				auto it = cell->connections_.find(w->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); +				} +			} +		} + +		box_list.emplace_back(cell); +	} +	log_assert(!box_list.empty()); + +	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; +	for (auto cell : box_list) { +		RTLIL::Module* orig_box_module = design->module(cell->type); +		log_assert(orig_box_module); +		IdString derived_name = orig_box_module->derive(design, cell->parameters); +		RTLIL::Module* box_module = design->module(derived_name); +		if (box_module->has_processes()) +			Pass::call_on_module(design, box_module, "proc"); + +		int box_inputs = 0; +		auto r = cell_cache.insert(std::make_pair(derived_name, nullptr)); +		Cell *holes_cell = r.first->second; +		if (r.second && box_module->get_bool_attribute("\\whitebox")) { +			holes_cell = holes_module->addCell(cell->name, cell->type); +			holes_cell->parameters = cell->parameters; +			r.first->second = holes_cell; + +			// Since Module::derive() will create a new module, there +			//   is a chance that the ports will be alphabetically ordered +			//   again, which is a problem when carry-chains are involved. +			//   Inherit the port ordering from the original module here... +			//   (and set the port_id below, when iterating through those) +			log_assert(GetSize(box_module->ports) == GetSize(orig_box_module->ports)); +			box_module->ports = orig_box_module->ports; +		} + +		// NB: Assume box_module->ports are sorted alphabetically +		//     (as RTLIL::Module::fixup_ports() would do) +		int box_port_id = 1; +		for (const auto &port_name : box_module->ports) { +			RTLIL::Wire *w = box_module->wire(port_name); +			log_assert(w); +			if (r.second) +				w->port_id = box_port_id++; +			RTLIL::Wire *holes_wire; +			RTLIL::SigSpec port_sig; +			if (w->port_input) +				for (int i = 0; i < GetSize(w); i++) { +					box_inputs++; +					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); +					} +					if (holes_cell) +						port_sig.append(holes_wire); +				} +			if (w->port_output) +				for (int i = 0; i < GetSize(w); i++) { +					if (GetSize(w) == 1) +						holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(w->name))); +					else +						holes_wire = holes_module->addWire(stringf("$abc%s.%s[%d]", cell->name.c_str(), log_id(w->name), i)); +					holes_wire->port_output = true; +					holes_wire->port_id = port_id++; +					holes_module->ports.push_back(holes_wire->name); +					if (holes_cell) +						port_sig.append(holes_wire); +					else +						holes_module->connect(holes_wire, State::S0); +				} +			if (!port_sig.empty()) { +				if (r.second) +					holes_cell->setPort(w->name, port_sig); +				else +					holes_module->connect(holes_cell->getPort(w->name), port_sig); +			} +		} + +		// For flops only, create an extra 1-bit input that drives a new wire +		//   called "<cell>.$abc9_currQ" that is used below +		if (box_module->get_bool_attribute("\\abc9_flop")) { +			log_assert(holes_cell); + +			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 *w = holes_module->addWire(stringf("%s.$abc9_currQ", cell->name.c_str())); +			holes_module->connect(w, holes_wire); +		} +	} +} + +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"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n"); +		log_push(); + +		bool break_scc_mode = false; +		bool unbreak_scc_mode = false; +		bool prep_dff_mode = false; +		bool prep_holes_mode = false; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			std::string arg = args[argidx]; +			if (arg == "-break_scc") { +				break_scc_mode = true; +				continue; +			} +			if (arg == "-unbreak_scc") { +				unbreak_scc_mode = true; +				continue; +			} +			if (arg == "-prep_dff") { +				prep_dff_mode = true; +				continue; +			} +			if (arg == "-prep_holes") { +				prep_holes_mode = true; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		for (auto mod : design->selected_modules()) { +			if (mod->get_blackbox_attribute()) +				continue; +			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 (break_scc_mode) +				break_scc(mod); +			if (unbreak_scc_mode) +				unbreak_scc(mod); +			if (prep_dff_mode) +				prep_dff(mod); +			if (prep_holes_mode) +				prep_holes(mod); +		} +	} +} Abc9OpsPass; + +PRIVATE_NAMESPACE_END  | 
