diff options
Diffstat (limited to 'passes/techmap')
| -rw-r--r-- | passes/techmap/Makefile.inc | 2 | ||||
| -rw-r--r-- | passes/techmap/abc.cc | 399 | ||||
| -rw-r--r-- | passes/techmap/abc9.cc | 1335 | ||||
| -rw-r--r-- | passes/techmap/abc9_exe.cc | 531 | ||||
| -rw-r--r-- | passes/techmap/abc9_ops.cc | 825 | ||||
| -rw-r--r-- | passes/techmap/iopadmap.cc | 88 | ||||
| -rw-r--r-- | passes/techmap/tribuf.cc | 6 | 
7 files changed, 1841 insertions, 1345 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/abc.cc b/passes/techmap/abc.cc index b29480e26..581652a41 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -29,17 +29,17 @@  // Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025  // http://en.wikipedia.org/wiki/Topological_sorting -#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put" -#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; if; mfs2" -#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; cover {I} {P}" -#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put" - -#define ABC_FAST_COMMAND_LIB "strash; dretime; retime {D}; map {D}" -#define ABC_FAST_COMMAND_CTR "strash; dretime; retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_FAST_COMMAND_LUT "strash; dretime; retime {D}; if" -#define ABC_FAST_COMMAND_SOP "strash; dretime; retime {D}; cover -I {I} -P {P}" -#define ABC_FAST_COMMAND_DFL "strash; dretime; retime {D}; map" +#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put" +#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2" +#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put" + +#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}" +#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "strash; dretime; if" +#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "strash; dretime; map"  #include "kernel/register.h"  #include "kernel/sigtools.h" @@ -747,6 +747,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  	else  		abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; +	if (script_file.empty() && !delay_target.empty()) +		for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1)) +			abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8); +  	for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))  		abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); @@ -1510,7 +1514,47 @@ struct AbcPass : public Pass {  #endif  #endif -		size_t argidx; +		// get arguments from scratchpad first, then override by command arguments +		std::string lut_arg, luts_arg, g_arg; +		exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */); +		script_file = design->scratchpad_get_string("abc.script", script_file); +		liberty_file = design->scratchpad_get_string("abc.liberty", liberty_file); +		constr_file = design->scratchpad_get_string("abc.constr", constr_file); +		if (design->scratchpad.count("abc.D")) { +			delay_target = "-D " + design->scratchpad_get_string("abc.D"); +		} +		if (design->scratchpad.count("abc.I")) { +			sop_inputs = "-I " + design->scratchpad_get_string("abc.I"); +		} +		if (design->scratchpad.count("abc.P")) { +			sop_products = "-P " + design->scratchpad_get_string("abc.P"); +		} +		if (design->scratchpad.count("abc.S")) { +			lutin_shared = "-S " + design->scratchpad_get_string("abc.S"); +		} +		lut_arg = design->scratchpad_get_string("abc.lut", lut_arg); +		luts_arg = design->scratchpad_get_string("abc.luts", luts_arg); +		sop_mode = design->scratchpad_get_bool("abc.sop", sop_mode); +		map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4); +		map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8); +		map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16); +		abc_dress = design->scratchpad_get_bool("abc.dress", abc_dress); +		g_arg = design->scratchpad_get_string("abc.g", g_arg); + +		fast_mode = design->scratchpad_get_bool("abc.fast", fast_mode); +		dff_mode = design->scratchpad_get_bool("abc.dff", dff_mode); +		if (design->scratchpad.count("abc.clk")) { +			clk_str = design->scratchpad_get_string("abc.clk"); +			dff_mode = true; +		} +		keepff = design->scratchpad_get_bool("abc.keepff", keepff); +		cleanup = !design->scratchpad_get_bool("abc.nocleanup", !cleanup); +		keepff = design->scratchpad_get_bool("abc.keepff", keepff); +		show_tempdir = design->scratchpad_get_bool("abc.showtmp", show_tempdir); +		markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups); + +		size_t argidx, g_argidx; +		bool g_arg_from_cmd = false;  		char pwd [PATH_MAX];  		if (!getcwd(pwd, sizeof(pwd))) {  			log_cmd_error("getcwd failed: %s\n", strerror(errno)); @@ -1524,23 +1568,14 @@ struct AbcPass : public Pass {  			}  			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 == "-liberty" && argidx+1 < args.size()) {  				liberty_file = args[++argidx]; -				rewrite_filename(liberty_file); -				if (!liberty_file.empty() && !is_absolute_path(liberty_file)) -					liberty_file = std::string(pwd) + "/" + liberty_file;  				continue;  			}  			if (arg == "-constr" && argidx+1 < args.size()) { -				rewrite_filename(constr_file);  				constr_file = args[++argidx]; -				if (!constr_file.empty() && !is_absolute_path(constr_file)) -					constr_file = std::string(pwd) + "/" + constr_file;  				continue;  			}  			if (arg == "-D" && argidx+1 < args.size()) { @@ -1560,37 +1595,11 @@ struct AbcPass : public Pass {  				continue;  			}  			if (arg == "-lut" && argidx+1 < args.size()) { -				string arg = args[++argidx]; -				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)); +				lut_arg = args[++argidx];  				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) < std::atoi(parts.at(0).c_str())) -							lut_costs.push_back(atoi(parts.at(1).c_str())); -					else -						log_cmd_error("Invalid -luts syntax.\n"); -				} +				luts_arg = args[++argidx];  				continue;  			}  			if (arg == "-sop") { @@ -1614,123 +1623,11 @@ struct AbcPass : public Pass {  				continue;  			}  			if (arg == "-g" && argidx+1 < args.size()) { -				for (auto g : split_tokens(args[++argidx], ",")) { -					vector<string> gate_list; -					bool remove_gates = false; -					if (GetSize(g) > 0 && g[0] == '-') { -						remove_gates = true; -						g = g.substr(1); -					} -					if (g == "AND") goto ok_gate; -					if (g == "NAND") goto ok_gate; -					if (g == "OR") goto ok_gate; -					if (g == "NOR") goto ok_gate; -					if (g == "XOR") goto ok_gate; -					if (g == "XNOR") goto ok_gate; -					if (g == "ANDNOT") goto ok_gate; -					if (g == "ORNOT") goto ok_gate; -					if (g == "MUX") goto ok_gate; -					if (g == "NMUX") goto ok_gate; -					if (g == "AOI3") goto ok_gate; -					if (g == "OAI3") goto ok_gate; -					if (g == "AOI4") goto ok_gate; -					if (g == "OAI4") goto ok_gate; -					if (g == "simple") { -						gate_list.push_back("AND"); -						gate_list.push_back("OR"); -						gate_list.push_back("XOR"); -						gate_list.push_back("MUX"); -						goto ok_alias; -					} -					if (g == "cmos2") { -						if (!remove_gates) -							cmos_cost = true; -						gate_list.push_back("NAND"); -						gate_list.push_back("NOR"); -						goto ok_alias; -					} -					if (g == "cmos3") { -						if (!remove_gates) -							cmos_cost = true; -						gate_list.push_back("NAND"); -						gate_list.push_back("NOR"); -						gate_list.push_back("AOI3"); -						gate_list.push_back("OAI3"); -						goto ok_alias; -					} -					if (g == "cmos4") { -						if (!remove_gates) -							cmos_cost = true; -						gate_list.push_back("NAND"); -						gate_list.push_back("NOR"); -						gate_list.push_back("AOI3"); -						gate_list.push_back("OAI3"); -						gate_list.push_back("AOI4"); -						gate_list.push_back("OAI4"); -						goto ok_alias; -					} -					if (g == "cmos") { -						if (!remove_gates) -							cmos_cost = true; -						gate_list.push_back("NAND"); -						gate_list.push_back("NOR"); -						gate_list.push_back("AOI3"); -						gate_list.push_back("OAI3"); -						gate_list.push_back("AOI4"); -						gate_list.push_back("OAI4"); -						gate_list.push_back("NMUX"); -						gate_list.push_back("MUX"); -						gate_list.push_back("XOR"); -						gate_list.push_back("XNOR"); -						goto ok_alias; -					} -					if (g == "gates") { -						gate_list.push_back("AND"); -						gate_list.push_back("NAND"); -						gate_list.push_back("OR"); -						gate_list.push_back("NOR"); -						gate_list.push_back("XOR"); -						gate_list.push_back("XNOR"); -						gate_list.push_back("ANDNOT"); -						gate_list.push_back("ORNOT"); -						goto ok_alias; -					} -					if (g == "aig") { -						gate_list.push_back("AND"); -						gate_list.push_back("NAND"); -						gate_list.push_back("OR"); -						gate_list.push_back("NOR"); -						gate_list.push_back("ANDNOT"); -						gate_list.push_back("ORNOT"); -						goto ok_alias; -					} -					if (g == "all") { -						gate_list.push_back("AND"); -						gate_list.push_back("NAND"); -						gate_list.push_back("OR"); -						gate_list.push_back("NOR"); -						gate_list.push_back("XOR"); -						gate_list.push_back("XNOR"); -						gate_list.push_back("ANDNOT"); -						gate_list.push_back("ORNOT"); -						gate_list.push_back("AOI3"); -						gate_list.push_back("OAI3"); -						gate_list.push_back("AOI4"); -						gate_list.push_back("OAI4"); -						gate_list.push_back("MUX"); -						gate_list.push_back("NMUX"); -					} -					cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); -				ok_gate: -					gate_list.push_back(g); -				ok_alias: -					for (auto gate : gate_list) { -						if (remove_gates) -							enabled_gates.erase(gate); -						else -							enabled_gates.insert(gate); -					} -				} +				if (g_arg_from_cmd) +					log_cmd_error("Can only use -g once. Please combine."); +				g_arg = args[++argidx]; +				g_argidx = argidx; +				g_arg_from_cmd = true;  				continue;  			}  			if (arg == "-fast") { @@ -1766,8 +1663,176 @@ struct AbcPass : public Pass {  		}  		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; +		rewrite_filename(liberty_file); +		if (!liberty_file.empty() && !is_absolute_path(liberty_file)) +			liberty_file = std::string(pwd) + "/" + liberty_file; +		rewrite_filename(constr_file); +		if (!constr_file.empty() && !is_absolute_path(constr_file)) +			constr_file = std::string(pwd) + "/" + constr_file; + +		// handle -lut argument +		if (!lut_arg.empty()) { +			size_t pos = lut_arg.find_first_of(':'); +			int lut_mode = 0, lut_mode2 = 0; +			if (pos != string::npos) { +				lut_mode = atoi(lut_arg.substr(0, pos).c_str()); +				lut_mode2 = atoi(lut_arg.substr(pos+1).c_str()); +			} else { +				lut_mode = atoi(lut_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)); +		} +		//handle -luts argument +		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) < std::atoi(parts.at(0).c_str())) +						lut_costs.push_back(atoi(parts.at(1).c_str())); +				else +					log_cmd_error("Invalid -luts syntax.\n"); +			} +		} + +		// handle -g argument +		if (!g_arg.empty()){ +			for (auto g : split_tokens(g_arg, ",")) { +				vector<string> gate_list; +				bool remove_gates = false; +				if (GetSize(g) > 0 && g[0] == '-') { +					remove_gates = true; +					g = g.substr(1); +				} +				if (g == "AND") goto ok_gate; +				if (g == "NAND") goto ok_gate; +				if (g == "OR") goto ok_gate; +				if (g == "NOR") goto ok_gate; +				if (g == "XOR") goto ok_gate; +				if (g == "XNOR") goto ok_gate; +				if (g == "ANDNOT") goto ok_gate; +				if (g == "ORNOT") goto ok_gate; +				if (g == "MUX") goto ok_gate; +				if (g == "NMUX") goto ok_gate; +				if (g == "AOI3") goto ok_gate; +				if (g == "OAI3") goto ok_gate; +				if (g == "AOI4") goto ok_gate; +				if (g == "OAI4") goto ok_gate; +				if (g == "simple") { +					gate_list.push_back("AND"); +					gate_list.push_back("OR"); +					gate_list.push_back("XOR"); +					gate_list.push_back("MUX"); +					goto ok_alias; +				} +				if (g == "cmos2") { +					if (!remove_gates) +						cmos_cost = true; +					gate_list.push_back("NAND"); +					gate_list.push_back("NOR"); +					goto ok_alias; +				} +				if (g == "cmos3") { +					if (!remove_gates) +						cmos_cost = true; +					gate_list.push_back("NAND"); +					gate_list.push_back("NOR"); +					gate_list.push_back("AOI3"); +					gate_list.push_back("OAI3"); +					goto ok_alias; +				} +				if (g == "cmos4") { +					if (!remove_gates) +						cmos_cost = true; +					gate_list.push_back("NAND"); +					gate_list.push_back("NOR"); +					gate_list.push_back("AOI3"); +					gate_list.push_back("OAI3"); +					gate_list.push_back("AOI4"); +					gate_list.push_back("OAI4"); +					goto ok_alias; +				} +				if (g == "cmos") { +					if (!remove_gates) +						cmos_cost = true; +					gate_list.push_back("NAND"); +					gate_list.push_back("NOR"); +					gate_list.push_back("AOI3"); +					gate_list.push_back("OAI3"); +					gate_list.push_back("AOI4"); +					gate_list.push_back("OAI4"); +					gate_list.push_back("NMUX"); +					gate_list.push_back("MUX"); +					gate_list.push_back("XOR"); +					gate_list.push_back("XNOR"); +					goto ok_alias; +				} +				if (g == "gates") { +					gate_list.push_back("AND"); +					gate_list.push_back("NAND"); +					gate_list.push_back("OR"); +					gate_list.push_back("NOR"); +					gate_list.push_back("XOR"); +					gate_list.push_back("XNOR"); +					gate_list.push_back("ANDNOT"); +					gate_list.push_back("ORNOT"); +					goto ok_alias; +				} +				if (g == "aig") { +					gate_list.push_back("AND"); +					gate_list.push_back("NAND"); +					gate_list.push_back("OR"); +					gate_list.push_back("NOR"); +					gate_list.push_back("ANDNOT"); +					gate_list.push_back("ORNOT"); +					goto ok_alias; +				} +				if (g == "all") { +					gate_list.push_back("AND"); +					gate_list.push_back("NAND"); +					gate_list.push_back("OR"); +					gate_list.push_back("NOR"); +					gate_list.push_back("XOR"); +					gate_list.push_back("XNOR"); +					gate_list.push_back("ANDNOT"); +					gate_list.push_back("ORNOT"); +					gate_list.push_back("AOI3"); +					gate_list.push_back("OAI3"); +					gate_list.push_back("AOI4"); +					gate_list.push_back("OAI4"); +					gate_list.push_back("MUX"); +					gate_list.push_back("NMUX"); +				} +				if (g_arg_from_cmd) +					cmd_error(args, g_argidx, stringf("Unsupported gate type: %s", g.c_str())); +				else +					log_cmd_error("Unsupported gate type: %s", g.c_str()); +			ok_gate: +				gate_list.push_back(g); +			ok_alias: +				for (auto gate : gate_list) { +					if (remove_gates) +						enabled_gates.erase(gate); +					else +						enabled_gates.insert(gate); +				} +			} +		} +  		if (!lut_costs.empty() && !liberty_file.empty()) -			log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n"); +			log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n");  		if (!constr_file.empty() && liberty_file.empty())  			log_cmd_error("Got -constr but no -liberty!\n"); diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 8276c3c16..2aeda16d6 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -2,7 +2,7 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *                2019  Eddie Hung <eddie@fpgeh.com> + *            (C) 2019  Eddie Hung    <eddie@fpgeh.com>   *   *  Permission to use, copy, modify, and/or distribute this software for any   *  purpose with or without fee is hereby granted, provided that the above @@ -22,806 +22,79 @@  // 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 +// abc9_exe.cc +std::string fold_abc9_cmd(std::string str);  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; -} - -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); -			} -		} - -		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 +	Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { } +	void on_register() YS_OVERRIDE  	{ -		log("Don't call ABC as there is nothing to map.\n"); +		RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs"; +		RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -a -v; &mfs"; +		RTLIL::constpad["abc9.script.default.fast"] = "+&if {C} {W} {D} {R} -v"; +		// Based on ABC's &flow +		RTLIL::constpad["abc9.script.flow"] = "+&scorr; &sweep;" \ +			"&dch -C 500;" \ +			/* Round 1 */ \ +			/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			"&st; &dsdb;" \ +			/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			"&st; &syn2 -m -R 10; &dsdb;" \ +			"&blut -a -K 6;" \ +			/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			/* Round 2 */ \ +			"&st; &sopb;" \ +			/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			"&st; &dsdb;" \ +			/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			"&st; &syn2 -m -R 10; &dsdb;" \ +			"&blut -a -K 6;" \ +			/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			/* Round 3 */ \ +			/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			"&st; &dsdb;" \ +			/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \ +			"&st; &syn2 -m -R 10; &dsdb;" \ +			"&blut -a -K 6;" \ +			/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;"; +		// Based on ABC's &flow2 +		RTLIL::constpad["abc9.script.flow2"] = "+&scorr; &sweep;" \ +			/* Comm1 */ "&synch2 -K 6 -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\ +			/* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\ +			"&load; &st; &sopb -R 10 -C 4; " \ +			/* Comm3 */ "&synch2 -K 6 -C 500; &if -m "/*"-E 5"*/" {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\ +			/* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save; "\ +			"&load"; +		// Based on ABC's &flow3 +		RTLIL::constpad["abc9.script.flow3"] = "+&scorr; &sweep;" \ +			"&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\ +			"&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &save; &load;"\ +			"&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &save; &load;"\ +			"&mfs";  	} - -	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---|  		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.\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 @@ -840,26 +113,17 @@ 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 (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("%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(ABC_FAST_COMMAND_LUT).c_str()); +		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("        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 +145,10 @@ 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("    -dff\n"); +		log("        also pass $_ABC9_FF_ cells through to ABC. modules with many clock\n"); +		log("        domains are marked as such and automatically partitioned by ABC.\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"); @@ -902,409 +157,175 @@ struct Abc9Pass : public Pass {  		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("        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");  		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\n"); +		log("externally 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 exe_cmd; +	bool dff_mode, 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; +	void clear_flags() YS_OVERRIDE +	{ +		exe_cmd.str(""); +		exe_cmd << "abc9_exe"; +		dff_mode = false; +		cleanup = true; +	} -#if 0 -		cleanup = false; -		show_tempdir = true; -#endif +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		std::string run_from, run_to; +		clear_flags(); -#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 +		// get arguments from scratchpad first, then override by command arguments +		dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode); +		cleanup = !design->scratchpad_get_bool("abc9.nocleanup", !cleanup); -		size_t argidx; -		char pwd [PATH_MAX]; -		if (!getcwd(pwd, sizeof(pwd))) { -			log_cmd_error("getcwd failed: %s\n", strerror(errno)); -			log_abort(); +		if (design->scratchpad_get_bool("abc9.debug")) { +			cleanup = false; +			exe_cmd << " -showtmp";  		} + +		size_t argidx;  		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; +			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 == "-D" && argidx+1 < args.size()) { -				delay_target = "-D " + args[++argidx]; +			if (arg == "-fast" || /* arg == "-dff" || */ +					/* arg == "-nocleanup" || */ arg == "-showtmp") { +				exe_cmd << " " << arg;  				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]; -				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 { -					pos = arg.find_first_of('.'); -					if (pos != string::npos) { -						lut_file = arg; -						rewrite_filename(lut_file); -						if (!lut_file.empty() && !is_absolute_path(lut_file)) -							lut_file = std::string(pwd) + "/" + lut_file; -					} -					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)); +			if (arg == "-dff") { +				dff_mode = true; +				exe_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; +			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); -		// 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 = std::string(pwd) + "/" + box_file; - -		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(); -			} +		log_assert(design); +		if (design->selected_modules().empty()) { +			log_warning("No modules selected for ABC9 techmapping.\n"); +			return;  		} -		for (auto mod : design->selected_modules()) -		{ -			if (mod->attributes.count(ID(abc9_box_id))) -				continue; - -			if (mod->processes.size() > 0) { -				log("Skipping module %s as it contains processes.\n", log_id(mod)); -				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); - -			std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); -			std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); +		log_header(design, "Executing ABC9 pass.\n"); +		log_push(); -			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; +		run_script(design, run_from, run_to); -			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; +		log_pop(); +	} -			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; +	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"); +		} -			for (auto cell : all_cells) -			{ -				clkdomain_t key; +		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"); +			} +			else { +				auto selected_modules = active_design->selected_modules(); +				active_design->selection_stack.emplace_back(false); -				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); -						} +				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))); -				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; +					log_push(); +					active_design->selection().select(mod); -				unassigned_cells.erase(cell); -				expand_queue.insert(cell); -				expand_queue_up.insert(cell); -				expand_queue_down.insert(cell); +					if (!active_design->selected_whole_module(mod)) +						log_error("Can't handle partially selected module %s!\n", log_id(mod)); -				assigned_cells[key].push_back(cell); -				assigned_cells_reverse[cell] = key; -			} - -			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); +					std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; +					if (!cleanup) +						tempdir_name[0] = tempdir_name[4] = '_'; +					tempdir_name = make_temp_dir(tempdir_name); -					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); -						} -				} +					run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); -				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); -						} -				} +					int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs"); -				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); -				} -			} +					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"); -			while (!expand_queue.empty()) -			{ -				RTLIL::Cell *cell = *expand_queue.begin(); -				clkdomain_t key = assigned_cells_reverse.at(cell); -				expand_queue.erase(cell); +					if (cleanup) { +						log("Removing temp directory.\n"); +						remove_directory(tempdir_name); +					} -				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(); +					active_design->selection().selected_modules.clear(); +					log_pop();  				} -				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); +				active_design->selection_stack.pop_back();  			}  		} - -		assign_map.clear(); - -		log_pop();  	}  } Abc9Pass; diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc new file mode 100644 index 000000000..01bf46539 --- /dev/null +++ b/passes/techmap/abc9_exe.cc @@ -0,0 +1,531 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *                2019  Eddie Hung <eddie@fpgeh.com> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// [[CITE]] ABC +// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification +// http://www.eecs.berkeley.edu/~alanmi/abc/ + +#include "kernel/register.h" +#include "kernel/log.h" + +#ifndef _WIN32 +#  include <unistd.h> +#  include <dirent.h> +#endif + +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + +std::string fold_abc9_cmd(std::string str) +{ +	std::string token, new_str = "          "; +	int char_counter = 10; + +	for (size_t i = 0; i <= str.size(); i++) { +		if (i < str.size()) +			token += str[i]; +		if (i == str.size() || str[i] == ';') { +			if (char_counter + token.size() > 75) +				new_str += "\n              ", char_counter = 14; +			new_str += token, char_counter += token.size(); +			token.clear(); +		} +	} + +	return new_str; +} + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +std::string add_echos_to_abc9_cmd(std::string str) +{ +	std::string new_str, token; +	for (size_t i = 0; i < str.size(); i++) { +		token += str[i]; +		if (str[i] == ';') { +			while (i+1 < str.size() && str[i+1] == ' ') +				i++; +			new_str += "echo + " + token + " " + token + " "; +			token.clear(); +		} +	} + +	if (!token.empty()) { +		if (!new_str.empty()) +			new_str += "echo + " + token + "; "; +		new_str += token; +	} + +	return new_str; +} + +std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +{ +	if (show_tempdir) +		return text; + +	while (1) { +		size_t pos = text.find(tempdir_name); +		if (pos == std::string::npos) +			break; +		text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); +	} + +	std::string  selfdir_name = proc_self_dirname(); +	if (selfdir_name != "/") { +		while (1) { +			size_t pos = text.find(selfdir_name); +			if (pos == std::string::npos) +				break; +			text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); +		} +	} + +	return text; +} + +struct abc9_output_filter +{ +	bool got_cr; +	int escape_seq_state; +	std::string linebuf; +	std::string tempdir_name; +	bool show_tempdir; + +	abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) +	{ +		got_cr = false; +		escape_seq_state = 0; +	} + +	void next_char(char ch) +	{ +		if (escape_seq_state == 0 && ch == '\033') { +			escape_seq_state = 1; +			return; +		} +		if (escape_seq_state == 1) { +			escape_seq_state = ch == '[' ? 2 : 0; +			return; +		} +		if (escape_seq_state == 2) { +			if ((ch < '0' || '9' < ch) && ch != ';') +				escape_seq_state = 0; +			return; +		} +		escape_seq_state = 0; +		if (ch == '\r') { +			got_cr = true; +			return; +		} +		if (ch == '\n') { +			log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); +			got_cr = false, linebuf.clear(); +			return; +		} +		if (got_cr) +			got_cr = false, linebuf.clear(); +		linebuf += ch; +	} + +	void next_line(const std::string &line) +	{ +		//int pi, po; +		//if (sscanf(line.c_str(), "Start-point = pi%d.  End-point = po%d.", &pi, &po) == 2) { +		//	log("ABC: Start-point = pi%d (%s).  End-point = po%d (%s).\n", +		//			pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", +		//			po, po_map.count(po) ? po_map.at(po).c_str() : "???"); +		//	return; +		//} + +		for (char ch : line) +			next_char(ch); +	} +}; + +void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file, +		vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, +		bool show_tempdir, std::string box_file, std::string lut_file, +		std::string wire_delay, std::string tempdir_name +) +{ +	std::string abc9_script; + +	if (!lut_costs.empty()) +		abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); +	else if (!lut_file.empty()) +		abc9_script += stringf("read_lut %s; ", lut_file.c_str()); +	else +		log_abort(); + +	log_assert(!box_file.empty()); +	abc9_script += stringf("read_box %s; ", box_file.c_str()); +	abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); + +	if (!script_file.empty()) { +		if (script_file[0] == '+') { +			for (size_t i = 1; i < script_file.size(); i++) +				if (script_file[i] == '\'') +					abc9_script += "'\\''"; +				else if (script_file[i] == ',') +					abc9_script += " "; +				else +					abc9_script += script_file[i]; +		} else +			abc9_script += stringf("source %s", script_file.c_str()); +	} else if (!lut_costs.empty() || !lut_file.empty()) { +		abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos) +			: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos); +	} else +		log_abort(); + +	for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) +		abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); + +	//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos)) +	//	abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3); + +	for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) +		abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); + +	std::string C; +	if (design->scratchpad.count("abc9.if.C")) +		C = "-C " + design->scratchpad_get_string("abc9.if.C"); +	for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos)) +		abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3); + +	std::string R; +	if (design->scratchpad.count("abc9.if.R")) +		R = "-R " + design->scratchpad_get_string("abc9.if.R"); +	for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos)) +		abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3); + +	abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str()); +	if (design->scratchpad_get_bool("abc9.verify")) { +		if (dff_mode) +			abc9_script += "; verify -s"; +		else +			abc9_script += "; verify"; +	} +	abc9_script += "; time"; +	abc9_script = add_echos_to_abc9_cmd(abc9_script); + +	for (size_t i = 0; i+1 < abc9_script.size(); i++) +		if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') +			abc9_script[i+1] = '\n'; + +	FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); +	fprintf(f, "%s\n", abc9_script.c_str()); +	fclose(f); + +	std::string buffer; + +	log_header(design, "Executing ABC9.\n"); + +	if (!lut_costs.empty()) { +		buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); +		f = fopen(buffer.c_str(), "wt"); +		if (f == NULL) +			log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); +		for (int i = 0; i < GetSize(lut_costs); i++) +			fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); +		fclose(f); +	} + +	buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); +	log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + +#ifndef YOSYS_LINK_ABC +	abc9_output_filter filt(tempdir_name, show_tempdir); +	int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); +#else +	// These needs to be mutable, supposedly due to getopt +	char *abc9_argv[5]; +	string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); +	abc9_argv[0] = strdup(exe_file.c_str()); +	abc9_argv[1] = strdup("-s"); +	abc9_argv[2] = strdup("-f"); +	abc9_argv[3] = strdup(tmp_script_name.c_str()); +	abc9_argv[4] = 0; +	int ret = Abc_RealMain(4, abc9_argv); +	free(abc9_argv[0]); +	free(abc9_argv[1]); +	free(abc9_argv[2]); +	free(abc9_argv[3]); +#endif +	if (ret != 0) +		log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); +} + +struct Abc9ExePass : public Pass { +	Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    abc9_exe [options]\n"); +		log("\n"); +		log(" \n"); +		log("This pass uses the ABC tool [1] for technology mapping of the top module\n"); +		log("(according to the (* top *) attribute or if only one module is currently selected)\n"); +		log("to a target FPGA architecture.\n"); +		log("\n"); +		log("    -exe <command>\n"); +#ifdef ABCEXTERNAL +		log("        use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else +		log("        use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif +		log("        This can e.g. be used to call a specific version of ABC or a wrapper.\n"); +		log("\n"); +		log("    -script <file>\n"); +		log("        use the specified ABC script file instead of the default script.\n"); +		log("\n"); +		log("        if <file> starts with a plus sign (+), then the rest of the filename\n"); +		log("        string is interpreted as the command string to be passed to ABC. The\n"); +		log("        leading plus sign is removed and all commas (,) in the string are\n"); +		log("        replaced with blanks before the string is passed to ABC.\n"); +		log("\n"); +		log("        if no -script parameter is given, the following scripts are used:\n"); +		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str()); +		log("\n"); +		log("    -fast\n"); +		log("        use different default scripts that are slightly faster (at the cost\n"); +		log("        of output quality):\n"); +		log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str()); +		log("\n"); +		log("    -D <picoseconds>\n"); +		log("        set delay target. the string {D} in the default scripts above is\n"); +		log("        replaced by this option when used, and an empty string otherwise\n"); +		log("        (indicating best possible delay).\n"); +		log("\n"); +//		log("    -S <num>\n"); +//		log("        maximum number of LUT inputs shared.\n"); +//		log("        (replaces {S} in the default scripts above, default: -S 1)\n"); +//		log("\n"); +		log("    -lut <width>\n"); +		log("        generate netlist using luts of (max) the specified width.\n"); +		log("\n"); +		log("    -lut <w1>:<w2>\n"); +		log("        generate netlist using luts of (max) the specified width <w2>. All\n"); +		log("        luts with width <= <w1> have constant cost. for luts larger than <w1>\n"); +		log("        the area cost doubles with each additional input bit. the delay cost\n"); +		log("        is still constant for all lut widths.\n"); +		log("\n"); +		log("    -lut <file>\n"); +		log("        pass this file with lut library to ABC.\n"); +		log("\n"); +		log("    -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n"); +		log("        generate netlist using luts. Use the specified costs for luts with 1,\n"); +		log("        2, 3, .. inputs.\n"); +		log("\n"); +		log("    -showtmp\n"); +		log("        print the temp dir name in log. usually this is suppressed so that the\n"); +		log("        command output is identical across runs.\n"); +		log("\n"); +		log("    -box <file>\n"); +		log("        pass this file with box library to ABC.\n"); +		log("\n"); +		log("    -cwd <dir>\n"); +		log("        use this as the current working directory, inside which the 'input.xaig'\n"); +		log("        file is expected. temporary files will be created in this directory, and\n"); +		log("        the mapped result will be written to 'output.aig'.\n"); +		log("\n"); +		log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); +		log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); +		log("ABC on logic snippets extracted from your design. You will not get any useful\n"); +		log("output when passing an ABC script that writes a file. Instead write your full\n"); +		log("design as BLIF file with write_blif and then load that into ABC externally if\n"); +		log("you want to use ABC to convert your design into another format.\n"); +		log("\n"); +		log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n"); + +#ifdef ABCEXTERNAL +		std::string exe_file = ABCEXTERNAL; +#else +		std::string exe_file = proc_self_dirname() + "yosys-abc"; +#endif +		std::string script_file, clk_str, box_file, lut_file; +		std::string delay_target, lutin_shared = "-S 1", wire_delay; +		std::string tempdir_name; +		bool fast_mode = false, dff_mode = false; +		bool show_tempdir = false; +		vector<int> lut_costs; + +#if 0 +		cleanup = false; +		show_tempdir = true; +#endif + +#ifdef _WIN32 +#ifndef ABCEXTERNAL +		if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) +			exe_file = proc_self_dirname() + "..\\yosys-abc"; +#endif +#endif + +		std::string lut_arg, luts_arg; +		exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */); +		script_file = design->scratchpad_get_string("abc9.script", script_file); +		if (design->scratchpad.count("abc9.D")) { +			delay_target = "-D " + design->scratchpad_get_string("abc9.D"); +		} +		lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); +		luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); +		fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode); +		dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode); +		show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); +		box_file = design->scratchpad_get_string("abc9.box", box_file); +		if (design->scratchpad.count("abc9.W")) { +			wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); +		} + +		size_t argidx; +		char pwd [PATH_MAX]; +		if (!getcwd(pwd, sizeof(pwd))) { +			log_cmd_error("getcwd failed: %s\n", strerror(errno)); +			log_abort(); +		} +		for (argidx = 1; argidx < args.size(); argidx++) { +			std::string arg = args[argidx]; +			if (arg == "-exe" && argidx+1 < args.size()) { +				exe_file = args[++argidx]; +				continue; +			} +			if (arg == "-script" && argidx+1 < args.size()) { +				script_file = args[++argidx]; +				continue; +			} +			if (arg == "-D" && argidx+1 < args.size()) { +				delay_target = "-D " + args[++argidx]; +				continue; +			} +			//if (arg == "-S" && argidx+1 < args.size()) { +			//	lutin_shared = "-S " + args[++argidx]; +			//	continue; +			//} +			if (arg == "-lut" && argidx+1 < args.size()) { +				lut_arg = args[++argidx]; +				continue; +			} +			if (arg == "-luts" && argidx+1 < args.size()) { +				lut_arg = args[++argidx]; +				continue; +			} +			if (arg == "-fast") { +				fast_mode = true; +				continue; +			} +			if (arg == "-dff") { +				dff_mode = true; +				continue; +			} +			if (arg == "-showtmp") { +				show_tempdir = true; +				continue; +			} +			if (arg == "-box" && argidx+1 < args.size()) { +				box_file = args[++argidx]; +				continue; +			} +			if (arg == "-W" && argidx+1 < args.size()) { +				wire_delay = "-W " + args[++argidx]; +				continue; +			} +			if (arg == "-cwd" && argidx+1 < args.size()) { +				tempdir_name = args[++argidx]; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		rewrite_filename(script_file); +		if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') +			script_file = std::string(pwd) + "/" + script_file; + +		// handle -lut / -luts args +		if (!lut_arg.empty()) { +			string arg = lut_arg; +			if (arg.find_first_not_of("0123456789:") == std::string::npos) { +				size_t pos = arg.find_first_of(':'); +				int lut_mode = 0, lut_mode2 = 0; +				if (pos != string::npos) { +					lut_mode = atoi(arg.substr(0, pos).c_str()); +					lut_mode2 = atoi(arg.substr(pos+1).c_str()); +				} else { +					lut_mode = atoi(arg.c_str()); +					lut_mode2 = lut_mode; +				} +				lut_costs.clear(); +				for (int i = 0; i < lut_mode; i++) +					lut_costs.push_back(1); +				for (int i = lut_mode; i < lut_mode2; i++) +					lut_costs.push_back(2 << (i - lut_mode)); +			} +			else { +				lut_file = arg; +				rewrite_filename(lut_file); +				if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+') +					lut_file = std::string(pwd) + "/" + lut_file; +			} +		} +		if (!luts_arg.empty()) { +			lut_costs.clear(); +			for (auto &tok : split_tokens(luts_arg, ",")) { +				auto parts = split_tokens(tok, ":"); +				if (GetSize(parts) == 0 && !lut_costs.empty()) +					lut_costs.push_back(lut_costs.back()); +				else if (GetSize(parts) == 1) +					lut_costs.push_back(atoi(parts.at(0).c_str())); +				else if (GetSize(parts) == 2) +					while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) +						lut_costs.push_back(atoi(parts.at(1).c_str())); +				else +					log_cmd_error("Invalid -luts syntax.\n"); +			} +		} + +		// ABC expects a box file for XAIG +		if (box_file.empty()) +			box_file = "+/dummy.box"; + +		rewrite_filename(box_file); +		if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') +			box_file = std::string(pwd) + "/" + box_file; + +		if (tempdir_name.empty()) +			log_cmd_error("abc9_exe '-cwd' option is mandatory.\n"); + + +		abc9_module(design, script_file, exe_file, lut_costs, dff_mode, +				delay_target, lutin_shared, fast_mode, show_tempdir, +				box_file, lut_file, wire_delay, tempdir_name); +	} +} Abc9ExePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc new file mode 100644 index 000000000..9ad29a8f6 --- /dev/null +++ b/passes/techmap/abc9_ops.cc @@ -0,0 +1,825 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *                2019  Eddie Hung <eddie@fpgeh.com> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" +#include "kernel/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +int map_autoidx; + +inline std::string remap_name(RTLIL::IdString abc9_name) +{ +	return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); +} + +void mark_scc(RTLIL::Module *module) +{ +	// For every unique SCC found, (arbitrarily) find the first +	//   cell in the component, and convert all wires driven by +	//   its output ports into a new PO, and drive its previous +	//   sinks with a new PI +	pool<RTLIL::Const> ids_seen; +	for (auto cell : module->cells()) { +		auto it = cell->attributes.find(ID(abc9_scc_id)); +		if (it == cell->attributes.end()) +			continue; +		auto id = it->second; +		auto r = ids_seen.insert(id); +		cell->attributes.erase(it); +		if (!r.second) +			continue; +		for (auto &c : cell->connections_) { +			if (c.second.is_fully_const()) continue; +			if (cell->output(c.first)) { +				SigBit b = c.second.as_bit(); +				Wire *w = b.wire; +				w->set_bool_attribute(ID::keep); +				w->attributes[ID(abc9_scc_id)] = id.as_int(); +			} +		} +	} + +	module->fixup_ports(); +} + +void prep_dff(RTLIL::Module *module) +{ +	auto design = module->design; +	log_assert(design); + +	SigMap assign_map(module); + +	typedef SigSpec clkdomain_t; +	dict<clkdomain_t, int> clk_to_mergeability; + +	for (auto cell : module->cells()) { +		if (cell->type != "$__ABC9_FF_") +			continue; + +		Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str())); +		if (abc9_clock_wire == NULL) +			log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		SigSpec abc9_clock = assign_map(abc9_clock_wire); + +		clkdomain_t key(abc9_clock); + +		auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1)); +		auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second)); +		log_assert(r2.second); + +		Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str())); +		if (abc9_init_wire == NULL) +			log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		log_assert(GetSize(abc9_init_wire) == 1); +		SigSpec abc9_init = assign_map(abc9_init_wire); +		if (!abc9_init.is_fully_const()) +			log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); +		if (abc9_init == State::S1) +			log_error("'%s.init' in module '%s' has value 1'b1 which is not supported by 'abc9 -dff'.\n", cell->name.c_str(), log_id(module)); +		r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const())); +		log_assert(r2.second); +	} + +	RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str())); +	if (holes_module) { +		SigMap sigmap(holes_module); + +		dict<SigSpec, SigSpec> replace; +		for (auto cell : holes_module->cells().to_vector()) { +			if (!cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", +						"$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) +				continue; +			SigBit D = cell->getPort("\\D"); +			SigBit Q = cell->getPort("\\Q"); +			// Emulate async control embedded inside $_DFF_* cell with mux in front of D +			if (cell->type.in("$_DFF_NN0_", "$_DFF_PN0_")) +				D = holes_module->MuxGate(NEW_ID, State::S0, D, cell->getPort("\\R")); +			else if (cell->type.in("$_DFF_NN1_", "$_DFF_PN1_")) +				D = holes_module->MuxGate(NEW_ID, State::S1, D, cell->getPort("\\R")); +			else if (cell->type.in("$_DFF_NP0_", "$_DFF_PP0_")) +				D = holes_module->MuxGate(NEW_ID, D, State::S0, cell->getPort("\\R")); +			else if (cell->type.in("$_DFF_NP1_", "$_DFF_PP1_")) +				D = holes_module->MuxGate(NEW_ID, D, State::S1, cell->getPort("\\R")); +			// Remove the $_DFF_* cell from what needs to be a combinatorial box +			holes_module->remove(cell); +			Wire *port; +			if (GetSize(Q.wire) == 1) +				port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str())); +			else +				port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset)); +			log_assert(port); +			// Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;" +			//   in order to extract just the combinatorial control logic that feeds the box +			//   (i.e. clock enable, synchronous reset, etc.) +			replace.insert(std::make_pair(Q,D)); +			// Since `flatten` above would have created wires named "<cell>.Q", +			//   extract the pre-techmap cell name +			auto pos = Q.wire->name.str().rfind("."); +			log_assert(pos != std::string::npos); +			IdString driver = Q.wire->name.substr(0, pos); +			// And drive the signal that was previously driven by "DFF.Q" (typically +			//   used to implement clock-enable functionality) with the "<cell>.$abc9_currQ" +			//   wire (which itself is driven an by input port) we inserted above +			Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str())); +			log_assert(currQ); +			holes_module->connect(Q, currQ); +		} + +		for (auto &conn : holes_module->connections_) +			conn.second = replace.at(sigmap(conn.second), conn.second); +	} +} + +void prep_xaiger(RTLIL::Module *module, bool dff) +{ +	auto design = module->design; +	log_assert(design); + +	SigMap sigmap(module); + +	dict<SigBit, pool<IdString>> bit_drivers, bit_users; +	TopoSort<IdString, RTLIL::sort_by_id_str> toposort; +	dict<IdString, std::vector<IdString>> box_ports; + +	for (auto cell : module->cells()) { +		if (cell->type == "$__ABC9_FF_") +			continue; +		if (cell->has_keep_attr()) +			continue; + +		auto inst_module = module->design->module(cell->type); +		bool abc9_box = inst_module && inst_module->attributes.count("\\abc9_box_id"); +		bool abc9_flop = false; +		if (abc9_box) { +			abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); +			if (abc9_flop && !dff) +				continue; + +			auto r = box_ports.insert(cell->type); +			if (r.second) { +				// Make carry in the last PI, and carry out the last PO +				//   since ABC requires it this way +				IdString carry_in, carry_out; +				for (const auto &port_name : inst_module->ports) { +					auto w = inst_module->wire(port_name); +					log_assert(w); +					if (w->get_bool_attribute("\\abc9_carry")) { +						if (w->port_input) { +							if (carry_in != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(inst_module)); +							carry_in = port_name; +						} +						if (w->port_output) { +							if (carry_out != IdString()) +								log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(inst_module)); +							carry_out = port_name; +						} +					} +					else +						r.first->second.push_back(port_name); +				} + +				if (carry_in != IdString() && carry_out == IdString()) +					log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(inst_module)); +				if (carry_in == IdString() && carry_out != IdString()) +					log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(inst_module)); +				if (carry_in != IdString()) { +					r.first->second.push_back(carry_in); +					r.first->second.push_back(carry_out); +				} +			} +		} +		else if (!yosys_celltypes.cell_known(cell->type)) +			continue; + +		// TODO: Speed up toposort -- we care about box ordering only +		for (auto conn : cell->connections()) { +			if (cell->input(conn.first)) +				for (auto bit : sigmap(conn.second)) +					bit_users[bit].insert(cell->name); + +			if (cell->output(conn.first) && !abc9_flop) +				for (auto bit : sigmap(conn.second)) +					bit_drivers[bit].insert(cell->name); +		} +		toposort.node(cell->name); +	} + +	if (box_ports.empty()) +		return; + +	for (auto &it : bit_users) +		if (bit_drivers.count(it.first)) +			for (auto driver_cell : bit_drivers.at(it.first)) +			for (auto user_cell : it.second) +				toposort.edge(driver_cell, user_cell); + +	if (ys_debug(1)) +		toposort.analyze_loops = true; + +	bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); + +	if (ys_debug(1)) { +		unsigned i = 0; +		for (auto &it : toposort.loops) { +			log("  loop %d\n", i++); +			for (auto cell_name : it) { +				auto cell = module->cell(cell_name); +				log_assert(cell); +				log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); +			} +		} +	} + +	log_assert(no_loops); + +	RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str())); +	log_assert(holes_module); +	holes_module->set_bool_attribute("\\abc9_holes"); + +	dict<IdString, Cell*> cell_cache; + +	int port_id = 1, box_count = 0; +	for (auto cell_name : toposort.sorted) { +		RTLIL::Cell *cell = module->cell(cell_name); +		log_assert(cell); + +		RTLIL::Module* box_module = design->module(cell->type); +		if (!box_module || !box_module->attributes.count("\\abc9_box_id")) +			continue; + +		cell->attributes["\\abc9_box_seq"] = box_count++; + +		IdString derived_name = box_module->derive(design, cell->parameters); +		box_module = design->module(derived_name); + +		auto r = cell_cache.insert(derived_name); +		auto &holes_cell = r.first->second; +		if (r.second) { +			if (box_module->has_processes()) +				Pass::call_on_module(design, box_module, "proc"); + +			if (box_module->get_bool_attribute("\\whitebox")) { +				holes_cell = holes_module->addCell(cell->name, derived_name); + +				int box_inputs = 0; +				for (auto port_name : box_ports.at(cell->type)) { +					RTLIL::Wire *w = box_module->wire(port_name); +					log_assert(w); +					log_assert(!w->port_input || !w->port_output); +					auto &conn = holes_cell->connections_[port_name]; +					if (w->port_input) { +						for (int i = 0; i < GetSize(w); i++) { +							box_inputs++; +							RTLIL::Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); +							if (!holes_wire) { +								holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); +								holes_wire->port_input = true; +								holes_wire->port_id = port_id++; +								holes_module->ports.push_back(holes_wire->name); +							} +							conn.append(holes_wire); +						} +					} +					else if (w->port_output) +						conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w)); +				} + +				// For flops only, create an extra 1-bit input that drives a new wire +				//   called "<cell>.abc9_ff.Q" that is used below +				if (box_module->get_bool_attribute("\\abc9_flop")) { +					box_inputs++; +					Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); +					if (!holes_wire) { +						holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); +						holes_wire->port_input = true; +						holes_wire->port_id = port_id++; +						holes_module->ports.push_back(holes_wire->name); +					} +					Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str())); +					holes_module->connect(Q, holes_wire); +				} +			} +			else // box_module is a blackbox +				log_assert(holes_cell == nullptr); +		} + +		for (auto port_name : box_ports.at(cell->type)) { +			RTLIL::Wire *w = box_module->wire(port_name); +			log_assert(w); +			if (!w->port_output) +				continue; +			Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w)); +			holes_wire->port_output = true; +			holes_wire->port_id = port_id++; +			holes_module->ports.push_back(holes_wire->name); +			if (holes_cell) // whitebox +				holes_module->connect(holes_wire, holes_cell->getPort(port_name)); +			else // blackbox +				holes_module->connect(holes_wire, Const(State::S0, GetSize(w))); +		} +	} +} + +void reintegrate(RTLIL::Module *module) +{ +	auto design = module->design; +	log_assert(design); + +	map_autoidx = autoidx++; + +	RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str())); +	if (mapped_mod == NULL) +		log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module)); + +	for (auto w : mapped_mod->wires()) +		module->addWire(remap_name(w->name), GetSize(w)); + +	dict<IdString,std::vector<IdString>> box_ports; + +	for (auto m : design->modules()) { +		if (!m->attributes.count(ID(abc9_box_id))) +			continue; + +		auto r = box_ports.insert(m->name); +		if (r.second) { +			// Make carry in the last PI, and carry out the last PO +			//   since ABC requires it this way +			IdString carry_in, carry_out; +			for (const auto &port_name : m->ports) { +				auto w = m->wire(port_name); +				log_assert(w); +				if (w->get_bool_attribute("\\abc9_carry")) { +					if (w->port_input) { +						if (carry_in != IdString()) +							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); +						carry_in = port_name; +					} +					if (w->port_output) { +						if (carry_out != IdString()) +							log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(m)); +						carry_out = port_name; +					} +				} +				else +					r.first->second.push_back(port_name); +			} + +			if (carry_in != IdString() && carry_out == IdString()) +				log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m)); +			if (carry_in == IdString() && carry_out != IdString()) +				log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m)); +			if (carry_in != IdString()) { +				r.first->second.push_back(carry_in); +				r.first->second.push_back(carry_out); +			} +		} +	} + +	std::vector<Cell*> boxes; +	for (auto cell : module->cells().to_vector()) { +		if (cell->has_keep_attr()) +			continue; +		if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) +			module->remove(cell); +		else if (cell->attributes.erase("\\abc9_box_seq")) +			boxes.emplace_back(cell); +	} + +	dict<SigBit, pool<IdString>> bit_drivers, bit_users; +	TopoSort<IdString, RTLIL::sort_by_id_str> toposort; +	dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; +	dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; + +	std::map<IdString, int> cell_stats; +	for (auto mapped_cell : mapped_mod->cells()) +	{ +		// TODO: Speed up toposort -- we care about NOT ordering only +		toposort.node(mapped_cell->name); + +		if (mapped_cell->type == ID($_NOT_)) { +			RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A); +			RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); +			bit_users[a_bit].insert(mapped_cell->name); +			// Ignore inouts for topo ordering +			if (y_bit.wire && !(y_bit.wire->port_input && y_bit.wire->port_output)) +				bit_drivers[y_bit].insert(mapped_cell->name); + +			if (!a_bit.wire) { +				mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); +				RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); +				log_assert(wire); +				module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); +			} +			else { +				RTLIL::Cell* driver_lut = nullptr; +				// ABC can return NOT gates that drive POs +				if (!a_bit.wire->port_input) { +					// If it's not a NOT gate that that comes from a PI directly, +					// find the driver LUT and clone that to guarantee that we won't +					// increase the max logic depth +					// (TODO: Optimise by not cloning unless will increase depth) +					RTLIL::IdString driver_name; +					if (GetSize(a_bit.wire) == 1) +						driver_name = stringf("$lut%s", a_bit.wire->name.c_str()); +					else +						driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset); +					driver_lut = mapped_mod->cell(driver_name); +				} + +				if (!driver_lut) { +					// If a driver couldn't be found (could be from PI or box CI) +					// then implement using a LUT +					RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())), +							RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), +							RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), +							RTLIL::Const::from_string("01")); +					bit2sinks[cell->getPort(ID::A)].push_back(cell); +					cell_stats[ID($lut)]++; +				} +				else +					not2drivers[mapped_cell] = driver_lut; +			} +			continue; +		} + +		if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { +			// Convert buffer into direct connection +			if (mapped_cell->type == ID($lut) && +					GetSize(mapped_cell->getPort(ID::A)) == 1 && +					mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { +				SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name)); +				SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name)); +				module->connect(my_y, my_a); +				log_abort(); +				continue; +			} +			RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); +			cell->parameters = mapped_cell->parameters; +			cell->attributes = mapped_cell->attributes; + +			for (auto &mapped_conn : mapped_cell->connections()) { +				RTLIL::SigSpec newsig; +				for (auto c : mapped_conn.second.chunks()) { +					if (c.width == 0) +						continue; +					//log_assert(c.width == 1); +					if (c.wire) +						c.wire = module->wires_.at(remap_name(c.wire->name)); +					newsig.append(c); +				} +				cell->setPort(mapped_conn.first, newsig); + +				if (cell->input(mapped_conn.first)) { +					for (auto i : newsig) +						bit2sinks[i].push_back(cell); +					for (auto i : mapped_conn.second) +						bit_users[i].insert(mapped_cell->name); +				} +				if (cell->output(mapped_conn.first)) +					for (auto i : mapped_conn.second) +						// Ignore inouts for topo ordering +						if (i.wire && !(i.wire->port_input && i.wire->port_output)) +							bit_drivers[i].insert(mapped_cell->name); +			} +		} +		else { +			RTLIL::Cell *existing_cell = module->cell(mapped_cell->name); +			log_assert(existing_cell); + +			RTLIL::Module* box_module = design->module(existing_cell->type); +			auto it = box_module->attributes.find(ID(abc9_box_id)); +			log_assert(it != box_module->attributes.end()); +			log_assert(mapped_cell->type == stringf("$__boxid%d", it->second.as_int())); +			mapped_cell->type = existing_cell->type; + +			RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); +			cell->parameters = existing_cell->parameters; +			cell->attributes = existing_cell->attributes; +			module->swap_names(cell, existing_cell); + +			auto jt = mapped_cell->connections_.find("\\i"); +			log_assert(jt != mapped_cell->connections_.end()); +			SigSpec inputs = std::move(jt->second); +			mapped_cell->connections_.erase(jt); +			jt = mapped_cell->connections_.find("\\o"); +			log_assert(jt != mapped_cell->connections_.end()); +			SigSpec outputs = std::move(jt->second); +			mapped_cell->connections_.erase(jt); + +			auto abc9_flop = box_module->attributes.count("\\abc9_flop"); +			if (!abc9_flop) { +				for (const auto &i : inputs) +					bit_users[i].insert(mapped_cell->name); +				for (const auto &i : outputs) +					// Ignore inouts for topo ordering +					if (i.wire && !(i.wire->port_input && i.wire->port_output)) +						bit_drivers[i].insert(mapped_cell->name); +			} + +			int input_count = 0, output_count = 0; +			for (const auto &port_name : box_ports.at(cell->type)) { +				RTLIL::Wire *w = box_module->wire(port_name); +				log_assert(w); + +				SigSpec sig; +				if (w->port_input) { +					sig = inputs.extract(input_count, GetSize(w)); +					input_count += GetSize(w); +				} +				if (w->port_output) { +					sig = outputs.extract(output_count, GetSize(w)); +					output_count += GetSize(w); +				} + +				SigSpec newsig; +				for (auto c : sig.chunks()) { +					if (c.width == 0) +						continue; +					//log_assert(c.width == 1); +					if (c.wire) +						c.wire = module->wires_.at(remap_name(c.wire->name)); +					newsig.append(c); +				} +				cell->setPort(port_name, newsig); + +				if (w->port_input && !abc9_flop) +					for (const auto &i : newsig) +						bit2sinks[i].push_back(cell); +			} +		} + +		cell_stats[mapped_cell->type]++; +	} + +	for (auto cell : boxes) +		module->remove(cell); + +	// Copy connections (and rename) from mapped_mod to module +	for (auto conn : mapped_mod->connections()) { +		if (!conn.first.is_fully_const()) { +			auto chunks = conn.first.chunks(); +			for (auto &c : chunks) +				c.wire = module->wires_.at(remap_name(c.wire->name)); +			conn.first = std::move(chunks); +		} +		if (!conn.second.is_fully_const()) { +			auto chunks = conn.second.chunks(); +			for (auto &c : chunks) +				if (c.wire) +					c.wire = module->wires_.at(remap_name(c.wire->name)); +			conn.second = std::move(chunks); +		} +		module->connect(conn); +	} + +	for (auto &it : cell_stats) +		log("ABC RESULTS:   %15s cells: %8d\n", it.first.c_str(), it.second); +	int in_wires = 0, out_wires = 0; + +	// Stitch in mapped_mod's inputs/outputs into module +	for (auto port : mapped_mod->ports) { +		RTLIL::Wire *mapped_wire = mapped_mod->wire(port); +		RTLIL::Wire *wire = module->wire(port); +		log_assert(wire); +		if (wire->attributes.erase(ID(abc9_scc_id))) { +			auto r YS_ATTRIBUTE(unused) = wire->attributes.erase(ID::keep); +			log_assert(r); +		} +		RTLIL::Wire *remap_wire = module->wire(remap_name(port)); +		RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire)); +		log_assert(GetSize(signal) >= GetSize(remap_wire)); + +		RTLIL::SigSig conn; +		if (mapped_wire->port_output) { +			conn.first = signal; +			conn.second = remap_wire; +			out_wires++; +			module->connect(conn); +		} +		else if (mapped_wire->port_input) { +			conn.first = remap_wire; +			conn.second = signal; +			in_wires++; +			module->connect(conn); +		} +	} + +	// ABC9 will return $_NOT_ gates in its mapping (since they are +	//   treated as being "free"), in particular driving primary +	//   outputs (real primary outputs, or cells treated as blackboxes) +	//   or driving box inputs. +	// Instead of just mapping those $_NOT_ gates into 2-input $lut-s +	//   at an area and delay cost, see if it is possible to push +	//   this $_NOT_ into the driving LUT, or into all sink LUTs. +	// When this is not possible, (i.e. this signal drives two primary +	//   outputs, only one of which is complemented) and when the driver +	//   is a LUT, then clone the LUT so that it can be inverted without +	//   increasing depth/delay. +	for (auto &it : bit_users) +		if (bit_drivers.count(it.first)) +			for (auto driver_cell : bit_drivers.at(it.first)) +			for (auto user_cell : it.second) +				toposort.edge(driver_cell, user_cell); +	bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); +	log_assert(no_loops); + +	for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { +		RTLIL::Cell *not_cell = mapped_mod->cell(*ii); +		log_assert(not_cell); +		if (not_cell->type != ID($_NOT_)) +			continue; +		auto it = not2drivers.find(not_cell); +		if (it == not2drivers.end()) +			continue; +		RTLIL::Cell *driver_lut = it->second; +		RTLIL::SigBit a_bit = not_cell->getPort(ID::A); +		RTLIL::SigBit y_bit = not_cell->getPort(ID::Y); +		RTLIL::Const driver_mask; + +		a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); +		y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); + +		auto jt = bit2sinks.find(a_bit); +		if (jt == bit2sinks.end()) +			goto clone_lut; + +		for (auto sink_cell : jt->second) +			if (sink_cell->type != ID($lut)) +				goto clone_lut; + +		// Push downstream LUTs past inverter +		for (auto sink_cell : jt->second) { +			SigSpec A = sink_cell->getPort(ID::A); +			RTLIL::Const mask = sink_cell->getParam(ID(LUT)); +			int index = 0; +			for (; index < GetSize(A); index++) +				if (A[index] == a_bit) +					break; +			log_assert(index < GetSize(A)); +			int i = 0; +			while (i < GetSize(mask)) { +				for (int j = 0; j < (1 << index); j++) +					std::swap(mask[i+j], mask[i+j+(1 << index)]); +				i += 1 << (index+1); +			} +			A[index] = y_bit; +			sink_cell->setPort(ID::A, A); +			sink_cell->setParam(ID(LUT), mask); +		} + +		// Since we have rewritten all sinks (which we know +		// to be only LUTs) to be after the inverter, we can +		// go ahead and clone the LUT with the expectation +		// that the original driving LUT will become dangling +		// and get cleaned away +clone_lut: +		driver_mask = driver_lut->getParam(ID(LUT)); +		for (auto &b : driver_mask.bits) { +			if (b == RTLIL::State::S0) b = RTLIL::State::S1; +			else if (b == RTLIL::State::S1) b = RTLIL::State::S0; +		} +		auto cell = module->addLut(NEW_ID, +				driver_lut->getPort(ID::A), +				y_bit, +				driver_mask); +		for (auto &bit : cell->connections_.at(ID::A)) { +			bit.wire = module->wires_.at(remap_name(bit.wire->name)); +			bit2sinks[bit].push_back(cell); +		} +	} + +	//log("ABC RESULTS:        internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); +	log("ABC RESULTS:           input signals: %8d\n", in_wires); +	log("ABC RESULTS:          output signals: %8d\n", out_wires); + +	design->remove(mapped_mod); +} + +struct Abc9OpsPass : public Pass { +	Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    abc9_ops [options] [selection]\n"); +		log("\n"); +		log("This pass contains a set of supporting operations for use during ABC technology\n"); +		log("mapping, and is expected to be called in conjunction with other operations from\n"); +		log("the `abc9' script pass. Only fully-selected modules are supported.\n"); +		log("\n"); +		log("    -mark_scc\n"); +		log("        for an arbitrarily chosen cell in each unique SCC of each selected module\n"); +		log("        (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n"); +		log("        wires driven by this cell's outputs with a (* keep *) attribute in order\n"); +		log("        to break the SCC. this temporary attribute will be removed on -reintegrate.\n"); +		log("\n"); +		log("    -prep_xaiger\n"); +		log("        prepare the design for XAIGER output. this includes computing the\n"); +		log("        topological ordering of ABC9 boxes, as well as preparing the\n"); +		log("        '<module-name>$holes' module that contains the logic behaviour of ABC9\n"); +		log("        whiteboxes.\n"); +		log("\n"); +		log("    -dff\n"); +		log("        consider flop cells (those instantiating modules marked with (* abc9_flop *)\n"); +		log("        during -prep_xaiger.\n"); +		log("\n"); +		log("    -prep_dff\n"); +		log("        compute the clock domain and initial value of each flop in the design.\n"); +		log("        process the '$holes' module to support clock-enable functionality.\n"); +		log("\n"); +		log("    -reintegrate\n"); +		log("        for each selected module, re-intergrate the module '<module-name>$abc9'\n"); +		log("        by first recovering ABC9 boxes, and then stitching in the remaining primary\n"); +		log("        inputs and outputs.\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n"); + +		bool mark_scc_mode = false; +		bool prep_dff_mode = false; +		bool prep_xaiger_mode = false; +		bool reintegrate_mode = false; +		bool dff_mode = false; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			std::string arg = args[argidx]; +			if (arg == "-mark_scc") { +				mark_scc_mode = true; +				continue; +			} +			if (arg == "-prep_dff") { +				prep_dff_mode = true; +				continue; +			} +			if (arg == "-prep_xaiger") { +				prep_xaiger_mode = true; +				continue; +			} +			if (arg == "-reintegrate") { +				reintegrate_mode = true; +				continue; +			} +			if (arg == "-dff") { +				dff_mode = true; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		if (!(mark_scc_mode || prep_dff_mode || reintegrate_mode)) +			log_cmd_error("At least one of -mark_scc, -prep_{xaiger,dff}, -reintegrate must be specified.\n"); + +		if (dff_mode && !prep_xaiger_mode) +			log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n"); + +		for (auto mod : design->selected_modules()) { +			if (mod->get_bool_attribute("\\abc9_holes")) +				continue; + +			if (mod->processes.size() > 0) { +				log("Skipping module %s as it contains processes.\n", log_id(mod)); +				continue; +			} + +			if (!design->selected_whole_module(mod)) +				log_error("Can't handle partially selected module %s!\n", log_id(mod)); + +			if (mark_scc_mode) +				mark_scc(mod); +			if (prep_dff_mode) +				prep_dff(mod); +			if (prep_xaiger_mode) +				prep_xaiger(mod, dff_mode); +			if (reintegrate_mode) +				reintegrate(mod); +		} +	} +} Abc9OpsPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 90cfef71e..531ac2b99 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -192,11 +192,28 @@ struct IopadmapPass : public Pass {  			if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())  			{  				dict<SigBit, Cell *> tbuf_bits; +				pool<SigBit> driven_bits; +				// Gather tristate buffers and always-on drivers.  				for (auto cell : module->cells())  					if (cell->type == ID($_TBUF_)) {  						SigBit bit = cell->getPort(ID::Y).as_bit();  						tbuf_bits[bit] = cell; +					} else { +						for (auto port : cell->connections()) +							if (!cell->known() || cell->output(port.first)) +								for (auto bit : port.second) +									driven_bits.insert(bit); +					} + +				// If a wire is a target of an assignment, it is driven, unless the source is 'z. +				for (auto &conn : module->connections()) +					for (int i = 0; i < GetSize(conn.first); i++) { +						SigBit dstbit = conn.first[i]; +						SigBit srcbit = conn.second[i]; +						if (!srcbit.wire && srcbit.data == State::Sz) +							continue; +						driven_bits.insert(dstbit);  					}  				for (auto wire : module->selected_wires()) @@ -204,41 +221,71 @@ struct IopadmapPass : public Pass {  					if (!wire->port_output)  						continue; +					// Don't handle inout ports if we have no suitable buffer type. +					if (wire->port_input && tinoutpad_celltype.empty()) +						continue; + +					// likewise for output ports. +					if (!wire->port_input && toutpad_celltype.empty()) +						continue; +  					for (int i = 0; i < GetSize(wire); i++)  					{  						SigBit wire_bit(wire, i); +						Cell *tbuf_cell = nullptr; -						if (tbuf_bits.count(wire_bit) == 0) +						if (skip_wire_bits.count(wire_bit))  							continue; -						Cell *tbuf_cell = tbuf_bits.at(wire_bit); - -						if (tbuf_cell == nullptr) -							continue; - -						SigBit en_sig = tbuf_cell->getPort(ID(E)).as_bit(); -						SigBit data_sig = tbuf_cell->getPort(ID::A).as_bit(); +						if (tbuf_bits.count(wire_bit)) +							tbuf_cell = tbuf_bits.at(wire_bit); + +						SigBit en_sig; +						SigBit data_sig; +						bool is_driven = driven_bits.count(wire_bit); + +						if (tbuf_cell != nullptr) { +							// Found a tristate buffer — use it. +							en_sig = tbuf_cell->getPort(ID(E)).as_bit(); +							data_sig = tbuf_cell->getPort(ID::A).as_bit(); +						} else if (is_driven) { +							// No tristate buffer, but an always-on driver is present. +							// If this is an inout port, we're creating a tinoutpad +							// anyway, just with a constant 1 as enable. +							if (!wire->port_input) +								continue; +							en_sig = SigBit(State::S1); +							data_sig = wire_bit; +						} else { +							// No driver on a wire.  Create a tristate pad with always-0 +							// enable. +							en_sig = SigBit(State::S0); +							data_sig = SigBit(State::Sx); +						} -						if (wire->port_input && !tinoutpad_celltype.empty()) +						if (wire->port_input)  						{  							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());  							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype));  							cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig); -							cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit); -							cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);  							cell->attributes[ID::keep] = RTLIL::Const(1); -							module->remove(tbuf_cell); +							if (tbuf_cell) { +								module->remove(tbuf_cell); +								cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit); +								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig); +							} else if (is_driven) { +								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), wire_bit); +							} else { +								cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit); +								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig); +							}  							skip_wire_bits.insert(wire_bit);  							if (!tinoutpad_portname_pad.empty())  								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad)); -							continue; -						} - -						if (!wire->port_input && !toutpad_celltype.empty()) -						{ +						} else {  							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());  							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); @@ -247,12 +294,13 @@ struct IopadmapPass : public Pass {  							cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig);  							cell->attributes[ID::keep] = RTLIL::Const(1); -							module->remove(tbuf_cell); -							module->connect(wire_bit, data_sig); +							if (tbuf_cell) { +								module->remove(tbuf_cell); +								module->connect(wire_bit, data_sig); +							}  							skip_wire_bits.insert(wire_bit);  							if (!toutpad_portname_pad.empty())  								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad)); -							continue;  						}  					}  				} diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc index 41fdc8f3d..decf9a202 100644 --- a/passes/techmap/tribuf.cc +++ b/passes/techmap/tribuf.cc @@ -86,6 +86,7 @@ struct TribufWorker {  					cell->unsetPort(ID(S));  					cell->type = tri_type;  					tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); +					module->design->scratchpad_set_bool("tribuf.added_something", true);  					continue;  				} @@ -95,6 +96,7 @@ struct TribufWorker {  					cell->unsetPort(ID(S));  					cell->type = tri_type;  					tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); +					module->design->scratchpad_set_bool("tribuf.added_something", true);  					continue;  				}  			} @@ -130,8 +132,10 @@ struct TribufWorker {  				if (no_tribuf)  					module->connect(it.first, muxout); -				else +				else {  					module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first); +					module->design->scratchpad_set_bool("tribuf.added_something", true); +				}  			}  		}  	} | 
