diff options
Diffstat (limited to 'passes/techmap')
| -rw-r--r-- | passes/techmap/Makefile.inc | 2 | ||||
| -rw-r--r-- | passes/techmap/abc9.cc | 997 | ||||
| -rw-r--r-- | passes/techmap/abc9_exe.cc | 531 | ||||
| -rw-r--r-- | passes/techmap/abc9_ops.cc | 814 | 
4 files changed, 1459 insertions, 885 deletions
| diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index cd357d72a..369c9de64 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -8,6 +8,8 @@ OBJS += passes/techmap/libparse.o  ifeq ($(ENABLE_ABC),1)  OBJS += passes/techmap/abc.o  OBJS += passes/techmap/abc9.o +OBJS += passes/techmap/abc9_exe.o +OBJS += passes/techmap/abc9_ops.o  ifneq ($(ABCEXTERNAL),)  passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'  passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 1f6cdaa22..6a296bfe7 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -2,7 +2,7 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *                2019  Eddie Hung <eddie@fpgeh.com> + *            (C) 2019  Eddie Hung    <eddie@fpgeh.com>   *   *  Permission to use, copy, modify, and/or distribute this software for any   *  purpose with or without fee is hereby granted, provided that the above @@ -23,714 +23,19 @@  // http://www.eecs.berkeley.edu/~alanmi/abc/  #include "kernel/register.h" -#include "kernel/sigtools.h"  #include "kernel/celltypes.h" -#include "kernel/cost.h" +#include "kernel/rtlil.h"  #include "kernel/log.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <cerrno> -#include <sstream> -#include <climits> -#ifndef _WIN32 -#  include <unistd.h> -#  include <dirent.h> -#endif - -#include "frontends/aiger/aigerparse.h" -#include "kernel/utils.h" - -#ifdef YOSYS_LINK_ABC -extern "C" int Abc_RealMain(int argc, char *argv[]); -#endif +// abc9_exe.cc +std::string fold_abc9_cmd(std::string str);  USING_YOSYS_NAMESPACE  PRIVATE_NAMESPACE_BEGIN -int map_autoidx; - -inline std::string remap_name(RTLIL::IdString abc9_name) -{ -	return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); -} - -void handle_loops(RTLIL::Design *design, RTLIL::Module *module) -{ -	Pass::call(design, "scc -set_attr abc9_scc_id {} % w:*"); - -	// For every unique SCC found, (arbitrarily) find the first -	// cell in the component, and select (and mark) all its output -	// wires -	pool<RTLIL::Const> ids_seen; -	for (auto cell : module->cells()) { -		auto it = cell->attributes.find(ID(abc9_scc_id)); -		if (it != cell->attributes.end()) { -			auto r = ids_seen.insert(it->second); -			if (r.second) { -				for (auto &c : cell->connections_) { -					if (c.second.is_fully_const()) continue; -					if (cell->output(c.first)) { -						SigBit b = c.second.as_bit(); -						Wire *w = b.wire; -						if (w->port_input) { -							// In this case, hopefully the loop break has been already created -							// Get the non-prefixed wire -							Wire *wo = module->wire(stringf("%s.abco", b.wire->name.c_str())); -							log_assert(wo != nullptr); -							log_assert(wo->port_output); -							log_assert(b.offset < GetSize(wo)); -							c.second = RTLIL::SigBit(wo, b.offset); -						} -						else { -							// Create a new output/input loop break -							w->port_input = true; -							w = module->wire(stringf("%s.abco", w->name.c_str())); -							if (!w) { -								w = module->addWire(stringf("%s.abco", b.wire->name.c_str()), GetSize(b.wire)); -								w->port_output = true; -							} -							else { -								log_assert(w->port_input); -								log_assert(b.offset < GetSize(w)); -							} -							w->set_bool_attribute(ID(abc9_scc_break)); -							c.second = RTLIL::SigBit(w, b.offset); -						} -					} -				} -			} -			cell->attributes.erase(it); -		} -	} - -	module->fixup_ports(); -} - -std::string add_echos_to_abc9_cmd(std::string str) -{ -	std::string new_str, token; -	for (size_t i = 0; i < str.size(); i++) { -		token += str[i]; -		if (str[i] == ';') { -			while (i+1 < str.size() && str[i+1] == ' ') -				i++; -			new_str += "echo + " + token + " " + token + " "; -			token.clear(); -		} -	} - -	if (!token.empty()) { -		if (!new_str.empty()) -			new_str += "echo + " + token + "; "; -		new_str += token; -	} - -	return new_str; -} - -std::string fold_abc9_cmd(std::string str) -{ -	std::string token, new_str = "          "; -	int char_counter = 10; - -	for (size_t i = 0; i <= str.size(); i++) { -		if (i < str.size()) -			token += str[i]; -		if (i == str.size() || str[i] == ';') { -			if (char_counter + token.size() > 75) -				new_str += "\n              ", char_counter = 14; -			new_str += token, char_counter += token.size(); -			token.clear(); -		} -	} - -	return new_str; -} - -std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +struct Abc9Pass : public ScriptPass  { -	if (show_tempdir) -		return text; - -	while (1) { -		size_t pos = text.find(tempdir_name); -		if (pos == std::string::npos) -			break; -		text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); -	} - -	std::string  selfdir_name = proc_self_dirname(); -	if (selfdir_name != "/") { -		while (1) { -			size_t pos = text.find(selfdir_name); -			if (pos == std::string::npos) -				break; -			text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); -		} -	} - -	return text; -} - -struct abc9_output_filter -{ -	bool got_cr; -	int escape_seq_state; -	std::string linebuf; -	std::string tempdir_name; -	bool show_tempdir; - -	abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) -	{ -		got_cr = false; -		escape_seq_state = 0; -	} - -	void next_char(char ch) -	{ -		if (escape_seq_state == 0 && ch == '\033') { -			escape_seq_state = 1; -			return; -		} -		if (escape_seq_state == 1) { -			escape_seq_state = ch == '[' ? 2 : 0; -			return; -		} -		if (escape_seq_state == 2) { -			if ((ch < '0' || '9' < ch) && ch != ';') -				escape_seq_state = 0; -			return; -		} -		escape_seq_state = 0; -		if (ch == '\r') { -			got_cr = true; -			return; -		} -		if (ch == '\n') { -			log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); -			got_cr = false, linebuf.clear(); -			return; -		} -		if (got_cr) -			got_cr = false, linebuf.clear(); -		linebuf += ch; -	} - -	void next_line(const std::string &line) -	{ -		//int pi, po; -		//if (sscanf(line.c_str(), "Start-point = pi%d.  End-point = po%d.", &pi, &po) == 2) { -		//	log("ABC: Start-point = pi%d (%s).  End-point = po%d (%s).\n", -		//			pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", -		//			po, po_map.count(po) ? po_map.at(po).c_str() : "???"); -		//	return; -		//} - -		for (char ch : line) -			next_char(ch); -	} -}; - -void abc9_module(RTLIL::Design *design, RTLIL::Module *module, std::string script_file, std::string exe_file, -		bool cleanup, vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, -		bool show_tempdir, std::string box_file, std::string lut_file, -		std::string wire_delay -) -{ -	map_autoidx = autoidx++; - -	std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; -	if (!cleanup) -		tempdir_name[0] = tempdir_name[4] = '_'; -	tempdir_name = make_temp_dir(tempdir_name); -	log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n", -			module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); - -	std::string abc9_script; - -	if (!lut_costs.empty()) { -		abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); -		if (!box_file.empty()) -			abc9_script += stringf("read_box %s; ", box_file.c_str()); -	} -	else -	if (!lut_file.empty()) { -		abc9_script += stringf("read_lut %s; ", lut_file.c_str()); -		if (!box_file.empty()) -			abc9_script += stringf("read_box %s; ", box_file.c_str()); -	} -	else -		log_abort(); - -	abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); - -	if (!script_file.empty()) { -		if (script_file[0] == '+') { -			for (size_t i = 1; i < script_file.size(); i++) -				if (script_file[i] == '\'') -					abc9_script += "'\\''"; -				else if (script_file[i] == ',') -					abc9_script += " "; -				else -					abc9_script += script_file[i]; -		} else -			abc9_script += stringf("source %s", script_file.c_str()); -	} else if (!lut_costs.empty() || !lut_file.empty()) { -		abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos) -			: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos); -	} else -		log_abort(); - -	for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) -		abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); - -	//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos)) -	//	abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3); - -	for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) -		abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); - -	std::string C; -	if (design->scratchpad.count("abc9.if.C")) -		C = "-C " + design->scratchpad_get_string("abc9.if.C"); -	for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos)) -		abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3); - -	std::string R; -	if (design->scratchpad.count("abc9.if.R")) -		R = "-R " + design->scratchpad_get_string("abc9.if.R"); -	for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos)) -		abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3); - -	abc9_script += stringf("; &ps -l; &write -n %s/output.aig;", tempdir_name.c_str()); -	if (design->scratchpad_get_bool("abc9.verify")) { -		if (dff_mode) -			abc9_script += "verify -s;"; -		else -			abc9_script += "verify;"; -	} -	abc9_script += "time"; -	abc9_script = add_echos_to_abc9_cmd(abc9_script); - -	for (size_t i = 0; i+1 < abc9_script.size(); i++) -		if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') -			abc9_script[i+1] = '\n'; - -	FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); -	fprintf(f, "%s\n", abc9_script.c_str()); -	fclose(f); - -	log_push(); - -	handle_loops(design, module); - -	Pass::call(design, "aigmap -select"); - -	Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); - -	int count_outputs = design->scratchpad_get_int("write_xaiger.num_outputs"); -	log("Extracted %d AND gates and %d wires to a netlist network with %d inputs and %d outputs.\n", -			design->scratchpad_get_int("write_xaiger.num_ands"), -			design->scratchpad_get_int("write_xaiger.num_wires"), -			design->scratchpad_get_int("write_xaiger.num_inputs"), -			count_outputs); - -	if (count_outputs > 0) { -		std::string buffer; -		std::ifstream ifs; -#if 0 -		buffer = stringf("%s/%s", tempdir_name.c_str(), "input.xaig"); -		ifs.open(buffer); -		if (ifs.fail()) -			log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); -		buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); -		log_assert(!design->module(ID($__abc9__))); -		{ -			AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); -			reader.parse_xaiger(); -		} -		ifs.close(); -		Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected")); -		design->remove(design->module(ID($__abc9__))); -#endif - -		log_header(design, "Executing ABC9.\n"); - -		if (!lut_costs.empty()) { -			buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); -			f = fopen(buffer.c_str(), "wt"); -			if (f == NULL) -				log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); -			for (int i = 0; i < GetSize(lut_costs); i++) -				fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); -			fclose(f); -		} - -		buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); -		log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); - -#ifndef YOSYS_LINK_ABC -		abc9_output_filter filt(tempdir_name, show_tempdir); -		int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); -#else -		// These needs to be mutable, supposedly due to getopt -		char *abc9_argv[5]; -		string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); -		abc9_argv[0] = strdup(exe_file.c_str()); -		abc9_argv[1] = strdup("-s"); -		abc9_argv[2] = strdup("-f"); -		abc9_argv[3] = strdup(tmp_script_name.c_str()); -		abc9_argv[4] = 0; -		int ret = Abc_RealMain(4, abc9_argv); -		free(abc9_argv[0]); -		free(abc9_argv[1]); -		free(abc9_argv[2]); -		free(abc9_argv[3]); -#endif -		if (ret != 0) -			log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); - -		buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig"); -		ifs.open(buffer, std::ifstream::binary); -		if (ifs.fail()) -			log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - -		buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); -		log_assert(!design->module(ID($__abc9__))); - -		AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); -		reader.parse_xaiger(); -		ifs.close(); - -#if 0 -		Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected")); -#endif - -		log_header(design, "Re-integrating ABC9 results.\n"); -		RTLIL::Module *mapped_mod = design->module(ID($__abc9__)); -		if (mapped_mod == NULL) -			log_error("ABC output file does not contain a module `$__abc9__'.\n"); - -		for (auto w : mapped_mod->wires()) -			module->addWire(remap_name(w->name), GetSize(w)); - -		dict<IdString, bool> abc9_box; -		vector<RTLIL::Cell*> boxes; -		for (auto cell : module->cells().to_vector()) { -			if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) { -				module->remove(cell); -				continue; -			} -			RTLIL::Module* box_module = design->module(cell->type); -			auto jt = abc9_box.find(cell->type); -			if (jt == abc9_box.end()) -				jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first; -			if (jt->second) { -				if (box_module->get_bool_attribute("\\abc9_flop")) { -					if (dff_mode) -						boxes.emplace_back(cell); -					else -						box_module->set_bool_attribute("\\abc9_keep", false); -				} -				else -					boxes.emplace_back(cell); -			} -		} - -		dict<SigBit, pool<IdString>> bit_drivers, bit_users; -		TopoSort<IdString, RTLIL::sort_by_id_str> toposort; -		dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; -		dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; - -		std::map<IdString, int> cell_stats; -		for (auto mapped_cell : mapped_mod->cells()) -		{ -			toposort.node(mapped_cell->name); - -			RTLIL::Cell *cell = nullptr; -			if (mapped_cell->type == ID($_NOT_)) { -				RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A); -				RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); -				bit_users[a_bit].insert(mapped_cell->name); -				bit_drivers[y_bit].insert(mapped_cell->name); - -				if (!a_bit.wire) { -					mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); -					RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); -					log_assert(wire); -					module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); -				} -				else if (!lut_costs.empty() || !lut_file.empty()) { -					RTLIL::Cell* driver_lut = nullptr; -					// ABC can return NOT gates that drive POs -					if (!a_bit.wire->port_input) { -						// If it's not a NOT gate that that comes from a PI directly, -						// find the driver LUT and clone that to guarantee that we won't -						// increase the max logic depth -						// (TODO: Optimise by not cloning unless will increase depth) -						RTLIL::IdString driver_name; -						if (GetSize(a_bit.wire) == 1) -							driver_name = stringf("$lut%s", a_bit.wire->name.c_str()); -						else -							driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset); -						driver_lut = mapped_mod->cell(driver_name); -					} - -					if (!driver_lut) { -						// If a driver couldn't be found (could be from PI or box CI) -						// then implement using a LUT -						cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())), -								RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), -								RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), -								RTLIL::Const::from_string("01")); -						bit2sinks[cell->getPort(ID::A)].push_back(cell); -						cell_stats[ID($lut)]++; -					} -					else -						not2drivers[mapped_cell] = driver_lut; -					continue; -				} -				else -					log_abort(); -				continue; -			} -			cell_stats[mapped_cell->type]++; - -			RTLIL::Cell *existing_cell = nullptr; -			if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { -				if (mapped_cell->type == ID($lut) && -						GetSize(mapped_cell->getPort(ID::A)) == 1 && -						mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { -					SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name)); -					SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name)); -					module->connect(my_y, my_a); -					log_abort(); -					continue; -				} -				cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); -			} -			else { -				existing_cell = module->cell(mapped_cell->name); -				log_assert(existing_cell); -				cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); -			} - -			if (existing_cell) { -				cell->parameters = existing_cell->parameters; -				cell->attributes = existing_cell->attributes; -			} -			else { -				cell->parameters = mapped_cell->parameters; -				cell->attributes = mapped_cell->attributes; -			} - -			RTLIL::Module* box_module = design->module(mapped_cell->type); -			auto abc9_flop = box_module && box_module->get_bool_attribute("\\abc9_flop"); -			for (auto &conn : mapped_cell->connections()) { -				RTLIL::SigSpec newsig; -				for (auto c : conn.second.chunks()) { -					if (c.width == 0) -						continue; -					//log_assert(c.width == 1); -					if (c.wire) -						c.wire = module->wires_.at(remap_name(c.wire->name)); -					newsig.append(c); -				} -				cell->setPort(conn.first, newsig); - -				if (!abc9_flop) { -					if (cell->input(conn.first)) { -						for (auto i : newsig) -							bit2sinks[i].push_back(cell); -						for (auto i : conn.second) -							bit_users[i].insert(mapped_cell->name); -					} -					if (cell->output(conn.first)) -						for (auto i : conn.second) -							bit_drivers[i].insert(mapped_cell->name); -				} -			} -		} - -		for (auto existing_cell : boxes) { -			Cell *cell = module->cell(remap_name(existing_cell->name)); -			if (cell) { -				for (auto &conn : existing_cell->connections()) { -					if (!conn.second.is_wire()) -						continue; -					Wire *wire = conn.second.as_wire(); -					if (!wire->get_bool_attribute(ID(abc9_padding))) -						continue; -					cell->unsetPort(conn.first); -					log_debug("Dropping padded port connection for %s (%s) .%s (%s )\n", log_id(cell), cell->type.c_str(), log_id(conn.first), log_signal(conn.second)); -				} -				module->swap_names(cell, existing_cell); -			} -			module->remove(existing_cell); -		} - -		// Copy connections (and rename) from mapped_mod to module -		for (auto conn : mapped_mod->connections()) { -			if (!conn.first.is_fully_const()) { -				auto chunks = conn.first.chunks(); -				for (auto &c : chunks) -					c.wire = module->wires_.at(remap_name(c.wire->name)); -				conn.first = std::move(chunks); -			} -			if (!conn.second.is_fully_const()) { -				auto chunks = conn.second.chunks(); -				for (auto &c : chunks) -					if (c.wire) -						c.wire = module->wires_.at(remap_name(c.wire->name)); -				conn.second = std::move(chunks); -			} -			module->connect(conn); -		} - -		for (auto &it : cell_stats) -			log("ABC RESULTS:   %15s cells: %8d\n", it.first.c_str(), it.second); -		int in_wires = 0, out_wires = 0; - -		// Stitch in mapped_mod's inputs/outputs into module -		for (auto port : mapped_mod->ports) { -			RTLIL::Wire *w = mapped_mod->wire(port); -			RTLIL::Wire *wire = module->wire(port); -			log_assert(wire); -			RTLIL::Wire *remap_wire = module->wire(remap_name(port)); -			RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire)); -			log_assert(GetSize(signal) >= GetSize(remap_wire)); - -			RTLIL::SigSig conn; -			if (w->port_output) { -				conn.first = signal; -				conn.second = remap_wire; -				out_wires++; -				module->connect(conn); -			} -			else if (w->port_input) { -				conn.first = remap_wire; -				conn.second = signal; -				in_wires++; -				module->connect(conn); -			} -		} - -		for (auto &it : bit_users) -			if (bit_drivers.count(it.first)) -				for (auto driver_cell : bit_drivers.at(it.first)) -				for (auto user_cell : it.second) -					toposort.edge(driver_cell, user_cell); -		bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); -		log_assert(no_loops); - -		for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { -			RTLIL::Cell *not_cell = mapped_mod->cell(*ii); -			log_assert(not_cell); -			if (not_cell->type != ID($_NOT_)) -				continue; -			auto it = not2drivers.find(not_cell); -			if (it == not2drivers.end()) -				continue; -			RTLIL::Cell *driver_lut = it->second; -			RTLIL::SigBit a_bit = not_cell->getPort(ID::A); -			RTLIL::SigBit y_bit = not_cell->getPort(ID::Y); -			RTLIL::Const driver_mask; - -			a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); -			y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); - -			auto jt = bit2sinks.find(a_bit); -			if (jt == bit2sinks.end()) -				goto clone_lut; - -			for (auto sink_cell : jt->second) -				if (sink_cell->type != ID($lut)) -					goto clone_lut; - -			// Push downstream LUTs past inverter -			for (auto sink_cell : jt->second) { -				SigSpec A = sink_cell->getPort(ID::A); -				RTLIL::Const mask = sink_cell->getParam(ID(LUT)); -				int index = 0; -				for (; index < GetSize(A); index++) -					if (A[index] == a_bit) -						break; -				log_assert(index < GetSize(A)); -				int i = 0; -				while (i < GetSize(mask)) { -					for (int j = 0; j < (1 << index); j++) -						std::swap(mask[i+j], mask[i+j+(1 << index)]); -					i += 1 << (index+1); -				} -				A[index] = y_bit; -				sink_cell->setPort(ID::A, A); -				sink_cell->setParam(ID(LUT), mask); -			} - -			// Since we have rewritten all sinks (which we know -			// to be only LUTs) to be after the inverter, we can -			// go ahead and clone the LUT with the expectation -			// that the original driving LUT will become dangling -			// and get cleaned away -clone_lut: -			driver_mask = driver_lut->getParam(ID(LUT)); -			for (auto &b : driver_mask.bits) { -				if (b == RTLIL::State::S0) b = RTLIL::State::S1; -				else if (b == RTLIL::State::S1) b = RTLIL::State::S0; -			} -			auto cell = module->addLut(NEW_ID, -					driver_lut->getPort(ID::A), -					y_bit, -					driver_mask); -			for (auto &bit : cell->connections_.at(ID::A)) { -				bit.wire = module->wires_.at(remap_name(bit.wire->name)); -				bit2sinks[bit].push_back(cell); -			} -		} - -		// Now 'unexpose' those wires by undoing -		// the expose operation -- remove them from PO/PI -		// and re-connecting them back together -		for (auto wire : module->wires()) { -			auto it = wire->attributes.find(ID(abc9_scc_break)); -			if (it != wire->attributes.end()) { -				wire->attributes.erase(it); -				log_assert(wire->port_output); -				wire->port_output = false; -				std::string name = wire->name.str(); -				RTLIL::Wire *i_wire = module->wire(name.substr(0, GetSize(name) - 5)); -				log_assert(i_wire); -				log_assert(i_wire->port_input); -				i_wire->port_input = false; -				module->connect(i_wire, wire); -			} -		} -		module->fixup_ports(); - -		//log("ABC RESULTS:        internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); -		log("ABC RESULTS:           input signals: %8d\n", in_wires); -		log("ABC RESULTS:          output signals: %8d\n", out_wires); - -		design->remove(mapped_mod); -	} -	else -	{ -		log("Don't call ABC as there is nothing to map.\n"); -	} - -	if (cleanup) -	{ -		log("Removing temp directory.\n"); -		remove_directory(tempdir_name); -	} - -	log_pop(); -} - -struct Abc9Pass : public Pass { -	Abc9Pass() : Pass("abc9", "use ABC9 for technology mapping") { } +	Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { }  	void on_register() YS_OVERRIDE  	{  		RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs"; @@ -782,8 +87,14 @@ struct Abc9Pass : public Pass {  		log("\n");  		log("    abc9 [options] [selection]\n");  		log("\n"); -		log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n"); -		log("library to a target architecture. Only fully-selected modules are supported.\n"); +		log("This script pass performs a sequence of commands to facilitate the use of the ABC\n"); +		log("tool [1] for technology mapping of the current design to a target FPGA\n"); +		log("architecture. Only fully-selected modules are supported.\n"); +		log("\n"); +		log("    -run <from_label>:<to_label>\n"); +		log("        only run the commands between the labels (see below). an empty\n"); +		log("        from label is synonymous to 'begin', and empty to label is\n"); +		log("        synonymous to the end of the command list.\n");  		log("\n");  		log("    -exe <command>\n");  #ifdef ABCEXTERNAL @@ -802,15 +113,11 @@ struct Abc9Pass : public Pass {  		log("        replaced with blanks before the string is passed to ABC.\n");  		log("\n");  		log("        if no -script parameter is given, the following scripts are used:\n"); -		log("\n"); -		log("        for -lut/-luts:\n");  		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str());  		log("\n");  		log("    -fast\n");  		log("        use different default scripts that are slightly faster (at the cost\n");  		log("        of output quality):\n"); -		log("\n"); -		log("        for -lut/-luts:\n");  		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str());  		log("\n");  		log("    -D <picoseconds>\n"); @@ -851,7 +158,7 @@ struct Abc9Pass : public Pass {  		log("        command output is identical across runs.\n");  		log("\n");  		log("    -box <file>\n"); -		log("        pass this file with box library to ABC. Use with -lut.\n"); +		log("        pass this file with box library to ABC.\n");  		log("\n");  		log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");  		log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); @@ -862,232 +169,152 @@ struct Abc9Pass : public Pass {  		log("\n");  		log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");  		log("\n"); +		help_script(); +		log("\n");  	} -	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE -	{ -		log_header(design, "Executing ABC9 pass (technology mapping using ABC9).\n"); -		log_push(); -#ifdef ABCEXTERNAL -		std::string exe_file = ABCEXTERNAL; -#else -		std::string exe_file = proc_self_dirname() + "yosys-abc"; -#endif -		std::string script_file, clk_str, box_file, lut_file; -		std::string delay_target, lutin_shared = "-S 1", wire_delay; -		bool fast_mode = false, dff_mode = false, cleanup = true; -		bool show_tempdir = false; -		vector<int> lut_costs; +	std::stringstream exe_cmd; +	bool dff_mode, cleanup; -#if 0 -		cleanup = false; -		show_tempdir = true; -#endif +	void clear_flags() YS_OVERRIDE +	{ +		exe_cmd.str(""); +		exe_cmd << "abc9_exe"; +		dff_mode = false; +		cleanup = true; +	} -#ifdef _WIN32 -#ifndef ABCEXTERNAL -		if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) -			exe_file = proc_self_dirname() + "..\\yosys-abc"; -#endif -#endif +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		std::string run_from, run_to; +		clear_flags();  		// get arguments from scratchpad first, then override by command arguments -		std::string lut_arg, luts_arg; -		exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */); -		script_file = design->scratchpad_get_string("abc9.script", script_file); -		if (design->scratchpad.count("abc9.D")) { -			delay_target = "-D " + design->scratchpad_get_string("abc9.D"); -		} -		lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); -		luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); -		fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode);  		dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);  		cleanup = !design->scratchpad_get_bool("abc9.nocleanup", !cleanup); -		show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); -		box_file = design->scratchpad_get_string("abc9.box", box_file); -		if (design->scratchpad.count("abc9.W")) { -			wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); -		}  		if (design->scratchpad_get_bool("abc9.debug")) {  			cleanup = false; -			show_tempdir = true; +			exe_cmd << " -showtmp";  		}  		size_t argidx; -		char pwd [PATH_MAX]; -		if (!getcwd(pwd, sizeof(pwd))) { -			log_cmd_error("getcwd failed: %s\n", strerror(errno)); -			log_abort(); -		}  		for (argidx = 1; argidx < args.size(); argidx++) {  			std::string arg = args[argidx]; -			if (arg == "-exe" && argidx+1 < args.size()) { -				exe_file = args[++argidx]; -				continue; -			} -			if (arg == "-script" && argidx+1 < args.size()) { -				script_file = args[++argidx]; -				continue; -			} -			if (arg == "-D" && argidx+1 < args.size()) { -				delay_target = "-D " + args[++argidx]; -				continue; -			} -			//if (arg == "-S" && argidx+1 < args.size()) { -			//	lutin_shared = "-S " + args[++argidx]; -			//	continue; -			//} -			if (arg == "-lut" && argidx+1 < args.size()) { -				lut_arg = args[++argidx]; +			if ((arg == "-exe" || arg == "-script" || arg == "-D" || +						/* arg == "-S" || */ arg == "-lut" || arg == "-luts" || +						arg == "-box" || arg == "-W") && +					argidx+1 < args.size()) { +				exe_cmd << " " << arg << " " << args[++argidx];  				continue;  			} -			if (arg == "-luts" && argidx+1 < args.size()) { -				luts_arg = args[++argidx]; -				continue; -			} -			if (arg == "-fast") { -				fast_mode = true; +			if (arg == "-fast" || /* arg == "-dff" || */ +					/* arg == "-nocleanup" || */ arg == "-showtmp") { +				exe_cmd << " " << arg;  				continue;  			}  			if (arg == "-dff") {  				dff_mode = true; +				exe_cmd << " " << arg;  				continue;  			}  			if (arg == "-nocleanup") {  				cleanup = false;  				continue;  			} -			if (arg == "-showtmp") { -				show_tempdir = true; -				continue; -			} -			if (arg == "-box" && argidx+1 < args.size()) { -				box_file = args[++argidx]; -				continue; -			} -			if (arg == "-W" && argidx+1 < args.size()) { -				wire_delay = "-W " + args[++argidx]; +			if (arg == "-run" && argidx+1 < args.size()) { +				size_t pos = args[argidx+1].find(':'); +				if (pos == std::string::npos) +					break; +				run_from = args[++argidx].substr(0, pos); +				run_to = args[argidx].substr(pos+1);  				continue;  			}  			break;  		}  		extra_args(args, argidx, design); -		rewrite_filename(script_file); -		if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') -			script_file = std::string(pwd) + "/" + script_file; - -		// handle -lut / -luts args -		if (!lut_arg.empty()) { -			string arg = lut_arg; -			if (arg.find_first_not_of("0123456789:") == std::string::npos) { -				size_t pos = arg.find_first_of(':'); -				int lut_mode = 0, lut_mode2 = 0; -				if (pos != string::npos) { -					lut_mode = atoi(arg.substr(0, pos).c_str()); -					lut_mode2 = atoi(arg.substr(pos+1).c_str()); -				} else { -					lut_mode = atoi(arg.c_str()); -					lut_mode2 = lut_mode; -				} -				lut_costs.clear(); -				for (int i = 0; i < lut_mode; i++) -					lut_costs.push_back(1); -				for (int i = lut_mode; i < lut_mode2; i++) -					lut_costs.push_back(2 << (i - lut_mode)); -			} -			else { -				lut_file = arg; -				rewrite_filename(lut_file); -				if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+') -					lut_file = std::string(pwd) + "/" + lut_file; -			} -		} -		if (!luts_arg.empty()) { -			lut_costs.clear(); -			for (auto &tok : split_tokens(luts_arg, ",")) { -				auto parts = split_tokens(tok, ":"); -				if (GetSize(parts) == 0 && !lut_costs.empty()) -					lut_costs.push_back(lut_costs.back()); -				else if (GetSize(parts) == 1) -					lut_costs.push_back(atoi(parts.at(0).c_str())); -				else if (GetSize(parts) == 2) -					while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) -						lut_costs.push_back(atoi(parts.at(1).c_str())); -				else -					log_cmd_error("Invalid -luts syntax.\n"); -			} -		} +		log_header(design, "Executing ABC9 pass.\n"); -		// ABC expects a box file for XAIG -		if (box_file.empty()) -		    box_file = "+/dummy.box"; +		run_script(design, run_from, run_to); +	} -		rewrite_filename(box_file); -		if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') -		    box_file = std::string(pwd) + "/" + box_file; +	void script() YS_OVERRIDE +	{ +		if (check_label("pre")) { +			run("scc -set_attr abc9_scc_id {}"); +			if (help_mode) +				run("abc9_ops -mark_scc -prep_xaiger [-dff]", "(option for -dff)"); +			else +				run("abc9_ops -mark_scc -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)"); +			run("select -set abc9_holes A:abc9_holes"); +			run("flatten -wb @abc9_holes"); +			run("techmap @abc9_holes"); +			if (dff_mode || help_mode) +				run("abc9_ops -prep_dff", "(only if -dff)"); +			run("opt -purge @abc9_holes"); +			run("aigmap"); +			run("wbflip @abc9_holes"); +		} -		SigMap assign_map; -		CellTypes ct(design); -		for (auto module : design->selected_modules()) -		{ -			if (module->processes.size() > 0) { -				log("Skipping module %s as it contains processes.\n", log_id(module)); -				continue; +		if (check_label("map")) { +			if (help_mode) { +				run("foreach module in selection"); +				run("    write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig"); +				run("    abc9_exe -cwd <abc-temp-dir> [options]"); +				run("    read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig"); +				run("    abc9_ops -reintegrate");  			} -			log_assert(!module->attributes.count(ID(abc9_box_id))); +			else { +				auto selected_modules = active_design->selected_modules(); +				active_design->selection_stack.emplace_back(false); -			if (!design->selected_whole_module(module)) -				log_error("Can't handle partially selected module %s!\n", log_id(module)); +				for (auto mod : selected_modules) { +					if (mod->processes.size() > 0) { +						log("Skipping module %s as it contains processes.\n", log_id(mod)); +						continue; +					} +					log_assert(!mod->attributes.count(ID(abc9_box_id))); -			assign_map.set(module); +					active_design->selection().select(mod); -			typedef SigSpec clkdomain_t; -			dict<clkdomain_t, int> clk_to_mergeability; +					if (!active_design->selected_whole_module(mod)) +						log_error("Can't handle partially selected module %s!\n", log_id(mod)); -			if (dff_mode) -				for (auto cell : module->cells()) { -					if (cell->type != "$__ABC9_FF_") -						continue; +					std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; +					if (!cleanup) +						tempdir_name[0] = tempdir_name[4] = '_'; +					tempdir_name = make_temp_dir(tempdir_name); + +					run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); -					Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str())); -					if (abc9_clock_wire == NULL) -						log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); -					SigSpec abc9_clock = assign_map(abc9_clock_wire); +					int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs"); -					clkdomain_t key(abc9_clock); +					log("Extracted %d AND gates and %d wires from module `%s' to a netlist network with %d inputs and %d outputs.\n", +							active_design->scratchpad_get_int("write_xaiger.num_ands"), +							active_design->scratchpad_get_int("write_xaiger.num_wires"), +							log_id(mod), +							active_design->scratchpad_get_int("write_xaiger.num_inputs"), +							num_outputs); +					if (num_outputs) { +						run(stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str())); +						run(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str())); +						run("abc9_ops -reintegrate"); +					} +					else +						log("Don't call ABC as there is nothing to map.\n"); -					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); +					if (cleanup) { +						log("Removing temp directory.\n"); +						remove_directory(tempdir_name); +					} -					Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str())); -					if (abc9_init_wire == NULL) -						log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); -					log_assert(GetSize(abc9_init_wire) == 1); -					SigSpec abc9_init = assign_map(abc9_init_wire); -					if (!abc9_init.is_fully_const()) -						log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); -					r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const())); -					log_assert(r2.second); -				} -			else -				for (auto cell : module->cells()) { -					auto inst_module = design->module(cell->type); -					if (!inst_module || !inst_module->get_bool_attribute("\\abc9_flop")) -						continue; -					cell->set_bool_attribute("\\abc9_keep"); +					active_design->selection().selected_modules.clear();  				} -			design->selected_active_module = module->name.str(); -			abc9_module(design, module, script_file, exe_file, cleanup, lut_costs, dff_mode, -					delay_target, lutin_shared, fast_mode, show_tempdir, -					box_file, lut_file, wire_delay); -			design->selected_active_module.clear(); +				active_design->selection_stack.pop_back(); +			}  		} - -		log_pop();  	}  } Abc9Pass; diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc new file mode 100644 index 000000000..a2acfac91 --- /dev/null +++ b/passes/techmap/abc9_exe.cc @@ -0,0 +1,531 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *                2019  Eddie Hung <eddie@fpgeh.com> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// [[CITE]] ABC +// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification +// http://www.eecs.berkeley.edu/~alanmi/abc/ + +#include "kernel/register.h" +#include "kernel/log.h" + +#ifndef _WIN32 +#  include <unistd.h> +#  include <dirent.h> +#endif + +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + +std::string fold_abc9_cmd(std::string str) +{ +	std::string token, new_str = "          "; +	int char_counter = 10; + +	for (size_t i = 0; i <= str.size(); i++) { +		if (i < str.size()) +			token += str[i]; +		if (i == str.size() || str[i] == ';') { +			if (char_counter + token.size() > 75) +				new_str += "\n              ", char_counter = 14; +			new_str += token, char_counter += token.size(); +			token.clear(); +		} +	} + +	return new_str; +} + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +std::string add_echos_to_abc9_cmd(std::string str) +{ +	std::string new_str, token; +	for (size_t i = 0; i < str.size(); i++) { +		token += str[i]; +		if (str[i] == ';') { +			while (i+1 < str.size() && str[i+1] == ' ') +				i++; +			new_str += "echo + " + token + " " + token + " "; +			token.clear(); +		} +	} + +	if (!token.empty()) { +		if (!new_str.empty()) +			new_str += "echo + " + token + "; "; +		new_str += token; +	} + +	return new_str; +} + +std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +{ +	if (show_tempdir) +		return text; + +	while (1) { +		size_t pos = text.find(tempdir_name); +		if (pos == std::string::npos) +			break; +		text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); +	} + +	std::string  selfdir_name = proc_self_dirname(); +	if (selfdir_name != "/") { +		while (1) { +			size_t pos = text.find(selfdir_name); +			if (pos == std::string::npos) +				break; +			text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); +		} +	} + +	return text; +} + +struct abc9_output_filter +{ +	bool got_cr; +	int escape_seq_state; +	std::string linebuf; +	std::string tempdir_name; +	bool show_tempdir; + +	abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) +	{ +		got_cr = false; +		escape_seq_state = 0; +	} + +	void next_char(char ch) +	{ +		if (escape_seq_state == 0 && ch == '\033') { +			escape_seq_state = 1; +			return; +		} +		if (escape_seq_state == 1) { +			escape_seq_state = ch == '[' ? 2 : 0; +			return; +		} +		if (escape_seq_state == 2) { +			if ((ch < '0' || '9' < ch) && ch != ';') +				escape_seq_state = 0; +			return; +		} +		escape_seq_state = 0; +		if (ch == '\r') { +			got_cr = true; +			return; +		} +		if (ch == '\n') { +			log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); +			got_cr = false, linebuf.clear(); +			return; +		} +		if (got_cr) +			got_cr = false, linebuf.clear(); +		linebuf += ch; +	} + +	void next_line(const std::string &line) +	{ +		//int pi, po; +		//if (sscanf(line.c_str(), "Start-point = pi%d.  End-point = po%d.", &pi, &po) == 2) { +		//	log("ABC: Start-point = pi%d (%s).  End-point = po%d (%s).\n", +		//			pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", +		//			po, po_map.count(po) ? po_map.at(po).c_str() : "???"); +		//	return; +		//} + +		for (char ch : line) +			next_char(ch); +	} +}; + +void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file, +		vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, +		bool show_tempdir, std::string box_file, std::string lut_file, +		std::string wire_delay, std::string tempdir_name +) +{ +	std::string abc9_script; + +	if (!lut_costs.empty()) +		abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); +	else if (!lut_file.empty()) +		abc9_script += stringf("read_lut %s; ", lut_file.c_str()); +	else +		log_abort(); + +	log_assert(!box_file.empty()); +	abc9_script += stringf("read_box %s; ", box_file.c_str()); +	abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); + +	if (!script_file.empty()) { +		if (script_file[0] == '+') { +			for (size_t i = 1; i < script_file.size(); i++) +				if (script_file[i] == '\'') +					abc9_script += "'\\''"; +				else if (script_file[i] == ',') +					abc9_script += " "; +				else +					abc9_script += script_file[i]; +		} else +			abc9_script += stringf("source %s", script_file.c_str()); +	} else if (!lut_costs.empty() || !lut_file.empty()) { +		abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos) +			: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos); +	} else +		log_abort(); + +	for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) +		abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); + +	//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos)) +	//	abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3); + +	for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) +		abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); + +	std::string C; +	if (design->scratchpad.count("abc9.if.C")) +		C = "-C " + design->scratchpad_get_string("abc9.if.C"); +	for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos)) +		abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3); + +	std::string R; +	if (design->scratchpad.count("abc9.if.R")) +		R = "-R " + design->scratchpad_get_string("abc9.if.R"); +	for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos)) +		abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3); + +	abc9_script += stringf("; &ps -l; &write -n %s/output.aig;", tempdir_name.c_str()); +	if (design->scratchpad_get_bool("abc9.verify")) { +		if (dff_mode) +			abc9_script += "verify -s;"; +		else +			abc9_script += "verify;"; +	} +	abc9_script += "time"; +	abc9_script = add_echos_to_abc9_cmd(abc9_script); + +	for (size_t i = 0; i+1 < abc9_script.size(); i++) +		if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') +			abc9_script[i+1] = '\n'; + +	FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); +	fprintf(f, "%s\n", abc9_script.c_str()); +	fclose(f); + +	std::string buffer; + +	log_header(design, "Executing ABC9.\n"); + +	if (!lut_costs.empty()) { +		buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); +		f = fopen(buffer.c_str(), "wt"); +		if (f == NULL) +			log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); +		for (int i = 0; i < GetSize(lut_costs); i++) +			fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); +		fclose(f); +	} + +	buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); +	log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + +#ifndef YOSYS_LINK_ABC +	abc9_output_filter filt(tempdir_name, show_tempdir); +	int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); +#else +	// These needs to be mutable, supposedly due to getopt +	char *abc9_argv[5]; +	string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); +	abc9_argv[0] = strdup(exe_file.c_str()); +	abc9_argv[1] = strdup("-s"); +	abc9_argv[2] = strdup("-f"); +	abc9_argv[3] = strdup(tmp_script_name.c_str()); +	abc9_argv[4] = 0; +	int ret = Abc_RealMain(4, abc9_argv); +	free(abc9_argv[0]); +	free(abc9_argv[1]); +	free(abc9_argv[2]); +	free(abc9_argv[3]); +#endif +	if (ret != 0) +		log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); +} + +struct Abc9ExePass : public Pass { +	Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    abc9_exe [options]\n"); +		log("\n"); +		log(" \n"); +		log("This pass uses the ABC tool [1] for technology mapping of the top module\n"); +		log("(according to the (* top *) attribute or if only one module is currently selected)\n"); +		log("to a target FPGA architecture.\n"); +		log("\n"); +		log("    -exe <command>\n"); +#ifdef ABCEXTERNAL +		log("        use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else +		log("        use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif +		log("        This can e.g. be used to call a specific version of ABC or a wrapper.\n"); +		log("\n"); +		log("    -script <file>\n"); +		log("        use the specified ABC script file instead of the default script.\n"); +		log("\n"); +		log("        if <file> starts with a plus sign (+), then the rest of the filename\n"); +		log("        string is interpreted as the command string to be passed to ABC. The\n"); +		log("        leading plus sign is removed and all commas (,) in the string are\n"); +		log("        replaced with blanks before the string is passed to ABC.\n"); +		log("\n"); +		log("        if no -script parameter is given, the following scripts are used:\n"); +		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str()); +		log("\n"); +		log("    -fast\n"); +		log("        use different default scripts that are slightly faster (at the cost\n"); +		log("        of output quality):\n"); +		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str()); +		log("\n"); +		log("    -D <picoseconds>\n"); +		log("        set delay target. the string {D} in the default scripts above is\n"); +		log("        replaced by this option when used, and an empty string otherwise\n"); +		log("        (indicating best possible delay).\n"); +		log("\n"); +//		log("    -S <num>\n"); +//		log("        maximum number of LUT inputs shared.\n"); +//		log("        (replaces {S} in the default scripts above, default: -S 1)\n"); +//		log("\n"); +		log("    -lut <width>\n"); +		log("        generate netlist using luts of (max) the specified width.\n"); +		log("\n"); +		log("    -lut <w1>:<w2>\n"); +		log("        generate netlist using luts of (max) the specified width <w2>. All\n"); +		log("        luts with width <= <w1> have constant cost. for luts larger than <w1>\n"); +		log("        the area cost doubles with each additional input bit. the delay cost\n"); +		log("        is still constant for all lut widths.\n"); +		log("\n"); +		log("    -lut <file>\n"); +		log("        pass this file with lut library to ABC.\n"); +		log("\n"); +		log("    -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n"); +		log("        generate netlist using luts. Use the specified costs for luts with 1,\n"); +		log("        2, 3, .. inputs.\n"); +		log("\n"); +		log("    -showtmp\n"); +		log("        print the temp dir name in log. usually this is suppressed so that the\n"); +		log("        command output is identical across runs.\n"); +		log("\n"); +		log("    -box <file>\n"); +		log("        pass this file with box library to ABC.\n"); +		log("\n"); +		log("    -cwd <dir>\n"); +		log("        use this as the current working directory, inside which the 'input.xaig'\n"); +		log("        file is expected. temporary files will be created in this directory, and\n"); +		log("        the mapped result will be written to 'output.aig'.\n"); +		log("\n"); +		log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); +		log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); +		log("ABC on logic snippets extracted from your design. You will not get any useful\n"); +		log("output when passing an ABC script that writes a file. Instead write your full\n"); +		log("design as BLIF file with write_blif and then load that into ABC externally if\n"); +		log("you want to use ABC to convert your design into another format.\n"); +		log("\n"); +		log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n"); + +#ifdef ABCEXTERNAL +		std::string exe_file = ABCEXTERNAL; +#else +		std::string exe_file = proc_self_dirname() + "yosys-abc"; +#endif +		std::string script_file, clk_str, box_file, lut_file; +		std::string delay_target, lutin_shared = "-S 1", wire_delay; +		std::string tempdir_name; +		bool fast_mode = false, dff_mode = false; +		bool show_tempdir = false; +		vector<int> lut_costs; + +#if 0 +		cleanup = false; +		show_tempdir = true; +#endif + +#ifdef _WIN32 +#ifndef ABCEXTERNAL +		if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) +			exe_file = proc_self_dirname() + "..\\yosys-abc"; +#endif +#endif + +		std::string lut_arg, luts_arg; +		exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */); +		script_file = design->scratchpad_get_string("abc9.script", script_file); +		if (design->scratchpad.count("abc9.D")) { +			delay_target = "-D " + design->scratchpad_get_string("abc9.D"); +		} +		lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); +		luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); +		fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode); +		dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode); +		show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); +		box_file = design->scratchpad_get_string("abc9.box", box_file); +		if (design->scratchpad.count("abc9.W")) { +			wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); +		} + +		size_t argidx; +		char pwd [PATH_MAX]; +		if (!getcwd(pwd, sizeof(pwd))) { +			log_cmd_error("getcwd failed: %s\n", strerror(errno)); +			log_abort(); +		} +		for (argidx = 1; argidx < args.size(); argidx++) { +			std::string arg = args[argidx]; +			if (arg == "-exe" && argidx+1 < args.size()) { +				exe_file = args[++argidx]; +				continue; +			} +			if (arg == "-script" && argidx+1 < args.size()) { +				script_file = args[++argidx]; +				continue; +			} +			if (arg == "-D" && argidx+1 < args.size()) { +				delay_target = "-D " + args[++argidx]; +				continue; +			} +			//if (arg == "-S" && argidx+1 < args.size()) { +			//	lutin_shared = "-S " + args[++argidx]; +			//	continue; +			//} +			if (arg == "-lut" && argidx+1 < args.size()) { +				lut_arg = args[++argidx]; +				continue; +			} +			if (arg == "-luts" && argidx+1 < args.size()) { +				lut_arg = args[++argidx]; +				continue; +			} +			if (arg == "-fast") { +				fast_mode = true; +				continue; +			} +			if (arg == "-dff") { +				dff_mode = true; +				continue; +			} +			if (arg == "-showtmp") { +				show_tempdir = true; +				continue; +			} +			if (arg == "-box" && argidx+1 < args.size()) { +				box_file = args[++argidx]; +				continue; +			} +			if (arg == "-W" && argidx+1 < args.size()) { +				wire_delay = "-W " + args[++argidx]; +				continue; +			} +			if (arg == "-cwd" && argidx+1 < args.size()) { +				tempdir_name = args[++argidx]; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		rewrite_filename(script_file); +		if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') +			script_file = std::string(pwd) + "/" + script_file; + +		// handle -lut / -luts args +		if (!lut_arg.empty()) { +			string arg = lut_arg; +			if (arg.find_first_not_of("0123456789:") == std::string::npos) { +				size_t pos = arg.find_first_of(':'); +				int lut_mode = 0, lut_mode2 = 0; +				if (pos != string::npos) { +					lut_mode = atoi(arg.substr(0, pos).c_str()); +					lut_mode2 = atoi(arg.substr(pos+1).c_str()); +				} else { +					lut_mode = atoi(arg.c_str()); +					lut_mode2 = lut_mode; +				} +				lut_costs.clear(); +				for (int i = 0; i < lut_mode; i++) +					lut_costs.push_back(1); +				for (int i = lut_mode; i < lut_mode2; i++) +					lut_costs.push_back(2 << (i - lut_mode)); +			} +			else { +				lut_file = arg; +				rewrite_filename(lut_file); +				if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+') +					lut_file = std::string(pwd) + "/" + lut_file; +			} +		} +		if (!luts_arg.empty()) { +			lut_costs.clear(); +			for (auto &tok : split_tokens(luts_arg, ",")) { +				auto parts = split_tokens(tok, ":"); +				if (GetSize(parts) == 0 && !lut_costs.empty()) +					lut_costs.push_back(lut_costs.back()); +				else if (GetSize(parts) == 1) +					lut_costs.push_back(atoi(parts.at(0).c_str())); +				else if (GetSize(parts) == 2) +					while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) +						lut_costs.push_back(atoi(parts.at(1).c_str())); +				else +					log_cmd_error("Invalid -luts syntax.\n"); +			} +		} + +		// ABC expects a box file for XAIG +		if (box_file.empty()) +			box_file = "+/dummy.box"; + +		rewrite_filename(box_file); +		if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') +			box_file = std::string(pwd) + "/" + box_file; + +		if (tempdir_name.empty()) +			log_cmd_error("abc9_exe '-cwd' option is mandatory.\n"); + + +		abc9_module(design, script_file, exe_file, lut_costs, dff_mode, +				delay_target, lutin_shared, fast_mode, show_tempdir, +				box_file, lut_file, wire_delay, tempdir_name); +	} +} Abc9ExePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc new file mode 100644 index 000000000..aa21ff283 --- /dev/null +++ b/passes/techmap/abc9_ops.cc @@ -0,0 +1,814 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *                2019  Eddie Hung <eddie@fpgeh.com> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" +#include "kernel/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +int map_autoidx; + +inline std::string remap_name(RTLIL::IdString abc9_name) +{ +	return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); +} + +void mark_scc(RTLIL::Module *module) +{ +	// For every unique SCC found, (arbitrarily) find the first +	//   cell in the component, and convert all wires driven by +	//   its output ports into a new PO, and drive its previous +	//   sinks with a new PI +	pool<RTLIL::Const> ids_seen; +	for (auto cell : module->cells()) { +		auto it = cell->attributes.find(ID(abc9_scc_id)); +		if (it == cell->attributes.end()) +			continue; +		auto id = it->second; +		auto r = ids_seen.insert(id); +		cell->attributes.erase(it); +		if (!r.second) +			continue; +		for (auto &c : cell->connections_) { +			if (c.second.is_fully_const()) continue; +			if (cell->output(c.first)) { +				SigBit b = c.second.as_bit(); +				Wire *w = b.wire; +				w->set_bool_attribute(ID::keep); +				w->attributes[ID(abc9_scc_id)] = id.as_int(); +			} +		} +	} + +	module->fixup_ports(); +} + +void prep_dff(RTLIL::Module *module) +{ +	auto design = module->design; +	log_assert(design); + +	SigMap assign_map(module); + +	typedef SigSpec clkdomain_t; +	dict<clkdomain_t, int> clk_to_mergeability; + +	for (auto cell : module->cells()) { +		if (cell->type != "$__ABC9_FF_") +			continue; + +		Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str())); +		if (abc9_clock_wire == NULL) +			log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		SigSpec abc9_clock = assign_map(abc9_clock_wire); + +		clkdomain_t key(abc9_clock); + +		auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1)); +		auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second)); +		log_assert(r2.second); + +		Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str())); +		if (abc9_init_wire == NULL) +			log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		log_assert(GetSize(abc9_init_wire) == 1); +		SigSpec abc9_init = assign_map(abc9_init_wire); +		if (!abc9_init.is_fully_const()) +			log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const())); +		log_assert(r2.second); +	} + +	RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str())); +	if (holes_module) { +		SigMap sigmap(holes_module); + +		dict<SigSpec, SigSpec> replace; +		for (auto it = holes_module->cells_.begin(); it != holes_module->cells_.end(); ) { +			auto cell = it->second; +			if (cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", +						"$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) { +				SigBit D = cell->getPort("\\D"); +				SigBit Q = cell->getPort("\\Q"); +				// Remove the $_DFF_* cell from what needs to be a combinatorial box +				it = holes_module->cells_.erase(it); +				Wire *port; +				if (GetSize(Q.wire) == 1) +					port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str())); +				else +					port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset)); +				log_assert(port); +				// Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;" +				//   in order to extract just the combinatorial control logic that feeds the box +				//   (i.e. clock enable, synchronous reset, etc.) +				replace.insert(std::make_pair(Q,D)); +				// Since `flatten` above would have created wires named "<cell>.Q", +				//   extract the pre-techmap cell name +				auto pos = Q.wire->name.str().rfind("."); +				log_assert(pos != std::string::npos); +				IdString driver = Q.wire->name.substr(0, pos); +				// And drive the signal that was previously driven by "DFF.Q" (typically +				//   used to implement clock-enable functionality) with the "<cell>.$abc9_currQ" +				//   wire (which itself is driven an by input port) we inserted above +				Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str())); +				log_assert(currQ); +				holes_module->connect(Q, currQ); +			} +			else +				++it; +		} + +		for (auto &conn : holes_module->connections_) +			conn.second = replace.at(sigmap(conn.second), conn.second); +	} +} + +void prep_xaiger(RTLIL::Module *module, bool dff) +{ +	auto design = module->design; +	log_assert(design); + +	SigMap sigmap(module); + +	dict<SigBit, pool<IdString>> bit_drivers, bit_users; +	TopoSort<IdString, RTLIL::sort_by_id_str> toposort; +	dict<IdString, std::vector<IdString>> box_ports; + +	for (auto cell : module->cells()) { +		if (cell->type == "$__ABC9_FF_") +			continue; + +		auto inst_module = module->design->module(cell->type); +		bool abc9_box = inst_module && inst_module->attributes.count("\\abc9_box_id"); +		bool abc9_flop = false; +		if (abc9_box) { +			abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); +			if (abc9_flop && !dff) +				continue; + +			auto r = box_ports.insert(cell->type); +			if (r.second) { +				// Make carry in the last PI, and carry out the last PO +				//   since ABC requires it this way +				IdString carry_in, carry_out; +				for (const auto &port_name : inst_module->ports) { +					auto w = inst_module->wire(port_name); +					log_assert(w); +					if (w->get_bool_attribute("\\abc9_carry")) { +						if (w->port_input) { +							if (carry_in != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(inst_module)); +							carry_in = port_name; +						} +						if (w->port_output) { +							if (carry_out != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(inst_module)); +							carry_out = port_name; +						} +					} +					else +						r.first->second.push_back(port_name); +				} + +				if (carry_in != IdString() && carry_out == IdString()) +					log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(inst_module)); +				if (carry_in == IdString() && carry_out != IdString()) +					log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(inst_module)); +				if (carry_in != IdString()) { +					r.first->second.push_back(carry_in); +					r.first->second.push_back(carry_out); +				} +			} +		} +		else if (!yosys_celltypes.cell_known(cell->type)) +			continue; + +		for (auto conn : cell->connections()) { +			if (cell->input(conn.first)) +				for (auto bit : sigmap(conn.second)) +					bit_users[bit].insert(cell->name); + +			if (cell->output(conn.first) && !abc9_flop) +				for (auto bit : sigmap(conn.second)) +					bit_drivers[bit].insert(cell->name); +		} + +		toposort.node(cell->name); +	} + +	if (box_ports.empty()) +		return; + +	for (auto &it : bit_users) +		if (bit_drivers.count(it.first)) +			for (auto driver_cell : bit_drivers.at(it.first)) +			for (auto user_cell : it.second) +				toposort.edge(driver_cell, user_cell); + +	if (ys_debug(1)) +		toposort.analyze_loops = true; + +	bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); + +	if (ys_debug(1)) { +		unsigned i = 0; +		for (auto &it : toposort.loops) { +			log("  loop %d\n", i++); +			for (auto cell_name : it) { +				auto cell = module->cell(cell_name); +				log_assert(cell); +				log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); +			} +		} +	} + +	log_assert(no_loops); + +	RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str())); +	log_assert(holes_module); +	holes_module->set_bool_attribute("\\abc9_holes"); + +	dict<IdString, Cell*> cell_cache; + +	int port_id = 1, box_count = 0; +	for (auto cell_name : toposort.sorted) { +		RTLIL::Cell *cell = module->cell(cell_name); +		log_assert(cell); + +		RTLIL::Module* box_module = design->module(cell->type); +		if (!box_module || !box_module->attributes.count("\\abc9_box_id")) +			continue; + +		cell->attributes["\\abc9_box_seq"] = box_count++; + +		IdString derived_name = box_module->derive(design, cell->parameters); +		box_module = design->module(derived_name); + +		auto r = cell_cache.insert(derived_name); +		auto &holes_cell = r.first->second; +		if (r.second) { +			if (box_module->has_processes()) +				Pass::call_on_module(design, box_module, "proc"); + +			if (box_module->get_bool_attribute("\\whitebox")) { +				holes_cell = holes_module->addCell(cell->name, derived_name); + +				int box_inputs = 0; +				for (auto port_name : box_ports.at(cell->type)) { +					RTLIL::Wire *w = box_module->wire(port_name); +					log_assert(w); +					log_assert(!w->port_input || !w->port_output); +					auto &conn = holes_cell->connections_[port_name]; +					if (w->port_input) { +						for (int i = 0; i < GetSize(w); i++) { +							box_inputs++; +							RTLIL::Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); +							if (!holes_wire) { +								holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); +								holes_wire->port_input = true; +								holes_wire->port_id = port_id++; +								holes_module->ports.push_back(holes_wire->name); +							} +							conn.append(holes_wire); +						} +					} +					else if (w->port_output) +						conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w)); +				} + +				// For flops only, create an extra 1-bit input that drives a new wire +				//   called "<cell>.abc9_ff.Q" that is used below +				if (box_module->get_bool_attribute("\\abc9_flop")) { +					box_inputs++; +					Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); +					if (!holes_wire) { +						holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); +						holes_wire->port_input = true; +						holes_wire->port_id = port_id++; +						holes_module->ports.push_back(holes_wire->name); +					} +					Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str())); +					holes_module->connect(Q, holes_wire); +				} +			} +			else // box_module is a blackbox +				log_assert(holes_cell == nullptr); +		} + +		for (auto port_name : box_ports.at(cell->type)) { +			RTLIL::Wire *w = box_module->wire(port_name); +			log_assert(w); +			if (!w->port_output) +				continue; +			Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w)); +			holes_wire->port_output = true; +			holes_wire->port_id = port_id++; +			holes_module->ports.push_back(holes_wire->name); +			if (holes_cell) // whitebox +				holes_module->connect(holes_wire, holes_cell->getPort(port_name)); +			else // blackbox +				holes_module->connect(holes_wire, Const(State::S0, GetSize(w))); +		} +	} +} + +void reintegrate(RTLIL::Module *module) +{ +	auto design = module->design; +	log_assert(design); + +	map_autoidx = autoidx++; + +	RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str())); +	if (mapped_mod == NULL) +		log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module)); + +	for (auto w : mapped_mod->wires()) +		module->addWire(remap_name(w->name), GetSize(w)); + +	dict<IdString,IdString> box_lookup; +	dict<IdString,std::vector<IdString>> box_ports; + +	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(stringf("$__boxid%d", 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); + +		auto r2 = box_ports.insert(m->name); +		if (r2.second) { +			// Make carry in the last PI, and carry out the last PO +			//   since ABC requires it this way +			IdString carry_in, carry_out; +			for (const auto &port_name : m->ports) { +				auto w = m->wire(port_name); +				log_assert(w); +				if (w->get_bool_attribute("\\abc9_carry")) { +					if (w->port_input) { +						if (carry_in != IdString()) +							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); +						carry_in = port_name; +					} +					if (w->port_output) { +						if (carry_out != IdString()) +							log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(m)); +						carry_out = port_name; +					} +				} +				else +					r2.first->second.push_back(port_name); +			} + +			if (carry_in != IdString() && carry_out == IdString()) +				log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m)); +			if (carry_in == IdString() && carry_out != IdString()) +				log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m)); +			if (carry_in != IdString()) { +				r2.first->second.push_back(carry_in); +				r2.first->second.push_back(carry_out); +			} +		} +	} + +	std::vector<Cell*> boxes; +	for (auto cell : module->cells().to_vector()) { +		if (cell->has_keep_attr()) +			continue; +		if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) +			module->remove(cell); +		else if (cell->attributes.erase("\\abc9_box_seq")) +			boxes.emplace_back(cell); +	} + +	dict<SigBit, pool<IdString>> bit_drivers, bit_users; +	TopoSort<IdString, RTLIL::sort_by_id_str> toposort; +	dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; +	dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; + +	std::map<IdString, int> cell_stats; +	for (auto mapped_cell : mapped_mod->cells()) +	{ +		toposort.node(mapped_cell->name); + +		if (mapped_cell->type == ID($_NOT_)) { +			RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A); +			RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); +			bit_users[a_bit].insert(mapped_cell->name); +			// Ignore inouts for topo ordering +			if (y_bit.wire && !(y_bit.wire->port_input && y_bit.wire->port_output)) +				bit_drivers[y_bit].insert(mapped_cell->name); + +			if (!a_bit.wire) { +				mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); +				RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); +				log_assert(wire); +				module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); +			} +			else { +				RTLIL::Cell* driver_lut = nullptr; +				// ABC can return NOT gates that drive POs +				if (!a_bit.wire->port_input) { +					// If it's not a NOT gate that that comes from a PI directly, +					// find the driver LUT and clone that to guarantee that we won't +					// increase the max logic depth +					// (TODO: Optimise by not cloning unless will increase depth) +					RTLIL::IdString driver_name; +					if (GetSize(a_bit.wire) == 1) +						driver_name = stringf("$lut%s", a_bit.wire->name.c_str()); +					else +						driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset); +					driver_lut = mapped_mod->cell(driver_name); +				} + +				if (!driver_lut) { +					// If a driver couldn't be found (could be from PI or box CI) +					// then implement using a LUT +					RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())), +							RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), +							RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), +							RTLIL::Const::from_string("01")); +					bit2sinks[cell->getPort(ID::A)].push_back(cell); +					cell_stats[ID($lut)]++; +				} +				else +					not2drivers[mapped_cell] = driver_lut; +			} +			continue; +		} + +		if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { +			// Convert buffer into direct connection +			if (mapped_cell->type == ID($lut) && +					GetSize(mapped_cell->getPort(ID::A)) == 1 && +					mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { +				SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name)); +				SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name)); +				module->connect(my_y, my_a); +				log_abort(); +				continue; +			} +			RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); +			cell->parameters = mapped_cell->parameters; +			cell->attributes = mapped_cell->attributes; + +			for (auto &mapped_conn : mapped_cell->connections()) { +				RTLIL::SigSpec newsig; +				for (auto c : mapped_conn.second.chunks()) { +					if (c.width == 0) +						continue; +					//log_assert(c.width == 1); +					if (c.wire) +						c.wire = module->wires_.at(remap_name(c.wire->name)); +					newsig.append(c); +				} +				cell->setPort(mapped_conn.first, newsig); + +				if (cell->input(mapped_conn.first)) { +					for (auto i : newsig) +						bit2sinks[i].push_back(cell); +					for (auto i : mapped_conn.second) +						bit_users[i].insert(mapped_cell->name); +				} +				if (cell->output(mapped_conn.first)) +					for (auto i : mapped_conn.second) +						// Ignore inouts for topo ordering +						if (i.wire && !(i.wire->port_input && i.wire->port_output)) +							bit_drivers[i].insert(mapped_cell->name); +			} +		} +		else { +			RTLIL::Cell *existing_cell = module->cell(mapped_cell->name); +			log_assert(existing_cell); +			log_assert(mapped_cell->type.begins_with("$__boxid")); + +			auto type = box_lookup.at(mapped_cell->type, IdString()); +			if (type == IdString()) +				log_error("No module with abc9_box_id = %s found.\n", mapped_cell->type.c_str() + strlen("$__boxid")); +			mapped_cell->type = type; + +			RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); +			cell->parameters = existing_cell->parameters; +			cell->attributes = existing_cell->attributes; +			module->swap_names(cell, existing_cell); + +			auto it = mapped_cell->connections_.find("\\i"); +			log_assert(it != mapped_cell->connections_.end()); +			SigSpec inputs = std::move(it->second); +			mapped_cell->connections_.erase(it); +			it = mapped_cell->connections_.find("\\o"); +			log_assert(it != mapped_cell->connections_.end()); +			SigSpec outputs = std::move(it->second); +			mapped_cell->connections_.erase(it); + +			RTLIL::Module* box_module = design->module(mapped_cell->type); +			auto abc9_flop = box_module->attributes.count("\\abc9_flop"); +			if (!abc9_flop) { +				for (const auto &i : inputs) +					bit_users[i].insert(mapped_cell->name); +				for (const auto &i : outputs) +					// Ignore inouts for topo ordering +					if (i.wire && !(i.wire->port_input && i.wire->port_output)) +						bit_drivers[i].insert(mapped_cell->name); +			} + +			int input_count = 0, output_count = 0; +			for (const auto &port_name : box_ports.at(cell->type)) { +				RTLIL::Wire *w = box_module->wire(port_name); +				log_assert(w); + +				SigSpec sig; +				if (w->port_input) { +					sig = inputs.extract(input_count, GetSize(w)); +					input_count += GetSize(w); +				} +				if (w->port_output) { +					sig = outputs.extract(output_count, GetSize(w)); +					output_count += GetSize(w); +				} + +				SigSpec newsig; +				for (auto c : sig.chunks()) { +					if (c.width == 0) +						continue; +					//log_assert(c.width == 1); +					if (c.wire) +						c.wire = module->wires_.at(remap_name(c.wire->name)); +					newsig.append(c); +				} +				cell->setPort(port_name, newsig); + +				if (w->port_input && !abc9_flop) +					for (const auto &i : newsig) +						bit2sinks[i].push_back(cell); +			} +		} + +		cell_stats[mapped_cell->type]++; +	} + +	for (auto cell : boxes) +		module->remove(cell); + +	// Copy connections (and rename) from mapped_mod to module +	for (auto conn : mapped_mod->connections()) { +		if (!conn.first.is_fully_const()) { +			auto chunks = conn.first.chunks(); +			for (auto &c : chunks) +				c.wire = module->wires_.at(remap_name(c.wire->name)); +			conn.first = std::move(chunks); +		} +		if (!conn.second.is_fully_const()) { +			auto chunks = conn.second.chunks(); +			for (auto &c : chunks) +				if (c.wire) +					c.wire = module->wires_.at(remap_name(c.wire->name)); +			conn.second = std::move(chunks); +		} +		module->connect(conn); +	} + +	for (auto &it : cell_stats) +		log("ABC RESULTS:   %15s cells: %8d\n", it.first.c_str(), it.second); +	int in_wires = 0, out_wires = 0; + +	// Stitch in mapped_mod's inputs/outputs into module +	for (auto port : mapped_mod->ports) { +		RTLIL::Wire *mapped_wire = mapped_mod->wire(port); +		RTLIL::Wire *wire = module->wire(port); +		log_assert(wire); +		if (wire->attributes.erase(ID(abc9_scc_id))) { +			auto r YS_ATTRIBUTE(unused) = wire->attributes.erase(ID::keep); +			log_assert(r); +		} +		RTLIL::Wire *remap_wire = module->wire(remap_name(port)); +		RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire)); +		log_assert(GetSize(signal) >= GetSize(remap_wire)); + +		RTLIL::SigSig conn; +		if (mapped_wire->port_output) { +			conn.first = signal; +			conn.second = remap_wire; +			out_wires++; +			module->connect(conn); +		} +		else if (mapped_wire->port_input) { +			conn.first = remap_wire; +			conn.second = signal; +			in_wires++; +			module->connect(conn); +		} +	} + +	for (auto &it : bit_users) +		if (bit_drivers.count(it.first)) +			for (auto driver_cell : bit_drivers.at(it.first)) +			for (auto user_cell : it.second) +				toposort.edge(driver_cell, user_cell); +	bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); +	log_assert(no_loops); + +	for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { +		RTLIL::Cell *not_cell = mapped_mod->cell(*ii); +		log_assert(not_cell); +		if (not_cell->type != ID($_NOT_)) +			continue; +		auto it = not2drivers.find(not_cell); +		if (it == not2drivers.end()) +			continue; +		RTLIL::Cell *driver_lut = it->second; +		RTLIL::SigBit a_bit = not_cell->getPort(ID::A); +		RTLIL::SigBit y_bit = not_cell->getPort(ID::Y); +		RTLIL::Const driver_mask; + +		a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); +		y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); + +		auto jt = bit2sinks.find(a_bit); +		if (jt == bit2sinks.end()) +			goto clone_lut; + +		for (auto sink_cell : jt->second) +			if (sink_cell->type != ID($lut)) +				goto clone_lut; + +		// Push downstream LUTs past inverter +		for (auto sink_cell : jt->second) { +			SigSpec A = sink_cell->getPort(ID::A); +			RTLIL::Const mask = sink_cell->getParam(ID(LUT)); +			int index = 0; +			for (; index < GetSize(A); index++) +				if (A[index] == a_bit) +					break; +			log_assert(index < GetSize(A)); +			int i = 0; +			while (i < GetSize(mask)) { +				for (int j = 0; j < (1 << index); j++) +					std::swap(mask[i+j], mask[i+j+(1 << index)]); +				i += 1 << (index+1); +			} +			A[index] = y_bit; +			sink_cell->setPort(ID::A, A); +			sink_cell->setParam(ID(LUT), mask); +		} + +		// Since we have rewritten all sinks (which we know +		// to be only LUTs) to be after the inverter, we can +		// go ahead and clone the LUT with the expectation +		// that the original driving LUT will become dangling +		// and get cleaned away +clone_lut: +		driver_mask = driver_lut->getParam(ID(LUT)); +		for (auto &b : driver_mask.bits) { +			if (b == RTLIL::State::S0) b = RTLIL::State::S1; +			else if (b == RTLIL::State::S1) b = RTLIL::State::S0; +		} +		auto cell = module->addLut(NEW_ID, +				driver_lut->getPort(ID::A), +				y_bit, +				driver_mask); +		for (auto &bit : cell->connections_.at(ID::A)) { +			bit.wire = module->wires_.at(remap_name(bit.wire->name)); +			bit2sinks[bit].push_back(cell); +		} +	} + +	//log("ABC RESULTS:        internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); +	log("ABC RESULTS:           input signals: %8d\n", in_wires); +	log("ABC RESULTS:          output signals: %8d\n", out_wires); + +	design->remove(mapped_mod); +} + +struct Abc9OpsPass : public Pass { +	Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    abc9_ops [options] [selection]\n"); +		log("\n"); +		log("This pass contains a set of supporting operations for use during ABC technology\n"); +		log("mapping, and is expected to be called in conjunction with other operations from\n"); +		log("the `abc9' script pass. Only fully-selected modules are supported.\n"); +		log("\n"); +		log("    -mark_scc\n"); +		log("        for an arbitrarily chosen cell in each unique SCC of each selected module\n"); +		log("        (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n"); +		log("        wires driven by this cell's outputs with a (* keep *) attribute in order\n"); +		log("        to break the SCC. this temporary attribute will be removed on -reintegrate.\n"); +		log("\n"); +		log("    -prep_xaiger\n"); +		log("        prepare the design for XAIGER output. this includes computing the\n"); +		log("        topological ordering of ABC9 boxes, as well as preparing the\n"); +		log("        '<module-name>$holes' module that contains the logic behaviour of ABC9\n"); +		log("        whiteboxes.\n"); +		log("\n"); +		log("    -dff\n"); +		log("        consider flop cells (those instantiating modules marked with (* abc9_flop *)\n"); +		log("        during -prep_xaiger.\n"); +		log("\n"); +		log("    -prep_dff\n"); +		log("        compute the clock domain and initial value of each flop in the design.\n"); +		log("        process the '$holes' module to support clock-enable functionality.\n"); +		log("\n"); +		log("    -reintegrate\n"); +		log("        for each selected module, re-intergrate the module '<module-name>$abc9'\n"); +		log("        by first recovering ABC9 boxes, and then stitching in the remaining primary\n"); +		log("        inputs and outputs.\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n"); + +		bool mark_scc_mode = false; +		bool prep_dff_mode = false; +		bool prep_xaiger_mode = false; +		bool reintegrate_mode = false; +		bool dff_mode = false; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			std::string arg = args[argidx]; +			if (arg == "-mark_scc") { +				mark_scc_mode = true; +				continue; +			} +			if (arg == "-prep_dff") { +				prep_dff_mode = true; +				continue; +			} +			if (arg == "-prep_xaiger") { +				prep_xaiger_mode = true; +				continue; +			} +			if (arg == "-reintegrate") { +				reintegrate_mode = true; +				continue; +			} +			if (arg == "-dff") { +				dff_mode = true; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		if (!(mark_scc_mode || prep_dff_mode || reintegrate_mode)) +			log_cmd_error("At least one of -mark_scc, -prep_{xaiger,dff}, -reintegrate must be specified.\n"); + +		if (dff_mode && !prep_xaiger_mode) +			log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n"); + +		for (auto mod : design->selected_modules()) { +			if (mod->get_bool_attribute("\\abc9_holes")) +				continue; + +			if (mod->processes.size() > 0) { +				log("Skipping module %s as it contains processes.\n", log_id(mod)); +				continue; +			} + +			if (!design->selected_whole_module(mod)) +				log_error("Can't handle partially selected module %s!\n", log_id(mod)); + +			if (mark_scc_mode) +				mark_scc(mod); +			if (prep_dff_mode) +				prep_dff(mod); +			if (prep_xaiger_mode) +				prep_xaiger(mod, dff_mode); +			if (reintegrate_mode) +				reintegrate(mod); +		} +	} +} Abc9OpsPass; + +PRIVATE_NAMESPACE_END | 
