diff options
Diffstat (limited to 'passes/cmds')
| -rw-r--r-- | passes/cmds/Makefile.inc | 4 | ||||
| -rw-r--r-- | passes/cmds/add.cc | 6 | ||||
| -rw-r--r-- | passes/cmds/check.cc | 162 | ||||
| -rw-r--r-- | passes/cmds/connect.cc | 10 | ||||
| -rw-r--r-- | passes/cmds/connwrappers.cc | 12 | ||||
| -rw-r--r-- | passes/cmds/copy.cc | 6 | ||||
| -rw-r--r-- | passes/cmds/cover.cc | 2 | ||||
| -rw-r--r-- | passes/cmds/delete.cc | 6 | ||||
| -rw-r--r-- | passes/cmds/design.cc | 6 | ||||
| -rw-r--r-- | passes/cmds/edgetypes.cc | 106 | ||||
| -rw-r--r-- | passes/cmds/plugin.cc | 10 | ||||
| -rw-r--r-- | passes/cmds/qwp.cc | 870 | ||||
| -rw-r--r-- | passes/cmds/rename.cc | 31 | ||||
| -rw-r--r-- | passes/cmds/scatter.cc | 6 | ||||
| -rw-r--r-- | passes/cmds/scc.cc | 67 | ||||
| -rw-r--r-- | passes/cmds/select.cc | 194 | ||||
| -rw-r--r-- | passes/cmds/setattr.cc | 119 | ||||
| -rw-r--r-- | passes/cmds/setundef.cc | 100 | ||||
| -rw-r--r-- | passes/cmds/show.cc | 42 | ||||
| -rw-r--r-- | passes/cmds/splice.cc | 41 | ||||
| -rw-r--r-- | passes/cmds/splitnets.cc | 103 | ||||
| -rw-r--r-- | passes/cmds/stat.cc | 91 | ||||
| -rw-r--r-- | passes/cmds/torder.cc | 123 | ||||
| -rw-r--r-- | passes/cmds/write_file.cc | 2 | 
24 files changed, 1952 insertions, 167 deletions
| diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 0e62abbc4..01ada7739 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -14,6 +14,7 @@ OBJS += passes/cmds/setattr.o  OBJS += passes/cmds/copy.o  OBJS += passes/cmds/splice.o  OBJS += passes/cmds/scc.o +OBJS += passes/cmds/torder.o  OBJS += passes/cmds/logcmd.o  OBJS += passes/cmds/tee.o  OBJS += passes/cmds/write_file.o @@ -21,4 +22,7 @@ OBJS += passes/cmds/connwrappers.o  OBJS += passes/cmds/cover.o  OBJS += passes/cmds/trace.o  OBJS += passes/cmds/plugin.o +OBJS += passes/cmds/check.o +OBJS += passes/cmds/qwp.o +OBJS += passes/cmds/edgetypes.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index 054cfc1cb..e698926f9 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -150,5 +150,5 @@ struct AddPass : public Pass {  		}  	}  } AddPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc new file mode 100644 index 000000000..b3622cb19 --- /dev/null +++ b/passes/cmds/check.cc @@ -0,0 +1,162 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + * + *  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/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct CheckPass : public Pass { +	CheckPass() : Pass("check", "check for obvious problems in the design") { } +	virtual void help() +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    check [options] [selection]\n"); +		log("\n"); +		log("This pass identifies the following problems in the current design:\n"); +		log("\n"); +		log(" - combinatorial loops\n"); +		log("\n"); +		log(" - two or more conflicting drivers for one wire\n"); +		log("\n"); +		log(" - used wires that do not have a driver\n"); +		log("\n"); +		log("When called with -noinit then this command also checks for wires which have\n"); +		log("the 'init' attribute set.\n"); +		log("\n"); +		log("When called with -assert then the command will produce an error if any\n"); +		log("problems are found in the current design.\n"); +		log("\n"); +	} +	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) +	{ +		int counter = 0; +		bool noinit = false; +		bool assert_mode = false; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			if (args[argidx] == "-noinit") { +				noinit = true; +				continue; +			} +			if (args[argidx] == "-assert") { +				assert_mode = true; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		log_header(design, "Executing CHECK pass (checking for obvious problems).\n"); + +		for (auto module : design->selected_whole_modules_warn()) +		{ +			if (module->has_processes_warn()) +				continue; + +			log("checking module %s..\n", log_id(module)); + +			SigMap sigmap(module); +			dict<SigBit, vector<string>> wire_drivers; +			dict<SigBit, int> wire_drivers_count; +			pool<SigBit> used_wires; +			TopoSort<string> topo; + +			for (auto cell : module->cells()) +			for (auto &conn : cell->connections()) { +				SigSpec sig = sigmap(conn.second); +				bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); +				if (cell->input(conn.first)) +					for (auto bit : sig) +						if (bit.wire) { +							if (logic_cell) +								topo.edge(stringf("wire %s", log_signal(bit)), +										stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); +							used_wires.insert(bit); +						} +				if (cell->output(conn.first)) +					for (int i = 0; i < GetSize(sig); i++) { +						if (logic_cell) +							topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), +									stringf("wire %s", log_signal(sig[i]))); +						if (sig[i].wire) +							wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", +									log_id(conn.first), i, log_id(cell), log_id(cell->type))); +					} +				if (!cell->input(conn.first) && cell->output(conn.first)) +					for (auto bit : sig) +						if (bit.wire) wire_drivers_count[bit]++; +			} + +			for (auto wire : module->wires()) { +				if (wire->port_input) { +					SigSpec sig = sigmap(wire); +					for (int i = 0; i < GetSize(sig); i++) +						wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); +				} +				if (wire->port_output) +					for (auto bit : sigmap(wire)) +						if (bit.wire) used_wires.insert(bit); +				if (wire->port_input && !wire->port_output) +					for (auto bit : sigmap(wire)) +						if (bit.wire) wire_drivers_count[bit]++; +				if (noinit && wire->attributes.count("\\init")) { +					log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire)); +					counter++; +				} +			} + +			for (auto it : wire_drivers) +				if (wire_drivers_count[it.first] > 1) { +					string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first)); +					for (auto str : it.second) +						message += stringf("    %s\n", str.c_str()); +					log_warning("%s", message.c_str()); +					counter++; +				} + +			for (auto bit : used_wires) +				if (!wire_drivers.count(bit)) { +					log_warning("Wire %s.%s is used but has no driver.\n", log_id(module), log_signal(bit)); +					counter++; +				} + +			topo.sort(); +			for (auto &loop : topo.loops) { +				string message = stringf("found logic loop in module %s:\n", log_id(module)); +				for (auto &str : loop) +					message += stringf("    %s\n", str.c_str()); +				log_warning("%s", message.c_str()); +				counter++; +			} +		} + +		log("found and reported %d problems.\n", counter); + +		if (assert_mode && counter > 0) +			log_error("Found %d problems in 'check -assert'.\n", counter); +	} +} CheckPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index e17c1b1c3..52611cf44 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -49,8 +49,8 @@ struct ConnectPass : public Pass {  		log("\n");  		log("    connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>\n");  		log("\n"); -		log("Create a connection. This is equivialent to adding the statement 'assign\n"); -		log("<lhs-expr> = <rhs-expr>;' to the verilog input. Per default, all existing\n"); +		log("Create a connection. This is equivalent to adding the statement 'assign\n"); +		log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n");  		log("drivers for <lhs-expr> are unconnected. This can be overwritten by using\n");  		log("the -nounset option.\n");  		log("\n"); @@ -185,5 +185,5 @@ struct ConnectPass : public Pass {  			log_cmd_error("Expected -set, -unset, or -port.\n");  	}  } ConnectPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc index a65a63644..c9ab226d5 100644 --- a/passes/cmds/connwrappers.cc +++ b/passes/cmds/connwrappers.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -158,8 +158,8 @@ struct ConnwrappersPass : public Pass {  		log("\n");  		log("Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports\n");  		log("in wrapper cells with a (larger) constant port size. I.e. the upper bits\n"); -		log("of the wrapper outut are signed/unsigned bit extended. This command uses this\n"); -		log("knowlege to rewire the inputs of the driven cells to match the output of\n"); +		log("of the wrapper output are signed/unsigned bit extended. This command uses this\n"); +		log("knowledge to rewire the inputs of the driven cells to match the output of\n");  		log("the driving cell.\n");  		log("\n");  		log("    -signed <cell_type> <port_name> <width_param>\n"); @@ -198,12 +198,12 @@ struct ConnwrappersPass : public Pass {  		}  		extra_args(args, argidx, design); -		log_header("Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); +		log_header(design, "Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n");  		for (auto &mod_it : design->modules_)  			if (design->selected(mod_it.second))  				worker.work(design, mod_it.second);  	}  } ConnwrappersPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc index 459e5b0e7..fb863512b 100644 --- a/passes/cmds/copy.cc +++ b/passes/cmds/copy.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -55,5 +55,5 @@ struct CopyPass : public Pass {  		design->add(new_mod);  	}  } CopyPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 5644066af..1475475c3 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -124,7 +124,7 @@ struct CoverPass : public Pass {  		extra_args(args, argidx, design);  		if (do_log) { -			log_header("Printing code coverage counters.\n"); +			log_header(design, "Printing code coverage counters.\n");  			log("\n");  		} diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc index b4362887e..6d51d30e7 100644 --- a/passes/cmds/delete.cc +++ b/passes/cmds/delete.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -140,5 +140,5 @@ struct DeletePass : public Pass {  		}  	}  } DeletePass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 9f800c31f..e900e7b9c 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -80,7 +80,7 @@ struct DesignPass : public Pass {  		log("\n");  		log("    design -copy-to <name> [-as <new_mod_name>] [selection]\n");  		log("\n"); -		log("Copy modules from the current design into the soecified one.\n"); +		log("Copy modules from the current design into the specified one.\n");  		log("\n");  	}  	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc new file mode 100644 index 000000000..7b75a009f --- /dev/null +++ b/passes/cmds/edgetypes.cc @@ -0,0 +1,106 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + * + *  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/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EdgetypePass : public Pass { +	EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } +	virtual void help() +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    edgetypes [options] [selection]\n"); +		log("\n"); +		log("This command lists all unique types of 'edges' found in the selection. An 'edge'\n"); +		log("is a 4-tuple of source and sink cell type and port name.\n"); +		log("\n"); +	} +	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) +	{ +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			// if (args[argidx] == "-ltr") { +			// 	config.ltr = true; +			// 	continue; +			// } +			break; +		} +		extra_args(args, argidx, design); + +		pool<string> edge_cache; + +		for (auto module : design->selected_modules()) +		{ +			SigMap sigmap(module); +			dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks; +			pool<std::pair<IdString, IdString>> multibit_ports; + +			for (auto cell : module->selected_cells()) +			for (auto conn : cell->connections()) +			{ +				IdString cell_type = cell->type; +				IdString port_name = conn.first; +				SigSpec sig = sigmap(conn.second); + +				if (GetSize(sig) > 1) +					multibit_ports.insert(std::pair<IdString, IdString>(cell_type, port_name)); + +				for (int i = 0; i < GetSize(sig); i++) { +					if (cell->output(port_name)) +						bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); +					if (cell->input(port_name)) +						bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); +				} +			} + +			for (auto &it : bit_sources) +			for (auto &source : it.second) +			for (auto &sink : bit_sinks[it.first]) +			{ +				auto source_cell_type = std::get<0>(source); +				auto source_port_name = std::get<1>(source); +				auto source_bit_index = std::get<2>(source); + +				auto sink_cell_type = std::get<0>(sink); +				auto sink_port_name = std::get<1>(sink); +				auto sink_bit_index = std::get<2>(sink); + +				string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ? +						stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) : +						stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name)); + +				string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ? +						stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) : +						stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name)); + +				edge_cache.insert(source_str + " " + sink_str); +			} +		} + +		edge_cache.sort(); +		for (auto &str : edge_cache) +			log("%s\n", str.c_str()); +	} +} EdgetypePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index f7c65bbde..828c671de 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -31,19 +31,23 @@ std::map<std::string, std::string> loaded_plugin_aliases;  #ifdef YOSYS_ENABLE_PLUGINS  void load_plugin(std::string filename, std::vector<std::string> aliases)  { +	std::string orig_filename = filename; +  	if (filename.find('/') == std::string::npos)  		filename = "./" + filename;  	if (!loaded_plugins.count(filename)) {  		void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL); +		if (hdl == NULL && orig_filename.find('/') == std::string::npos) +			hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL);  		if (hdl == NULL)  			log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror()); -		loaded_plugins[filename] = hdl; +		loaded_plugins[orig_filename] = hdl;  		Pass::init_register();  	}  	for (auto &alias : aliases) -		loaded_plugin_aliases[alias] = filename; +		loaded_plugin_aliases[alias] = orig_filename;  }  #else  void load_plugin(std::string, std::vector<std::string>) @@ -115,7 +119,7 @@ struct PluginPass : public Pass {  				log("\n");  				int max_alias_len = 1;  				for (auto &it : loaded_plugin_aliases) -					max_alias_len = std::max(max_alias_len, GetSize(it.first)); +					max_alias_len = max(max_alias_len, GetSize(it.first));  				for (auto &it : loaded_plugin_aliases)  					log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str());  			} diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc new file mode 100644 index 000000000..1b800b6df --- /dev/null +++ b/passes/cmds/qwp.cc @@ -0,0 +1,870 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + * + *  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/yosys.h" +#include "kernel/sigtools.h" + +#undef LOG_MATRICES +#undef PYPLOT_EDGES + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static uint32_t xorshift32_state; + +static double xorshift32() +{ +	xorshift32_state ^= xorshift32_state << 13; +	xorshift32_state ^= xorshift32_state >> 17; +	xorshift32_state ^= xorshift32_state << 5; +	return (xorshift32_state % 1000000) / 1e6; +} + +struct QwpConfig +{ +	bool ltr; +	bool alpha; +	bool verbose; +	double grid; + +	std::ofstream dump_file; + +	QwpConfig() { +		ltr = false; +		alpha = false; +		verbose = false; +		grid = 1.0 / 16; +	} +}; + +struct QwpWorker +{ +	QwpConfig &config; +	Module *module; +	char direction; + +	struct Node { +		Cell *cell; +		bool tied, alt_tied; + +		// pos = position in current direction +		// alt_pos = position in the other direction +		double pos, alt_pos; + +		Node() { +			cell = nullptr; +			tied = false; +			pos = xorshift32(); +			alt_tied = false; +			alt_pos = xorshift32(); +		} + +		void tie(double v) { +			tied = true; +			pos = v; +		} + +		void alt_tie(double v) { +			alt_tied = true; +			alt_pos = v; +		} + +		void swap_alt() { +			std::swap(tied, alt_tied); +			std::swap(pos, alt_pos); +		} + +		void proj_left(double midpos) { +			cell = nullptr; +			tie(pos > midpos ? midpos : pos); +		} + +		void proj_right(double midpos) { +			cell = nullptr; +			tie(pos < midpos ? midpos : pos); +		} +	}; + +	vector<Node> nodes; +	dict<pair<int, int>, double> edges; +	dict<Cell*, int> cell_to_node; + +	// worker state variables +	double midpos; +	double radius; +	double alt_midpos; +	double alt_radius; + +	QwpWorker(QwpConfig &config, Module *module, char direction = 'x') : config(config), module(module), direction(direction) +	{ +		log_assert(direction == 'x' || direction == 'y'); +	} + +	void load_module() +	{ +		log_assert(direction == 'x'); + +		SigMap sigmap(module); +		dict<SigBit, pool<int>> bits_to_nodes; + +		if (config.ltr || config.alpha) +		{ +			dict<Wire*, double> alpha_inputs, alpha_outputs; + +			if (config.alpha) +			{ +				dict<string, Wire*> alpha_order; + +				for (auto wire : module->wires()) { +					if (wire->port_input || wire->port_output) +						alpha_order[wire->name.str()] = wire; +				} + +				alpha_order.sort(); + +				for (auto &it : alpha_order) { +					if (it.second->port_input) { +						int idx = GetSize(alpha_inputs); +						alpha_inputs[it.second] = idx + 0.5; +					} +					if (it.second->port_output) { +						int idx = GetSize(alpha_outputs); +						alpha_outputs[it.second] = idx + 0.5; +					} +				} +			} + +			for (auto wire : module->wires()) +			{ +				if (!wire->port_input && !wire->port_output) +					continue; + +				int idx = GetSize(nodes); +				nodes.push_back(Node()); + +				if (config.ltr) { +					if (wire->port_input) +						nodes[idx].tie(0.0); +					else +						nodes[idx].tie(1.0); +				} + +				if (config.alpha) { +					if (wire->port_input) +						nodes[idx].alt_tie(alpha_inputs.at(wire) / GetSize(alpha_inputs)); +					else +						nodes[idx].alt_tie(alpha_outputs.at(wire) / GetSize(alpha_outputs)); +				} + +				for (auto bit : sigmap(wire)) +					bits_to_nodes[bit].insert(idx); +			} +		} + +		for (auto cell : module->selected_cells()) +		{ +			log_assert(cell_to_node.count(cell) == 0); +			int idx = GetSize(nodes); +			nodes.push_back(Node()); + +			cell_to_node[cell] = GetSize(nodes); +			nodes[idx].cell = cell; + +			for (auto &conn : cell->connections()) +			for (auto bit : sigmap(conn.second)) +				bits_to_nodes[bit].insert(idx); +		} + +		for (auto &it : bits_to_nodes) +		{ +			if (GetSize(it.second) > 100) +				continue; + +			for (int idx1 : it.second) +			for (int idx2 : it.second) +				if (idx1 < idx2) +					edges[pair<int, int>(idx1, idx2)] += 1.0 / GetSize(it.second); +		} +	} + +	void solve(bool alt_mode = false) +	{ +		// A := constraint_matrix +		// y := constraint_rhs_vector +		// +		// AA = A' * A +		// Ay = A' * y +		// +		// M := [AA Ay] + +		if (config.verbose) +			log("> System size: %d^2\n", GetSize(nodes)); + +		// Row major order +		int N = GetSize(nodes), N1 = N+1; +		vector<double> M(N * N1); + +		if (config.verbose) +			log("> Edge constraints: %d\n", GetSize(edges)); + +		// Edge constraints: +		//   A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0 +		// +		// i.e. nonzero columns in A[i,:] at the two node indices. +		for (auto &edge : edges) +		{ +			int idx1 = edge.first.first; +			int idx2 = edge.first.second; +			double weight = edge.second * (1.0 + xorshift32() * 1e-3); + +			M[idx1 + idx1*N1] += weight * weight; +			M[idx2 + idx2*N1] += weight * weight; + +			M[idx1 + idx2*N1] += -weight * weight; +			M[idx2 + idx1*N1] += -weight * weight; +		} + +		if (config.verbose) +			log("> Node constraints: %d\n", GetSize(nodes)); + +		// Node constraints: +		//   A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos +		// +		// i.e. nonzero column in A[i,:] at the node index +		// +		// "tied" nodes have a large weight, pinning them in position. Untied +		// nodes have a small weight, giving then a tiny preference to stay at +		// the current position, making sure that AA never is singular. +		for (int idx = 0; idx < GetSize(nodes); idx++) +		{ +			auto &node = nodes[idx]; +			double rhs = (alt_mode ? node.alt_pos : node.pos); + +			double weight = 1e-3; +			if (alt_mode ? node.alt_tied : node.tied) +				weight = 1e3; +			weight *= (1.0 + xorshift32() * 1e-3); + +			M[idx + idx*N1] += weight * weight; +			M[N + idx*N1] += rhs * weight * weight; +		} + +#ifdef LOG_MATRICES +		log("\n"); +		for (int i = 0; i < N; i++) { +			for (int j = 0; j < N+1; j++) +				log(" %10.2e", M[(N+1)*i + j]); +			log("\n"); +		} +#endif + +		if (config.verbose) +			log("> Solving\n"); + +		// Solve "AA*x = Ay" +		// (least squares fit for "A*x = y") +		// +		// Using gaussian elimination to get M := [Id x] + +		vector<int> pivot_cache; +		vector<int> queue; + +		for (int i = 0; i < N; i++) +			queue.push_back(i); + +		// gaussian elimination +		for (int i = 0; i < N; i++) +		{ +			if (config.verbose && ((i+1) % (N/15)) == 0) +				log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N); + +			// find best row +			int best_row = queue.front(); +			int best_row_queue_idx = 0; +			double best_row_absval = 0; + +			for (int k = 0; k < GetSize(queue); k++) { +				int row = queue[k]; +				double absval = fabs(M[i + row*N1]); +				if (absval > best_row_absval) { +					best_row = row; +					best_row_queue_idx = k; +					best_row_absval = absval; +				} +			} + +			int row = best_row; +			pivot_cache.push_back(row); + +			queue[best_row_queue_idx] = queue.back(); +			queue.pop_back(); + +			// normalize row +			for (int k = i+1; k < N1; k++) +				M[k + row*N1] /= M[i + row*N1]; +			M[i + row*N1] = 1.0; + +			// elimination +			for (int other_row : queue) { +				double d = M[i + other_row*N1]; +				for (int k = i+1; k < N1; k++) +					M[k + other_row*N1] -= d*M[k + row*N1]; +				M[i + other_row*N1] = 0.0; +			} +		} + +		if (config.verbose) +			log("> Solved\n"); + +		log_assert(queue.empty()); +		log_assert(GetSize(pivot_cache) == N); + +		// back substitution +		for (int i = N-1; i >= 0; i--) +		for (int j = i+1; j < N; j++) +		{ +			int row = pivot_cache[i]; +			int other_row = pivot_cache[j]; +			M[N + row*N1] -= M[j + row*N1] * M[N + other_row*N1]; +			M[j + row*N1] = 0.0; +		} + +#ifdef LOG_MATRICES +		log("\n"); +		for (int i = 0; i < N; i++) { +			for (int j = 0; j < N+1; j++) +				log(" %10.2e", M[(N+1)*i + j]); +			log("\n"); +		} +#endif + +		if (config.verbose) +			log("> Update nodes\n"); + +		// update node positions +		for (int i = 0; i < N; i++) +		{ +			double v = M[(N+1)*i + N]; +			double c = alt_mode ? alt_midpos : midpos; +			double r = alt_mode ? alt_radius : radius; + +			if (std::isfinite(v)) { +				v = min(v, c+r); +				v = max(v, c-r); +			} else { +				v = c; +			} + +			if (alt_mode) { +				if (!nodes[i].alt_tied) +					nodes[i].alt_pos = v; +			} else { +				if (!nodes[i].tied) +					nodes[i].pos = v; +			} +		} +	} + +	void log_cell_coordinates(int indent, bool log_all_nodes = false) +	{ +		for (auto &node : nodes) +		{ +			if (node.cell == nullptr && !log_all_nodes) +				continue; + +			for (int i = 0; i < indent; i++) +				log("  "); + +			if (direction == 'x') +				log("X=%.2f, Y=%.2f", node.pos, node.alt_pos); +			else +				log("X=%.2f, Y=%.2f", node.alt_pos, node.pos); + +			if (node.tied) +				log(" [%c-tied]", direction); + +			if (node.alt_tied) +				log(" [%c-tied]", direction == 'x' ? 'y' : 'x'); + +			if (node.cell != nullptr) +				log(" %s (%s)", log_id(node.cell), log_id(node.cell->type)); +			else +				log(" (none)"); + +			log("\n"); +		} +	} + +	void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1) +	{ +		double x_center = direction == 'x' ? midpos : alt_midpos; +		double y_center = direction == 'y' ? midpos : alt_midpos; + +		double x_radius = direction == 'x' ? radius : alt_radius; +		double y_radius = direction == 'y' ? radius : alt_radius; + +		config.dump_file << stringf("<svg height=\"240\" width=\"470\">\n"); +		config.dump_file << stringf("<rect x=\"0\" y=\"0\" width=\"470\" height=\"240\" style=\"fill:rgb(250,250,200);\" />\n"); +		config.dump_file << stringf("<rect x=\"20\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); +		config.dump_file << stringf("<rect x=\"250\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); + +		double win_x = 250 + 200 * (direction == 'x' ? midpos - radius : alt_midpos - alt_radius); +		double win_y =  20 + 200 * (direction == 'y' ? midpos - radius : alt_midpos - alt_radius); + +		double win_w = 200 * (direction == 'x' ? 2*radius : 2*alt_radius); +		double win_h = 200 * (direction == 'y' ? 2*radius : 2*alt_radius); + +		config.dump_file << stringf("<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" " +				"style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none\" />\n", win_x, win_y, win_w, win_h); + +		if (median >= 0) +		{ +			double x1 = 20.0, x2 = 220.0, y1 = 20.0, y2 = 220.0; + +			if (direction == 'x') +				x1 = x2 = 120 + 100 * (median - x_center) / x_radius; +			else +				y1 = y2 = 120 + 100 * (median - y_center) / y_radius; + +			config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " +					"style=\"stroke:rgb(150,0,150);stroke-width:1\" />\n", x1, y1, x2, y2); +		} + +		for (auto &edge : edges) +		{ +			auto &node1 = nodes[edge.first.first]; +			auto &node2 = nodes[edge.first.second]; + +			double x1 = direction == 'x' ? node1.pos : node1.alt_pos; +			double y1 = direction == 'y' ? node1.pos : node1.alt_pos; + +			double x2 = direction == 'x' ? node2.pos : node2.alt_pos; +			double y2 = direction == 'y' ? node2.pos : node2.alt_pos; + +			x1 = 120 + 100 * (x1 - x_center) / x_radius; +			y1 = 120 + 100 * (y1 - y_center) / y_radius; + +			x2 = 120 + 100 * (x2 - x_center) / x_radius; +			y2 = 120 + 100 * (y2 - y_center) / y_radius; + +			config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " +					"style=\"stroke:rgb(0,0,0);stroke-width:1\" />\n", x1, y1, x2, y2); +		} + +		for (int i = 0; i < GetSize(nodes); i++) +		{ +			auto &node = nodes[i]; + +			double x = direction == 'x' ? node.pos : node.alt_pos; +			double y = direction == 'y' ? node.pos : node.alt_pos; + +			x = 120 + 100 * (x - x_center) / x_radius; +			y = 120 + 100 * (y - y_center) / y_radius; + +			const char *color = node.cell == nullptr ? "blue" : "red"; + +			if (green_nodes != nullptr && green_nodes->count(i)) +				color = "green"; + +			config.dump_file << stringf("<circle cx=\"%.2f\" cy=\"%.2f\" r=\"3\" fill=\"%s\"/>\n", x, y, color); +		} + +		config.dump_file << stringf("</svg>\n"); +	} + +	void run_worker(int indent) +	{ +		int count_cells = 0; + +		for (auto &node : nodes) +			if (node.cell != nullptr) +				count_cells++; + +		for (int i = 0; i < indent; i++) +			log("  "); + +		string range_str; + +		if (direction == 'x') +			range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", +					midpos - radius, midpos + radius, +					alt_midpos - alt_radius, alt_midpos + alt_radius); +		else +			range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", +					alt_midpos - alt_radius, alt_midpos + alt_radius, +					midpos - radius, midpos + radius); + +		log("%c-qwp on %s with %d cells, %d nodes, and %d edges.\n", direction, +				range_str.c_str(), count_cells, GetSize(nodes), GetSize(edges)); + +		solve(); +		solve(true); + +		// detect median position and check for break condition + +		vector<pair<double, int>> sorted_pos; +		for (int i = 0; i < GetSize(nodes); i++) +			if (nodes[i].cell != nullptr) +				sorted_pos.push_back(pair<double, int>(nodes[i].pos, i)); + +		std::sort(sorted_pos.begin(), sorted_pos.end()); +		int median_sidx = GetSize(sorted_pos)/2; +		double median = sorted_pos[median_sidx].first; + +		double left_scale = radius / (median - (midpos - radius)); +		double right_scale = radius / ((midpos + radius) - median); + +		if (config.dump_file.is_open()) +		{ +			config.dump_file << stringf("<h4>LSQ %c-Solution for %s:</h4>\n", direction, range_str.c_str()); + +			pool<int> green_nodes; +			for (int i = 0; i < median_sidx; i++) +				green_nodes.insert(sorted_pos[i].second); + +			dump_svg(&green_nodes, median); +		} + +		for (auto &node : nodes) +		{ +			double rel_pos = node.pos - median; +			if (rel_pos < 0) { +				node.pos = midpos + left_scale*rel_pos; +				if (std::isfinite(node.pos)) { +					node.pos = min(node.pos, midpos); +					node.pos = max(node.pos, midpos - radius); +				} else +					node.pos = midpos - radius/2; +			} else { +				node.pos = midpos + right_scale*rel_pos; +				if (std::isfinite(node.pos)) { +					node.pos = max(node.pos, midpos); +					node.pos = min(node.pos, midpos + radius); +				} else +					node.pos = midpos + radius/2; +			} +		} + +		if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) { +			log_cell_coordinates(indent + 1); +			return; +		} + +		// create child workers + +		char child_direction = direction == 'x' ? 'y' : 'x'; + +		QwpWorker left_worker(config, module, child_direction); +		QwpWorker right_worker(config, module, child_direction); + +		// duplicate nodes into child workers + +		dict<int, int> left_nodes, right_nodes; + +		for (int k = 0; k < GetSize(sorted_pos); k++) +		{ +			int i = sorted_pos[k].second; + +			if (k < median_sidx) { +				left_nodes[i] = GetSize(left_worker.nodes); +				left_worker.nodes.push_back(nodes[i]); +				if (left_worker.nodes.back().pos > midpos) +					left_worker.nodes.back().pos = midpos; +				left_worker.nodes.back().swap_alt(); +			} else { +				right_nodes[i] = GetSize(right_worker.nodes); +				right_worker.nodes.push_back(nodes[i]); +				if (right_worker.nodes.back().pos < midpos) +					right_worker.nodes.back().pos = midpos; +				right_worker.nodes.back().swap_alt(); +			} +		} + +		// duplicate edges into child workers, project nodes as needed + +		for (auto &edge : edges) +		{ +			int idx1 = edge.first.first; +			int idx2 = edge.first.second; +			double weight = edge.second; + +			if (nodes[idx1].cell == nullptr && nodes[idx2].cell == nullptr) +				continue; + +			int left_idx1 = left_nodes.count(idx1) ? left_nodes.at(idx1) : -1; +			int left_idx2 = left_nodes.count(idx2) ? left_nodes.at(idx2) : -1; + +			int right_idx1 = right_nodes.count(idx1) ? right_nodes.at(idx1) : -1; +			int right_idx2 = right_nodes.count(idx2) ? right_nodes.at(idx2) : -1; + +			if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) { +				left_idx2 = left_nodes[idx2] = GetSize(left_worker.nodes); +				left_worker.nodes.push_back(nodes[idx2]); +				left_worker.nodes.back().proj_left(midpos); +				left_worker.nodes.back().swap_alt(); +			} else +			if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) { +				left_idx1 = left_nodes[idx1] = GetSize(left_worker.nodes); +				left_worker.nodes.push_back(nodes[idx1]); +				left_worker.nodes.back().proj_left(midpos); +				left_worker.nodes.back().swap_alt(); +			} + +			if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) { +				right_idx2 = right_nodes[idx2] = GetSize(right_worker.nodes); +				right_worker.nodes.push_back(nodes[idx2]); +				right_worker.nodes.back().proj_right(midpos); +				right_worker.nodes.back().swap_alt(); +			} else +			if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) { +				right_idx1 = right_nodes[idx1] = GetSize(right_worker.nodes); +				right_worker.nodes.push_back(nodes[idx1]); +				right_worker.nodes.back().proj_right(midpos); +				right_worker.nodes.back().swap_alt(); +			} + +			if (left_idx1 >= 0 && left_idx2 >= 0) +				left_worker.edges[pair<int, int>(left_idx1, left_idx2)] += weight; + +			if (right_idx1 >= 0 && right_idx2 >= 0) +				right_worker.edges[pair<int, int>(right_idx1, right_idx2)] += weight; +		} + +		// run child workers + +		left_worker.midpos = right_worker.midpos = alt_midpos; +		left_worker.radius = right_worker.radius = alt_radius; + +		left_worker.alt_midpos = midpos - radius/2; +		right_worker.alt_midpos = midpos + radius/2; +		left_worker.alt_radius = right_worker.alt_radius = radius/2; + +		left_worker.run_worker(indent+1); +		right_worker.run_worker(indent+1); + +		// re-integrate results + +		for (auto &it : left_nodes) +			if (left_worker.nodes[it.second].cell != nullptr) { +				nodes[it.first].pos = left_worker.nodes[it.second].alt_pos; +				nodes[it.first].alt_pos = left_worker.nodes[it.second].pos; +			} + +		for (auto &it : right_nodes) +			if (right_worker.nodes[it.second].cell != nullptr) { +				nodes[it.first].pos = right_worker.nodes[it.second].alt_pos; +				nodes[it.first].alt_pos = right_worker.nodes[it.second].pos; +			} + +		if (config.dump_file.is_open()) { +			config.dump_file << stringf("<h4>Final %c-Solution for %s:</h4>\n", direction, range_str.c_str()); +			dump_svg(); +		} +	} + +	void histogram(const vector<double> &values) +	{ +		if (values.empty()) { +			log("no data\n"); +			return; +		} + +		double min_value = values.front(); +		double max_value = values.front(); + +		for (auto &v : values) { +			min_value = min(min_value, v); +			max_value = max(max_value, v); +		} + +		if (fabs(max_value - min_value) < 0.001) { +			log("all values in range %f .. %f\n", min_value, max_value); +			return; +		} + +		vector<int> buckets(60); +		int max_bucket_val = 0; + +		for (auto &v : values) { +			int idx = min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1); +			max_bucket_val = max(max_bucket_val, ++buckets.at(idx)); +		} + +		for (int i = 4; i >= 0; i--) { +			for (int k = 0; k < GetSize(buckets); k++) { +				int v = 10 * buckets[k] / max_bucket_val; +				if (v >= 2*i+1) +					log(v == 2*i+1 ? "." : ":"); +				else +					log(i == 0 ? (buckets[k] > 0 ? "," : "_") : " "); +			} +			log("\n"); +		} +		log("%-30f%30f\n", min_value, max_value); +	} + +	void run() +	{ +		log("\n"); +		log("Running qwp on module %s..\n", log_id(module)); + +		if (config.dump_file.is_open()) +			config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module)); + +		load_module(); + +		midpos = 0.5; +		radius = 0.5; +		alt_midpos = 0.5; +		alt_radius = 0.5; +		run_worker(1); + +		for (auto &node : nodes) +			if (node.cell != nullptr) +				node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos); + +		vector<double> edge_lengths; +		vector<double> weighted_edge_lengths; + +		double total_edge_length = 0; +		double total_weighted_edge_length = 0; + +		for (auto &edge : edges) +		{ +			auto &node1 = nodes[edge.first.first]; +			auto &node2 = nodes[edge.first.second]; + +			double distance = sqrt(pow(node1.pos - node2.pos, 2) + pow(node1.alt_pos - node2.alt_pos, 2)); +			double weighted_distance = distance * edge.second; + +			edge_lengths.push_back(distance); +			weighted_edge_lengths.push_back(weighted_distance); + +			total_edge_length += distance; +			total_weighted_edge_length += weighted_distance; +		} + +		log("\n"); +		log("Summary for module %s:\n", log_id(module)); +		log("Number of edges: %d\n", GetSize(edges)); +		log("Total edge length: %f\n", total_edge_length); +		log("Total weighted edge length: %f\n", total_weighted_edge_length); + +		log("\n"); +		log("Histogram over edge lengths:\n"); +		histogram(edge_lengths); + +		log("\n"); +		log("Histogram over weighted edge lengths:\n"); +		histogram(weighted_edge_lengths); +	} +}; + +struct QwpPass : public Pass { +	QwpPass() : Pass("qwp", "quadratic wirelength placer") { } +	virtual void help() +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    qwp [options] [selection]\n"); +		log("\n"); +		log("This command runs quadratic wirelength placement on the selected modules and\n"); +		log("annotates the cells in the design with 'qwp_position' attributes.\n"); +		log("\n"); +		log("    -ltr\n"); +		log("        Add left-to-right constraints: constrain all inputs on the left border\n"); +		log("        outputs to the right border.\n"); +		log("\n"); +		log("    -alpha\n"); +		log("        Add constraints for inputs/outputs to be placed in alphanumerical\n"); +		log("        order along the y-axis (top-to-bottom).\n"); +		log("\n"); +		log("    -grid N\n"); +		log("        Number of grid divisions in x- and y-direction. (default=16)\n"); +		log("\n"); +		log("    -dump <html_file_name>\n"); +		log("        Dump a protocol of the placement algorithm to the html file.\n"); +		log("\n"); +		log("    -v\n"); +		log("        Verbose solver output for profiling or debugging\n"); +		log("\n"); +		log("Note: This implementation of a quadratic wirelength placer uses exact\n"); +		log("dense matrix operations. It is only a toy-placer for small circuits.\n"); +		log("\n"); +	} +	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) +	{ +		QwpConfig config; +		xorshift32_state = 123456789; + +		log_header(design, "Executing QWP pass (quadratic wirelength placer).\n"); + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			if (args[argidx] == "-ltr") { +				config.ltr = true; +				continue; +			} +			if (args[argidx] == "-alpha") { +				config.alpha = true; +				continue; +			} +			if (args[argidx] == "-v") { +				config.verbose = true; +				continue; +			} +			if (args[argidx] == "-grid" && argidx+1 < args.size()) { +				config.grid = 1.0 / atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-dump" && argidx+1 < args.size()) { +				config.dump_file.open(args[++argidx], std::ofstream::trunc); +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		for (auto module : design->selected_modules()) +		{ +			QwpWorker worker(config, module); +			worker.run(); + +#ifdef PYPLOT_EDGES +			log("\n"); +			log("plt.figure(figsize=(10, 10));\n"); + +			for (auto &edge : worker.edges) { +				log("plt.plot([%.2f, %.2f], [%.2f, %.2f], \"r-\");\n", +						worker.nodes[edge.first.first].pos, +						worker.nodes[edge.first.second].pos, +						worker.nodes[edge.first.first].alt_pos, +						worker.nodes[edge.first.second].alt_pos); +			} + +			for (auto &node : worker.nodes) { +				const char *style = node.cell != nullptr ? "ko" : "ks"; +				log("plt.plot([%.2f], [%.2f], \"%s\");\n", node.pos, node.alt_pos, style); +			} +#endif +		} +	} +} QwpPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 17d803e96..6a002869b 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -76,12 +76,17 @@ struct RenamePass : public Pass {  		log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");  		log("with public names. This ignores all selected ports.\n");  		log("\n"); +		log("    rename -top new_name\n"); +		log("\n"); +		log("Rename top module.\n"); +		log("\n");  	}  	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)  	{  		std::string pattern_prefix = "_", pattern_suffix = "_";  		bool flag_enumerate = false;  		bool flag_hide = false; +		bool flag_top = false;  		bool got_mode = false;  		size_t argidx; @@ -98,6 +103,11 @@ struct RenamePass : public Pass {  				got_mode = true;  				continue;  			} +			if (arg == "-top" && !got_mode) { +				flag_top = true; +				got_mode = true; +				continue; +			}  			if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) {  				int pos = args[++argidx].find('%');  				pattern_prefix = args[argidx].substr(0, pos); @@ -171,6 +181,21 @@ struct RenamePass : public Pass {  			}  		}  		else +		if (flag_top) +		{ +			if (argidx+1 != args.size()) +				log_cmd_error("Invalid number of arguments!\n"); + +			IdString new_name = RTLIL::escape_id(args[argidx]); +			RTLIL::Module *module = design->top_module(); + +			if (module == NULL) +				log_cmd_error("No top module found!\n"); + +			log("Renaming module %s to %s.\n", log_id(module), log_id(new_name)); +			design->rename(module, new_name); +		} +		else  		{  			if (argidx+2 != args.size())  				log_cmd_error("Invalid number of arguments!\n"); @@ -203,5 +228,5 @@ struct RenamePass : public Pass {  		}  	}  } RenamePass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index 1cd55ecb0..f083e1f67 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -69,5 +69,5 @@ struct ScatterPass : public Pass {  		}  	}  } ScatterPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index f7f50ab2a..bb6d74474 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -18,7 +18,7 @@   */  // [[CITE]] Tarjan's strongly connected components algorithm -// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146–160, doi:10.1137/0201010 +// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010  // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm  #include "kernel/register.h" @@ -69,10 +69,10 @@ struct SccWorker  		for (auto nextCell : cellToNextCell[cell])  			if (cellLabels.count(nextCell) == 0) {  				run(nextCell, depth+1, maxDepth); -				cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); +				cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);  			} else  			if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) { -					cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); +					cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);  			}  		if (cellLabels[cell].first == cellLabels[cell].second) @@ -100,7 +100,8 @@ struct SccWorker  		}  	} -	SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool allCellTypes, int maxDepth) : design(design), module(module), sigmap(module) +	SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) : +			design(design), module(module), sigmap(module)  	{  		if (module->processes.size() > 0) {  			log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str()); @@ -167,10 +168,22 @@ struct SccWorker  		labelCounter = 0;  		cellLabels.clear(); -		while (workQueue.size() > 0) { +		while (workQueue.size() > 0) +		{  			RTLIL::Cell *cell = *workQueue.begin();  			log_assert(cellStack.size() == 0);  			cellDepth.clear(); + +			if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { +				log("Found an SCC:"); +				std::set<RTLIL::Cell*> scc; +				log(" %s", RTLIL::id2cstr(cell->name)); +				cell2scc[cell] = sccList.size(); +				scc.insert(cell); +				sccList.push_back(scc); +				log("\n"); +			} +  			run(cell, 0, maxDepth);  		} @@ -212,10 +225,18 @@ struct SccPass : public Pass {  		log("This command identifies strongly connected components (aka logic loops) in the\n");  		log("design.\n");  		log("\n"); +		log("    -expect <num>\n"); +		log("        expect to find exactly <num> SSCs. A different number of SSCs will\n"); +		log("        produce an error.\n"); +		log("\n");  		log("    -max_depth <num>\n"); -		log("        limit to loops not longer than the specified number of cells. This can\n"); -		log("        e.g. be useful in identifying local loops in a module that turns out\n"); -		log("        to be one gigantic SCC.\n"); +		log("        limit to loops not longer than the specified number of cells. This\n"); +		log("        can e.g. be useful in identifying small local loops in a module that\n"); +		log("        implements one large SCC.\n"); +		log("\n"); +		log("    -nofeedback\n"); +		log("        do not count cells that have their output fed back into one of their\n"); +		log("        inputs as single-cell scc.\n");  		log("\n");  		log("    -all_cell_types\n");  		log("        Usually this command only considers internal non-memory cells. With\n"); @@ -239,9 +260,11 @@ struct SccPass : public Pass {  		std::map<std::string, std::string> setCellAttr, setWireAttr;  		bool allCellTypes = false;  		bool selectMode = false; +		bool nofeedbackMode = false;  		int maxDepth = -1; +		int expect = -1; -		log_header("Executing SCC pass (detecting logic loops).\n"); +		log_header(design, "Executing SCC pass (detecting logic loops).\n");  		size_t argidx;  		for (argidx = 1; argidx < args.size(); argidx++) { @@ -249,6 +272,14 @@ struct SccPass : public Pass {  				maxDepth = atoi(args[++argidx].c_str());  				continue;  			} +			if (args[argidx] == "-expect" && argidx+1 < args.size()) { +				expect = atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-nofeedback") { +				nofeedbackMode = true; +				continue; +			}  			if (args[argidx] == "-all_cell_types") {  				allCellTypes = true;  				continue; @@ -282,16 +313,26 @@ struct SccPass : public Pass {  			log_cmd_error("The -set*_attr options are not implemented at the moment!\n");  		RTLIL::Selection newSelection(false); +		int scc_counter = 0;  		for (auto &mod_it : design->modules_)  			if (design->selected(mod_it.second))  			{ -				SccWorker worker(design, mod_it.second, allCellTypes, maxDepth); +				SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth); +				scc_counter += GetSize(worker.sccList);  				if (selectMode)  					worker.select(newSelection);  			} +		if (expect >= 0) { +			if (scc_counter == expect) +				log("Found and expected %d SCCs.\n", scc_counter); +			else +				log_error("Found %d SCCs but expected %d.\n", scc_counter, expect); +		} else +			log("Found %d SCCs.\n", scc_counter); +  		if (selectMode) {  			log_assert(origSelectPos >= 0);  			design->selection_stack[origSelectPos] = newSelection; @@ -299,5 +340,5 @@ struct SccPass : public Pass {  		}  	}  } SccPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 247765f0d..d2e1a2e2b 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -180,6 +180,47 @@ static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs)  	lhs.selected_members.swap(new_sel.selected_members);  } +static int my_xorshift32_rng() { +	static uint32_t x32 = 314159265; +	x32 ^= x32 << 13; +	x32 ^= x32 >> 17; +	x32 ^= x32 << 5; +	return x32 & 0x0fffffff; +} + +static void select_op_random(RTLIL::Design *design, RTLIL::Selection &lhs, int count) +{ +	vector<pair<IdString, IdString>> objects; + +	for (auto mod : design->modules()) +	{ +		if (!lhs.selected_module(mod->name)) +			continue; + +		for (auto cell : mod->cells()) { +			if (lhs.selected_member(mod->name, cell->name)) +				objects.push_back(make_pair(mod->name, cell->name)); +		} + +		for (auto wire : mod->wires()) { +			if (lhs.selected_member(mod->name, wire->name)) +				objects.push_back(make_pair(mod->name, wire->name)); +		} +	} + +	lhs = RTLIL::Selection(false); + +	while (!objects.empty() && count-- > 0) +	{ +		int idx = my_xorshift32_rng() % GetSize(objects); +		lhs.selected_members[objects[idx].first].insert(objects[idx].second); +		objects[idx] = objects.back(); +		objects.pop_back(); +	} + +	lhs.optimize(design); +} +  static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs)  {  	for (auto &mod_it : design->modules_) @@ -196,6 +237,27 @@ static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs)  	}  } +static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection &lhs) +{ +	RTLIL::Selection new_sel(false); +	for (auto &mod_it : design->modules_) +		if (lhs.selected_module(mod_it.first)) +			for (auto &cell_it : mod_it.second->cells_) +				if (lhs.selected_member(mod_it.first, cell_it.first) && design->modules_.count(cell_it.second->type)) +					new_sel.selected_modules.insert(cell_it.second->type); +	lhs = new_sel; +} + +static void select_op_module_to_cells(RTLIL::Design *design, RTLIL::Selection &lhs) +{ +	RTLIL::Selection new_sel(false); +	for (auto &mod_it : design->modules_) +		for (auto &cell_it : mod_it.second->cells_) +			if (design->modules_.count(cell_it.second->type) && lhs.selected_whole_module(cell_it.second->type)) +				new_sel.selected_members[mod_it.first].insert(cell_it.first); +	lhs = new_sel; +} +  static void select_op_fullmod(RTLIL::Design *design, RTLIL::Selection &lhs)  {  	lhs.optimize(design); @@ -365,7 +427,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, std::string str,  	}  } -static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct) +static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only)  {  	int sel_objects = 0;  	bool is_input, is_output; @@ -401,6 +463,8 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v  		for (auto &conn : cell.second->connections())  		{  			char last_mode = '-'; +			if (eval_only && !yosys_celltypes.cell_evaluable(cell.second->type)) +				goto exclude_match;  			for (auto &rule : rules) {  				last_mode = rule.mode;  				if (rule.cell_types.size() > 0 && rule.cell_types.count(cell.second->type) == 0) @@ -433,9 +497,10 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v  	return sel_objects;  } -static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) +static void select_op_expand(RTLIL::Design *design, std::string arg, char mode, bool eval_only)  { -	int pos = mode == 'x' ? 2 : 3, levels = 1, rem_objects = -1; +	int pos = (mode == 'x' ? 2 : 3) + (eval_only ? 1 : 0); +	int levels = 1, rem_objects = -1;  	std::vector<expand_rule_t> rules;  	std::set<RTLIL::IdString> limits; @@ -526,7 +591,7 @@ static void select_op_expand(RTLIL::Design *design, std::string arg, char mode)  #endif  	while (levels-- > 0 && rem_objects != 0) { -		int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct); +		int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct, eval_only);  		if (num_objects == 0)  			break;  		rem_objects -= num_objects; @@ -540,7 +605,7 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se  {  	if (design->selected_active_module.empty())  		return; -	 +  	if (sel.full_selection) {  		sel.full_selection = false;  		sel.selected_modules.clear(); @@ -610,11 +675,27 @@ static void select_stmt(RTLIL::Design *design, std::string arg)  			select_op_intersect(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);  			work_stack.pop_back();  		} else +		if (arg.size() >= 2 && arg[0] == '%' && arg[1] == 'R') { +			if (work_stack.size() < 1) +				log_cmd_error("Must have at least one element on the stack for operator %%R.\n"); +			int count = arg.size() > 2 ? atoi(arg.c_str() + 2) : 1; +			select_op_random(design, work_stack[work_stack.size()-1], count); +		} else  		if (arg == "%s") {  			if (work_stack.size() < 1)  				log_cmd_error("Must have at least one element on the stack for operator %%s.\n");  			select_op_submod(design, work_stack[work_stack.size()-1]);  		} else +		if (arg == "%M") { +			if (work_stack.size() < 1) +				log_cmd_error("Must have at least one element on the stack for operator %%M.\n"); +			select_op_cells_to_modules(design, work_stack[work_stack.size()-1]); +		} else +		if (arg == "%C") { +			if (work_stack.size() < 1) +				log_cmd_error("Must have at least one element on the stack for operator %%M.\n"); +			select_op_module_to_cells(design, work_stack[work_stack.size()-1]); +		} else  		if (arg == "%c") {  			if (work_stack.size() < 1)  				log_cmd_error("Must have at least one element on the stack for operator %%c.\n"); @@ -633,17 +714,32 @@ static void select_stmt(RTLIL::Design *design, std::string arg)  		if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) {  			if (work_stack.size() < 1)  				log_cmd_error("Must have at least one element on the stack for operator %%x.\n"); -			select_op_expand(design, arg, 'x'); +			select_op_expand(design, arg, 'x', false);  		} else  		if (arg == "%ci" || (arg.size() > 3 && arg.substr(0, 3) == "%ci" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {  			if (work_stack.size() < 1)  				log_cmd_error("Must have at least one element on the stack for operator %%ci.\n"); -			select_op_expand(design, arg, 'i'); +			select_op_expand(design, arg, 'i', false);  		} else  		if (arg == "%co" || (arg.size() > 3 && arg.substr(0, 3) == "%co" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {  			if (work_stack.size() < 1)  				log_cmd_error("Must have at least one element on the stack for operator %%co.\n"); -			select_op_expand(design, arg, 'o'); +			select_op_expand(design, arg, 'o', false); +		} else +		if (arg == "%xe" || (arg.size() > 3 && arg.substr(0, 3) == "%xe" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { +			if (work_stack.size() < 1) +				log_cmd_error("Must have at least one element on the stack for operator %%xe.\n"); +			select_op_expand(design, arg, 'x', true); +		} else +		if (arg == "%cie" || (arg.size() > 4 && arg.substr(0, 4) == "%cie" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { +			if (work_stack.size() < 1) +				log_cmd_error("Must have at least one element on the stack for operator %%cie.\n"); +			select_op_expand(design, arg, 'i', true); +		} else +		if (arg == "%coe" || (arg.size() > 4 && arg.substr(0, 4) == "%coe" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { +			if (work_stack.size() < 1) +				log_cmd_error("Must have at least one element on the stack for operator %%coe.\n"); +			select_op_expand(design, arg, 'o', true);  		} else  			log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str());  		if (work_stack.size() >= 1) @@ -684,7 +780,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)  		select_filter_active_mod(design, work_stack.back());  		return;  	} -	 +  	sel.full_selection = false;  	for (auto &mod_it : design->modules_)  	{ @@ -856,7 +952,7 @@ struct SelectPass : public Pass {  		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|  		log("\n");  		log("    select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n"); -		log("    select [ -assert-none | -assert-any ] {-read <filename> | <selection>}\n"); +		log("    select [ <assert_option> ] {-read <filename> | <selection>}\n");  		log("    select [ -list | -write <filename> | -count | -clear ]\n");  		log("    select -module <modname>\n");  		log("\n"); @@ -892,6 +988,14 @@ struct SelectPass : public Pass {  		log("        do not modify the current selection. instead assert that the given\n");  		log("        selection contains exactly N objects.\n");  		log("\n"); +		log("    -assert-max N\n"); +		log("        do not modify the current selection. instead assert that the given\n"); +		log("        selection contains less than or exactly N objects.\n"); +		log("\n"); +		log("    -assert-min N\n"); +		log("        do not modify the current selection. instead assert that the given\n"); +		log("        selection contains at least N objects.\n"); +		log("\n");  		log("    -list\n");  		log("        list all objects in the current selection\n");  		log("\n"); @@ -1012,7 +1116,7 @@ struct SelectPass : public Pass {  		log("        like %%d but swap the roles of two top sets on the stack\n");  		log("\n");  		log("    %%c\n"); -		log("        create a copy of the top set rom the stack and push it\n"); +		log("        create a copy of the top set from the stack and push it\n");  		log("\n");  		log("    %%x[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");  		log("        expand top set <num1> num times according to the specified rules.\n"); @@ -1029,19 +1133,31 @@ struct SelectPass : public Pass {  		log("\n");  		log("    %%ci[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");  		log("    %%co[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); -		log("        simmilar to %%x, but only select input (%%ci) or output cones (%%co)\n"); +		log("        similar to %%x, but only select input (%%ci) or output cones (%%co)\n"); +		log("\n"); +		log("    %%xe[...] %%cie[...] %%coe\n"); +		log("        like %%x, %%ci, and %%co but only consider combinatorial cells\n");  		log("\n");  		log("    %%a\n");  		log("        expand top set by selecting all wires that are (at least in part)\n");  		log("        aliases for selected wires.\n");  		log("\n");  		log("    %%s\n"); -		log("        expand top set by adding all modules of instantiated cells in selected\n"); +		log("        expand top set by adding all modules that implement cells in selected\n");  		log("        modules\n");  		log("\n");  		log("    %%m\n");  		log("        expand top set by selecting all modules that contain selected objects\n");  		log("\n"); +		log("    %%M\n"); +		log("        select modules that implement selected cells\n"); +		log("\n"); +		log("    %%C\n"); +		log("        select cells that implement selected modules\n"); +		log("\n"); +		log("    %%R[<num>]\n"); +		log("        select <num> random objects from top selection (default 1)\n"); +		log("\n");  		log("Example: the following command selects all wires that are connected to a\n");  		log("'GATE' input of a 'SWITCH' cell:\n");  		log("\n"); @@ -1060,6 +1176,8 @@ struct SelectPass : public Pass {  		bool assert_none = false;  		bool assert_any = false;  		int assert_count = -1; +		int assert_max = -1; +		int assert_min = -1;  		std::string write_file, read_file;  		std::string set_name, sel_str; @@ -1089,6 +1207,14 @@ struct SelectPass : public Pass {  				assert_count = atoi(args[++argidx].c_str());  				continue;  			} +			if (arg == "-assert-max" && argidx+1 < args.size()) { +				assert_max = atoi(args[++argidx].c_str()); +				continue; +			} +			if (arg == "-assert-min" && argidx+1 < args.size()) { +				assert_min = atoi(args[++argidx].c_str()); +				continue; +			}  			if (arg == "-clear") {  				clear_mode = true;  				continue; @@ -1165,14 +1291,14 @@ struct SelectPass : public Pass {  		if (none_mode && args.size() != 2)  			log_cmd_error("Option -none can not be combined with any other options.\n"); -		if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) > 1) -			log_cmd_error("Options -add, -del, -assert-none, -assert-any or -assert-count can not be combined.\n"); +		if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0) > 1) +			log_cmd_error("Options -add, -del, -assert-none, -assert-any, assert-count, -assert-max or -assert-min can not be combined.\n"); -		if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) -			log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any or -assert-count.\n"); +		if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) +			log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n"); -		if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) -			log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any or -assert-count.\n"); +		if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) +			log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");  		if (work_stack.size() == 0 && got_module) {  			RTLIL::Selection sel; @@ -1261,8 +1387,9 @@ struct SelectPass : public Pass {  		{  			if (work_stack.size() == 0)  				log_cmd_error("No selection to check.\n"); +			work_stack.back().optimize(design);  			if (!work_stack.back().empty()) -				log_error("Assertation failed: selection is not empty:%s\n", sel_str.c_str()); +				log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str());  			return;  		} @@ -1270,12 +1397,13 @@ struct SelectPass : public Pass {  		{  			if (work_stack.size() == 0)  				log_cmd_error("No selection to check.\n"); +			work_stack.back().optimize(design);  			if (work_stack.back().empty()) -				log_error("Assertation failed: selection is empty:%s\n", sel_str.c_str()); +				log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str());  			return;  		} -		if (assert_count >= 0) +		if (assert_count >= 0 || assert_max >= 0 || assert_min >= 0)  		{  			int total_count = 0;  			if (work_stack.size() == 0) @@ -1297,9 +1425,15 @@ struct SelectPass : public Pass {  						if (sel->selected_member(mod_it.first, it.first))  							total_count++;  				} -			if (assert_count != total_count) -				log_error("Assertation failed: selection contains %d elements instead of the asserted %d:%s\n", +			if (assert_count >= 0 && assert_count != total_count) +				log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n",  						total_count, assert_count, sel_str.c_str()); +			if (assert_max >= 0 && assert_max < total_count) +				log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n", +						total_count, assert_max, sel_str.c_str()); +			if (assert_min >= 0 && assert_min > total_count) +				log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n", +						total_count, assert_min, sel_str.c_str());  			return;  		} @@ -1328,7 +1462,7 @@ struct SelectPass : public Pass {  		design->selection_stack.back().optimize(design);  	}  } SelectPass; -  +  struct CdPass : public Pass {  	CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { }  	virtual void help() @@ -1343,7 +1477,7 @@ struct CdPass : public Pass {  		log("    cd <cellname>\n");  		log("\n");  		log("When no module with the specified name is found, but there is a cell\n"); -		log("with the specified name in the current module, then this is equivialent\n"); +		log("with the specified name in the current module, then this is equivalent\n");  		log("to 'cd <celltype>'.\n");  		log("\n");  		log("    cd ..\n"); @@ -1400,7 +1534,7 @@ static void log_matches(const char *title, Module *module, T list)  			log("  %s\n", RTLIL::id2cstr(id));  	}  } -  +  struct LsPass : public Pass {  	LsPass() : Pass("ls", "list modules or objects in modules") { }  	virtual void help() @@ -1444,5 +1578,5 @@ struct LsPass : public Pass {  		}  	}  } LsPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 9a6d8a038..9b05ae32f 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -32,25 +32,20 @@ struct setunset_t  	setunset_t(std::string unset_name) : name(RTLIL::escape_id(unset_name)), value(), unset(true) { } -	setunset_t(std::string set_name, std::vector<std::string> args, size_t &argidx) : name(RTLIL::escape_id(set_name)), value(), unset(false) +	setunset_t(std::string set_name, std::string set_value) : name(RTLIL::escape_id(set_name)), value(), unset(false)  	{ -		if (!args[argidx].empty() && args[argidx][0] == '"') { -			std::string str = args[argidx++].substr(1); -			while (str.size() != 0 && str[str.size()-1] != '"' && argidx < args.size()) -				str += args[argidx++]; -			if (str.size() != 0 && str[str.size()-1] == '"') -				str = str.substr(0, str.size()-1); -			value = RTLIL::Const(str); +		if (set_value.substr(0, 1) == "\"" && set_value.substr(GetSize(set_value)-1) == "\"") { +			value = RTLIL::Const(set_value.substr(1, GetSize(set_value)-2));  		} else {  			RTLIL::SigSpec sig_value; -			if (!RTLIL::SigSpec::parse(sig_value, NULL, args[argidx++])) -				log_cmd_error("Can't decode value '%s'!\n", args[argidx-1].c_str()); +			if (!RTLIL::SigSpec::parse(sig_value, NULL, set_value)) +				log_cmd_error("Can't decode value '%s'!\n", set_value.c_str());  			value = sig_value.as_const();  		}  	}  }; -static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, std::vector<setunset_t> &list) +static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::vector<setunset_t> &list)  {  	for (auto &item : list)  		if (item.unset) @@ -84,9 +79,9 @@ struct SetattrPass : public Pass {  		{  			std::string arg = args[argidx];  			if (arg == "-set" && argidx+2 < args.size()) { -				argidx += 2; -				setunset_list.push_back(setunset_t(args[argidx-1], args, argidx)); -				argidx--; +				string set_key = args[++argidx]; +				string set_val = args[++argidx]; +				setunset_list.push_back(setunset_t(set_key, set_val));  				continue;  			}  			if (arg == "-unset" && argidx+1 < args.size()) { @@ -132,7 +127,7 @@ struct SetattrPass : public Pass {  		}  	}  } SetattrPass; -  +  struct SetparamPass : public Pass {  	SetparamPass() : Pass("setparam", "set/unset parameters on objects") { }  	virtual void help() @@ -154,9 +149,9 @@ struct SetparamPass : public Pass {  		{  			std::string arg = args[argidx];  			if (arg == "-set" && argidx+2 < args.size()) { -				argidx += 2; -				setunset_list.push_back(setunset_t(args[argidx-1], args, argidx)); -				argidx--; +				string set_key = args[++argidx]; +				string set_val = args[++argidx]; +				setunset_list.push_back(setunset_t(set_key, set_val));  				continue;  			}  			if (arg == "-unset" && argidx+1 < args.size()) { @@ -180,5 +175,87 @@ struct SetparamPass : public Pass {  		}  	}  } SetparamPass; -  + +struct ChparamPass : public Pass { +	ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { } +	virtual void help() +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    chparam [ -set name value ]... [selection]\n"); +		log("\n"); +		log("Re-evaluate the selected modules with new parameters. String values must be\n"); +		log("passed in double quotes (\").\n"); +		log("\n"); +		log("\n"); +		log("    chparam -list [selection]\n"); +		log("\n"); +		log("List the available parameters of the selected modules.\n"); +		log("\n"); +	} +	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) +	{ +		std::vector<setunset_t> setunset_list; +		dict<RTLIL::IdString, RTLIL::Const> new_parameters; +		bool list_mode = false; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			std::string arg = args[argidx]; +			if (arg == "-set" && argidx+2 < args.size()) { +				string set_key = args[++argidx]; +				string set_val = args[++argidx]; +				setunset_list.push_back(setunset_t(set_key, set_val)); +				continue; +			} +			if (arg == "-list") { +				list_mode = true; +				continue; +			} +			break; +		} + +		for (int i = argidx; i < GetSize(args); i++) +			if (design->module("$abstract\\" + args[i]) != nullptr && +					design->module(RTLIL::escape_id(args[i])) == nullptr) +				args[i] = "$abstract\\" + args[i]; + +		extra_args(args, argidx, design); + +		do_setunset(new_parameters, setunset_list); + +		if (list_mode) { +			if (!new_parameters.empty()) +				log_cmd_error("The options -set and -list cannot be used together.\n"); +			for (auto module : design->selected_modules()) { +				log("%s:\n", log_id(module)); +				for (auto param : module->avail_parameters) +					log("  %s\n", log_id(param)); +			} +			return; +		} + +		pool<IdString> modnames, old_modnames; +		for (auto module : design->selected_whole_modules_warn()) { +			modnames.insert(module->name); +			old_modnames.insert(module->name); +		} +		modnames.sort(); + +		for (auto modname : modnames) { +			Module *module = design->module(modname); +			Module *new_module = design->module(module->derive(design, new_parameters)); +			if (module != new_module) { +				Module *m = new_module->clone(); +				m->name = module->name; +				design->remove(module); +				design->add(m); +			} +			if (old_modnames.count(new_module->name) == 0) +				design->remove(new_module); +		} +	} +} ChparamPass; +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index b9a29b7d2..26b2eb87d 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -79,11 +79,15 @@ struct SetundefPass : public Pass {  		log("        replace with random bits using the specified integer als seed\n");  		log("        value for the random number generator.\n");  		log("\n"); +		log("    -init\n"); +		log("        also create/update init values for flip-flops\n"); +		log("\n");  	}  	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)  	{  		bool got_value = false;  		bool undriven_mode = false; +		bool init_mode = false;  		SetundefWorker worker;  		size_t argidx; @@ -103,6 +107,10 @@ struct SetundefPass : public Pass {  				worker.next_bit_mode = 1;  				continue;  			} +			if (args[argidx] == "-init") { +				init_mode = true; +				continue; +			}  			if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {  				got_value = true;  				worker.next_bit_mode = 2; @@ -118,12 +126,8 @@ struct SetundefPass : public Pass {  		if (!got_value)  			log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n"); -		for (auto &mod_it : design->modules_) +		for (auto module : design->selected_modules())  		{ -			RTLIL::Module *module = mod_it.second; -			if (!design->selected(module)) -				continue; -  			if (undriven_mode)  			{  				if (!module->processes.empty()) @@ -151,9 +155,89 @@ struct SetundefPass : public Pass {  				}  			} +			if (init_mode) +			{ +				SigMap sigmap(module); +				pool<SigBit> ffbits; +				pool<Wire*> initwires; + +				pool<IdString> fftypes; +				fftypes.insert("$dff"); +				fftypes.insert("$dffe"); +				fftypes.insert("$dffsr"); +				fftypes.insert("$adff"); + +				std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'}; + +				for (auto c1 : list_np) +					fftypes.insert(stringf("$_DFF_%c_", c1)); + +				for (auto c1 : list_np) +				for (auto c2 : list_np) +					fftypes.insert(stringf("$_DFFE_%c%c_", c1, c2)); + +				for (auto c1 : list_np) +				for (auto c2 : list_np) +				for (auto c3 : list_01) +					fftypes.insert(stringf("$_DFF_%c%c%c_", c1, c2, c3)); + +				for (auto c1 : list_np) +				for (auto c2 : list_np) +				for (auto c3 : list_np) +					fftypes.insert(stringf("$_DFFSR_%c%c%c_", c1, c2, c3)); + +				for (auto cell : module->cells()) +				{ +					if (!fftypes.count(cell->type)) +						continue; + +					for (auto bit : sigmap(cell->getPort("\\Q"))) +						ffbits.insert(bit); +				} + +				for (auto wire : module->wires()) +				{ +					if (!wire->attributes.count("\\init")) +						continue; + +					for (auto bit : sigmap(wire)) +						ffbits.erase(bit); + +					initwires.insert(wire); +				} + +				for (int wire_types = 0; wire_types < 2; wire_types++) +					for (auto wire : module->wires()) +					{ +						if (wire->name[0] == (wire_types ? '\\' : '$')) +					next_wire: +							continue; + +						for (auto bit : sigmap(wire)) +							if (!ffbits.count(bit)) +								goto next_wire; + +						for (auto bit : sigmap(wire)) +							ffbits.erase(bit); + +						initwires.insert(wire); +					} + +				for (auto wire : initwires) +				{ +					Const &initval = wire->attributes["\\init"]; + +					for (int i = 0; i < GetSize(wire); i++) +						if (GetSize(initval) <= i) +							initval.bits.push_back(worker.next_bit()); +						else if (initval.bits[i] == State::Sx) +							initval.bits[i] = worker.next_bit(); +				} +			} +  			module->rewrite_sigspecs(worker);  		}  	}  } SetundefPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 63da29b94..87504a33f 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -41,7 +41,7 @@ struct ShowWorker  {  	CellTypes ct; -	std::vector<std::string> dot_escape_store; +	vector<shared_str> dot_escape_store;  	std::map<RTLIL::IdString, int> dot_id2num_store;  	std::map<RTLIL::IdString, int> autonames;  	int single_idx_count; @@ -552,7 +552,7 @@ struct ShowWorker  				continue;  			if (design->selected_whole_module(module->name)) {  				if (module->get_bool_attribute("\\blackbox")) { -					log("Skipping blackbox module %s.\n", id2cstr(module->name)); +					// log("Skipping blackbox module %s.\n", id2cstr(module->name));  					continue;  				} else  				if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) { @@ -590,6 +590,10 @@ struct ShowPass : public Pass {  		log("        inputs or outputs. This option can be used multiple times to specify\n");  		log("        more than one library.\n");  		log("\n"); +		log("        note: in most cases it is better to load the library before calling\n"); +		log("        show with 'read_verilog -lib <filename>'. it is also possible to\n"); +		log("        load liberty files with 'read_liberty -lib <filename>'.\n"); +		log("\n");  		log("    -prefix <prefix>\n");  		log("        generate <prefix>.* instead of ~/.yosys_show.*\n");  		log("\n"); @@ -606,7 +610,7 @@ struct ShowPass : public Pass {  		log("    -colors <seed>\n");  		log("        Randomly assign colors to the wires. The integer argument is the seed\n");  		log("        for the random number generator. Change the seed value if the colored\n"); -		log("        graph still is ambigous. A seed of zero deactivates the coloring.\n"); +		log("        graph still is ambiguous. A seed of zero deactivates the coloring.\n");  		log("\n");  		log("    -colorattr <attribute_name>\n");  		log("        Use the specified attribute to assign colors. A unique color is\n"); @@ -616,7 +620,7 @@ struct ShowPass : public Pass {  		log("        annotate busses with a label indicating the width of the bus.\n");  		log("\n");  		log("    -signed\n"); -		log("        mark ports (A, B) that are declarted as signed (using the [AB]_SIGNED\n"); +		log("        mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n");  		log("        cell parameter) with an asterisk next to the port name.\n");  		log("\n");  		log("    -stretch\n"); @@ -630,7 +634,7 @@ struct ShowPass : public Pass {  		log("        enumerate objects with internal ($-prefixed) names\n");  		log("\n");  		log("    -long\n"); -		log("        do not abbeviate objects with internal ($-prefixed) names\n"); +		log("        do not abbreviate objects with internal ($-prefixed) names\n");  		log("\n");  		log("    -notitle\n");  		log("        do not add the module name as graph title to the dot file\n"); @@ -641,18 +645,26 @@ struct ShowPass : public Pass {  		log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n");  		log("unless another prefix is specified using -prefix <prefix>.\n");  		log("\n"); +		log("Yosys on Windows and YosysJS use different defaults: The output is written\n"); +		log("to 'show.dot' in the current directory and new viewer is launched.\n"); +		log("\n");  	}  	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)  	{ -		log_header("Generating Graphviz representation of design.\n"); +		log_header(design, "Generating Graphviz representation of design.\n");  		log_push();  		std::vector<std::pair<std::string, RTLIL::Selection>> color_selections;  		std::vector<std::pair<std::string, RTLIL::Selection>> label_selections; +#if defined(EMSCRIPTEN) || defined(_WIN32) +		std::string format = "dot"; +		std::string prefix = "show"; +#else  		std::string format; -		std::string viewer_exe;  		std::string prefix = stringf("%s/.yosys_show", getenv("HOME") ? getenv("HOME") : "."); +#endif +		std::string viewer_exe;  		std::vector<std::string> libfiles;  		std::vector<RTLIL::Design*> libs;  		uint32_t colorSeed = 0; @@ -661,7 +673,7 @@ struct ShowPass : public Pass {  		bool flag_stretch = false;  		bool flag_pause = false;  		bool flag_enum = false; -		bool flag_abbeviate = true; +		bool flag_abbreviate = true;  		bool flag_notitle = false;  		RTLIL::IdString colorattr; @@ -731,12 +743,12 @@ struct ShowPass : public Pass {  			}  			if (arg == "-enum") {  				flag_enum = true; -				flag_abbeviate = false; +				flag_abbreviate = false;  				continue;  			}  			if (arg == "-long") {  				flag_enum = false; -				flag_abbeviate = false; +				flag_abbreviate = false;  				continue;  			}  			if (arg == "-notitle") { @@ -772,7 +784,7 @@ struct ShowPass : public Pass {  		}  		if (libs.size() > 0) -			log_header("Continuing show pass.\n"); +			log_header(design, "Continuing show pass.\n");  		std::string dot_file = stringf("%s.dot", prefix.c_str());  		std::string out_file = stringf("%s.%s", prefix.c_str(), format.empty() ? "svg" : format.c_str()); @@ -784,7 +796,7 @@ struct ShowPass : public Pass {  				delete lib;  			log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str());  		} -		ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbeviate, flag_notitle, color_selections, label_selections, colorattr); +		ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, color_selections, label_selections, colorattr);  		fclose(f);  		for (auto lib : libs) @@ -833,5 +845,5 @@ struct ShowPass : public Pass {  		log_pop();  	}  } ShowPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 3e0158c5c..7418ec4d2 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -36,6 +36,8 @@ struct SpliceWorker  	bool sel_by_wire;  	bool sel_any_bit;  	bool no_outputs; +	bool do_wires; +  	std::set<RTLIL::IdString> ports;  	std::set<RTLIL::IdString> no_ports; @@ -62,7 +64,7 @@ struct SpliceWorker  			return sliced_signals_cache.at(sig);  		int offset = 0; -		int p = driven_bits_map.at(sig.extract(0, 1).to_single_sigbit()) - 1; +		int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1;  		while (driven_bits.at(p) != RTLIL::State::Sm)  			p--, offset++; @@ -209,23 +211,23 @@ struct SpliceWorker  		std::vector<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires;  		std::vector<Wire*> mod_wires = module->wires(); -		for (auto mod : mod_wires) -			if (!no_outputs && mod->port_output) { -				if (!design->selected(module, mod)) +		for (auto wire : mod_wires) +			if ((!no_outputs && wire->port_output) || (do_wires && wire->name[0] == '\\')) { +				if (!design->selected(module, wire))  					continue; -				RTLIL::SigSpec sig = sigmap(mod); +				RTLIL::SigSpec sig = sigmap(wire);  				if (driven_chunks.count(sig) > 0)  					continue;  				RTLIL::SigSpec new_sig = get_spliced_signal(sig);  				if (new_sig != sig) -					rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, new_sig)); +					rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, new_sig));  			} else -			if (!mod->port_input) { -				RTLIL::SigSpec sig = sigmap(mod); +			if (!wire->port_input) { +				RTLIL::SigSpec sig = sigmap(wire);  				if (spliced_signals_cache.count(sig) && spliced_signals_cache.at(sig) != sig) -					rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, spliced_signals_cache.at(sig))); +					rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, spliced_signals_cache.at(sig)));  				else if (sliced_signals_cache.count(sig) && sliced_signals_cache.at(sig) != sig) -					rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, sliced_signals_cache.at(sig))); +					rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, sliced_signals_cache.at(sig)));  			}  		for (auto &it : rework_wires) @@ -253,7 +255,7 @@ struct SplicePass : public Pass {  		log("\n");  		log("This command adds $slice and $concat cells to the design to make the splicing\n");  		log("of multi-bit signals explicit. This for example is useful for coarse grain\n"); -		log("synthesis, where dedidacted hardware is needed to splice signals.\n"); +		log("synthesis, where dedicated hardware is needed to splice signals.\n");  		log("\n");  		log("    -sel_by_cell\n");  		log("        only select the cell ports to rewire by the cell. if the selection\n"); @@ -268,6 +270,9 @@ struct SplicePass : public Pass {  		log("        it is sufficient if the driver of any bit of a cell port is selected.\n");  		log("        by default all bits must be selected.\n");  		log("\n"); +		log("    -wires\n"); +		log("        also add $slice and $concat cells to drive otherwise unused wires.\n"); +		log("\n");  		log("    -no_outputs\n");  		log("        do not rewire selected module outputs.\n");  		log("\n"); @@ -289,6 +294,7 @@ struct SplicePass : public Pass {  		bool sel_by_wire = false;  		bool sel_any_bit = false;  		bool no_outputs = false; +		bool do_wires = false;  		std::set<RTLIL::IdString> ports, no_ports;  		size_t argidx; @@ -305,6 +311,10 @@ struct SplicePass : public Pass {  				sel_any_bit = true;  				continue;  			} +			if (args[argidx] == "-wires") { +				do_wires = true; +				continue; +			}  			if (args[argidx] == "-no_outputs") {  				no_outputs = true;  				continue; @@ -331,7 +341,7 @@ struct SplicePass : public Pass {  		if (!ports.empty() && !no_ports.empty())  			log_cmd_error("The options -port and -no_port are exclusive!\n"); -		log_header("Executing SPLICE pass (creating cells for signal splicing).\n"); +		log_header(design, "Executing SPLICE pass (creating cells for signal splicing).\n");  		for (auto &mod_it : design->modules_)  		{ @@ -348,11 +358,12 @@ struct SplicePass : public Pass {  			worker.sel_by_wire = sel_by_wire;  			worker.sel_any_bit = sel_any_bit;  			worker.no_outputs = no_outputs; +			worker.do_wires = do_wires;  			worker.ports = ports;  			worker.no_ports = no_ports;  			worker.run();  		}  	}  } SplicePass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index d4e721a5d..14eeb066f 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -50,10 +50,23 @@ struct SplitnetsWorker  			new_wire_name += format.substr(1, 1);  		RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width); -		new_wire->port_id = wire->port_id; +		new_wire->port_id = wire->port_id ? wire->port_id + offset : 0;  		new_wire->port_input = wire->port_input;  		new_wire->port_output = wire->port_output; +		if (wire->attributes.count("\\src")) +			new_wire->attributes["\\src"] = wire->attributes.at("\\src"); + +		if (wire->attributes.count("\\keep")) +			new_wire->attributes["\\keep"] = wire->attributes.at("\\keep"); + +		if (wire->attributes.count("\\init")) { +			Const old_init = wire->attributes.at("\\init"), new_init; +			for (int i = offset; i < offset+width; i++) +				new_init.bits.push_back(i < GetSize(old_init) ? old_init.bits.at(i) : State::Sx); +			new_wire->attributes["\\init"] = new_init; +		} +  		std::vector<RTLIL::SigBit> sigvec = RTLIL::SigSpec(new_wire).to_sigbit_vector();  		splitmap[wire].insert(splitmap[wire].end(), sigvec.begin(), sigvec.end());  	} @@ -96,7 +109,7 @@ struct SplitnetsPass : public Pass {  		bool flag_driver = false;  		std::string format = "[]:"; -		log_header("Executing SPLITNETS pass (splitting up multi-bit signals).\n"); +		log_header(design, "Executing SPLITNETS pass (splitting up multi-bit signals).\n");  		size_t argidx;  		for (argidx = 1; argidx < args.size(); argidx++) @@ -117,14 +130,27 @@ struct SplitnetsPass : public Pass {  		}  		extra_args(args, argidx, design); -		for (auto &mod_it : design->modules_) -		{ -			RTLIL::Module *module = mod_it.second; -			if (!design->selected(module)) -				continue; +		// module_ports_db[module_name][old_port_name] = new_port_name_list +		dict<IdString, dict<IdString, vector<IdString>>> module_ports_db; +		for (auto module : design->selected_modules()) +		{  			SplitnetsWorker worker; +			if (flag_ports) +			{ +				int normalized_port_factor = 0; + +				for (auto wire : module->wires()) +					if (wire->port_id != 0) { +						normalized_port_factor = max(normalized_port_factor, wire->port_id+1); +						normalized_port_factor = max(normalized_port_factor, GetSize(wire)+1); +					} + +				for (auto wire : module->wires()) +					wire->port_id *= normalized_port_factor; +			} +  			if (flag_driver)  			{  				CellTypes ct(design); @@ -176,14 +202,69 @@ struct SplitnetsPass : public Pass {  			module->rewrite_sigspecs(worker); +			if (flag_ports) +			{ +				for (auto wire : module->wires()) +				{ +					if (wire->port_id == 0) +						continue; + +					SigSpec sig(wire); +					worker(sig); + +					if (sig == wire) +						continue; + +					vector<IdString> &new_ports = module_ports_db[module->name][wire->name]; + +					for (SigSpec c : sig.chunks()) +						new_ports.push_back(c.as_wire()->name); +				} +			} +  			pool<RTLIL::Wire*> delete_wires;  			for (auto &it : worker.splitmap)  				delete_wires.insert(it.first);  			module->remove(delete_wires); -			module->fixup_ports(); +			if (flag_ports) +				module->fixup_ports(); +		} + +		if (!module_ports_db.empty()) +		{ +			for (auto module : design->modules()) +			for (auto cell : module->cells()) +			{ +				if (module_ports_db.count(cell->type) == 0) +					continue; + +				for (auto &it : module_ports_db.at(cell->type)) +				{ +					IdString port_id = it.first; +					const auto &new_port_ids = it.second; + +					if (!cell->hasPort(port_id)) +						continue; + +					int offset = 0; +					SigSpec sig = cell->getPort(port_id); + +					for (auto nid : new_port_ids) +					{ +						int nlen = GetSize(design->module(cell->type)->wire(nid)); +						if (offset + nlen > GetSize(sig)) +							nlen = GetSize(sig) - offset; +						if (nlen > 0) +							cell->setPort(nid, sig.extract(offset, nlen)); +						offset += nlen; +					} + +					cell->unsetPort(port_id); +				} +			}  		}  	}  } SplitnetsPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index d68c57b20..362a0edfc 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -2,11 +2,11 @@   *  yosys -- Yosys Open SYnthesis Suite   *   *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> - *   + *   *  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 @@ -19,6 +19,8 @@  #include "kernel/register.h"  #include "kernel/celltypes.h" +#include "passes/techmap/libparse.h" +  #include "kernel/log.h"  USING_YOSYS_NAMESPACE @@ -29,17 +31,21 @@ struct statdata_t  	#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \  			X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) +	#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) +  	#define X(_name) int _name;  	STAT_INT_MEMBERS  	#undef X +	double area;  	std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type; +	std::set<RTLIL::IdString> unknown_cell_area;  	statdata_t operator+(const statdata_t &other) const  	{  		statdata_t sum = other;  	#define X(_name) sum._name += _name; -		STAT_INT_MEMBERS +		STAT_NUMERIC_MEMBERS  	#undef X  		for (auto &it : num_cells_by_type)  			sum.num_cells_by_type[it.first] += it.second; @@ -50,7 +56,7 @@ struct statdata_t  	{  		statdata_t sum = *this;  	#define X(_name) sum._name *= other; -		STAT_INT_MEMBERS +		STAT_NUMERIC_MEMBERS  	#undef X  		for (auto &it : sum.num_cells_by_type)  			it.second *= other; @@ -60,14 +66,14 @@ struct statdata_t  	statdata_t()  	{  	#define X(_name) _name = 0; -		STAT_INT_MEMBERS +		STAT_NUMERIC_MEMBERS  	#undef X  	} -	statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode) +	statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)  	{  	#define X(_name) _name = 0; -		STAT_INT_MEMBERS +		STAT_NUMERIC_MEMBERS  	#undef X  		for (auto &it : mod->wires_) @@ -110,7 +116,7 @@ struct statdata_t  					int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0;  					int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0;  					int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0; -					cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y})); +					cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y}));  				}  				else if (cell_type.in("$mux", "$pmux"))  					cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y"))); @@ -118,6 +124,13 @@ struct statdata_t  					cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q")));  			} +			if (!cell_area.empty()) { +				if (cell_area.count(cell_type)) +					area += cell_area.at(cell_type); +				else +					unknown_cell_area.insert(cell_type); +			} +  			num_cells++;  			num_cells_by_type[cell_type]++;  		} @@ -141,6 +154,17 @@ struct statdata_t  		log("   Number of cells:             %6d\n", num_cells);  		for (auto &it : num_cells_by_type)  			log("     %-26s %6d\n", RTLIL::id2cstr(it.first), it.second); + +		if (!unknown_cell_area.empty()) { +			log("\n"); +			for (auto cell_type : unknown_cell_area) +				log("   Area for cell type %s is unknown!\n", cell_type.c_str()); +		} + +		if (area != 0) { +			log("\n"); +			log("   Chip area for this module: %f\n", area); +		}  	}  }; @@ -162,6 +186,26 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL  	return mod_data;  } +void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file) +{ +	std::ifstream f; +	f.open(liberty_file.c_str()); +	if (f.fail()) +		log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); +	LibertyParser libparser(f); +	f.close(); + +	for (auto cell : libparser.ast->children) +	{ +		if (cell->id != "cell" || cell->args.size() != 1) +			continue; + +		LibertyAst *ar = cell->find("area"); +		if (ar != NULL && !ar->value.empty()) +			cell_area["\\" + cell->args[0]] = atof(ar->value.c_str()); +	} +} +  struct StatPass : public Pass {  	StatPass() : Pass("stat", "print some statistics") { }  	virtual void help() @@ -178,6 +222,9 @@ struct StatPass : public Pass {  		log("        selected and a module has the 'top' attribute set, this module is used\n");  		log("        default value for this option.\n");  		log("\n"); +		log("    -liberty <liberty_file>\n"); +		log("        use cell area information from the provided liberty file\n"); +		log("\n");  		log("    -width\n");  		log("        annotate internal cell types with their word width.\n");  		log("        e.g. $add_8 for an 8 bit wide $add cell.\n"); @@ -185,11 +232,12 @@ struct StatPass : public Pass {  	}  	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)  	{ -		log_header("Printing statistics.\n"); +		log_header(design, "Printing statistics.\n");  		bool width_mode = false;  		RTLIL::Module *top_mod = NULL;  		std::map<RTLIL::IdString, statdata_t> mod_stat; +		dict<IdString, double> cell_area;  		size_t argidx;  		for (argidx = 1; argidx < args.size(); argidx++) @@ -198,6 +246,12 @@ struct StatPass : public Pass {  				width_mode = true;  				continue;  			} +			if (args[argidx] == "-liberty" && argidx+1 < args.size()) { +				string liberty_file = args[++argidx]; +				rewrite_filename(liberty_file); +				read_liberty_cellarea(cell_area, liberty_file); +				continue; +			}  			if (args[argidx] == "-top" && argidx+1 < args.size()) {  				if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)  					log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str()); @@ -208,25 +262,22 @@ struct StatPass : public Pass {  		}  		extra_args(args, argidx, design); -		for (auto &it : design->modules_) +		for (auto mod : design->selected_modules())  		{ -			if (!design->selected_module(it.first)) -				continue; -  			if (!top_mod && design->full_selection()) -				if (it.second->get_bool_attribute("\\top")) -					top_mod = it.second; +				if (mod->get_bool_attribute("\\top")) +					top_mod = mod; -			statdata_t data(design, it.second, width_mode); -			mod_stat[it.first] = data; +			statdata_t data(design, mod, width_mode, cell_area); +			mod_stat[mod->name] = data;  			log("\n"); -			log("=== %s%s ===\n", RTLIL::id2cstr(it.first), design->selected_whole_module(it.first) ? "" : " (partially selected)"); +			log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)");  			log("\n");  			data.log_data();  		} -		if (top_mod != NULL) +		if (top_mod != NULL && GetSize(mod_stat) > 1)  		{  			log("\n");  			log("=== design hierarchy ===\n"); @@ -242,5 +293,5 @@ struct StatPass : public Pass {  		log("\n");  	}  } StatPass; -  +  PRIVATE_NAMESPACE_END diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc new file mode 100644 index 000000000..56223610d --- /dev/null +++ b/passes/cmds/torder.cc @@ -0,0 +1,123 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + * + *  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/yosys.h" +#include "kernel/celltypes.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TorderPass : public Pass { +	TorderPass() : Pass("torder", "print cells in topological order") { } +	virtual void help() +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    torder [options] [selection]\n"); +		log("\n"); +		log("This command prints the selected cells in topological order.\n"); +		log("\n"); +		log("    -stop <cell_type> <cell_port>\n"); +		log("        do not use the specified cell port in topological sorting\n"); +		log("\n"); +		log("    -noautostop\n"); +		log("        by default Q outputs of internal FF cells and memory read port outputs\n"); +		log("        are not used in topological sorting. this option deactivates that.\n"); +		log("\n"); +	} +	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) +	{ +		bool noautostop = false; +		dict<IdString, pool<IdString>> stop_db; + +		log_header(design, "Executing TORDER pass (print cells in topological order).\n"); + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) { +			if (args[argidx] == "-stop" && argidx+2 < args.size()) { +				IdString cell_type = RTLIL::escape_id(args[++argidx]); +				IdString cell_port = RTLIL::escape_id(args[++argidx]); +				stop_db[cell_type].insert(cell_port); +				continue; +			} +			if (args[argidx] == "-noautostop") { +				noautostop = true; +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		for (auto module : design->selected_modules()) +		{ +			log("module %s\n", log_id(module)); + +			SigMap sigmap(module); +			dict<SigBit, pool<IdString>> bit_drivers, bit_users; +			TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + +			for (auto cell : module->selected_cells()) +			for (auto conn : cell->connections()) +			{ +				if (stop_db.count(cell->type) && stop_db.at(cell->type).count(conn.first)) +					continue; + +				if (!noautostop && yosys_celltypes.cell_known(cell->type)) { +					if (conn.first.in("\\Q", "\\CTRL_OUT", "\\RD_DATA")) +						continue; +					if (cell->type == "$memrd" && conn.first == "\\DATA") +						continue; +				} + +				if (cell->input(conn.first)) +					for (auto bit : sigmap(conn.second)) +						bit_users[bit].insert(cell->name); + +				if (cell->output(conn.first)) +					for (auto bit : sigmap(conn.second)) +						bit_drivers[bit].insert(cell->name); + +				toposort.node(cell->name); +			} + +			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); + +			toposort.analyze_loops = true; +			toposort.sort(); + +			for (auto &it : toposort.loops) { +				log("  loop"); +				for (auto cell : it) +					log(" %s", log_id(cell)); +				log("\n"); +			} + +			for (auto cell : toposort.sorted) +					log("  cell %s\n", log_id(cell)); +		} +	} +} TorderPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index 25ec4acc2..b78265933 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -31,7 +31,7 @@ struct WriteFileFrontend : public Frontend {  		log("\n");  		log("    write_file [options] output_file [input_file]\n");  		log("\n"); -		log("Write the text fron the input file to the output file.\n"); +		log("Write the text from the input file to the output file.\n");  		log("\n");  		log("    -a\n");  		log("        Append to output file (instead of overwriting)\n"); | 
