diff options
Diffstat (limited to 'passes')
| -rw-r--r-- | passes/cmds/setundef.cc | 20 | ||||
| -rw-r--r-- | passes/hierarchy/hierarchy.cc | 48 | ||||
| -rw-r--r-- | passes/opt/opt_expr.cc | 7 | ||||
| -rw-r--r-- | passes/opt/opt_rmdff.cc | 13 | ||||
| -rw-r--r-- | passes/opt/wreduce.cc | 99 | ||||
| -rw-r--r-- | passes/pmgen/.gitignore | 1 | ||||
| -rw-r--r-- | passes/pmgen/Makefile.inc | 8 | ||||
| -rw-r--r-- | passes/pmgen/README.md | 224 | ||||
| -rw-r--r-- | passes/pmgen/ice40_dsp.cc | 237 | ||||
| -rw-r--r-- | passes/pmgen/ice40_dsp.pmg | 160 | ||||
| -rw-r--r-- | passes/pmgen/pmgen.py | 486 | ||||
| -rw-r--r-- | passes/sat/Makefile.inc | 3 | ||||
| -rw-r--r-- | passes/sat/async2sync.cc | 53 | ||||
| -rw-r--r-- | passes/sat/fmcombine.cc | 341 | ||||
| -rw-r--r-- | passes/sat/mutate.cc | 905 | ||||
| -rw-r--r-- | passes/sat/supercover.cc | 92 | ||||
| -rw-r--r-- | passes/techmap/abc.cc | 93 | ||||
| -rw-r--r-- | passes/techmap/dfflibmap.cc | 4 | ||||
| -rw-r--r-- | passes/techmap/flowmap.cc | 4 | 
19 files changed, 2742 insertions, 56 deletions
| diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index 56ef2d125..f6949c820 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -143,6 +143,9 @@ struct SetundefPass : public Pass {  		log("    -init\n");  		log("        also create/update init values for flip-flops\n");  		log("\n"); +		log("    -params\n"); +		log("        replace undef in cell parameters\n"); +		log("\n");  	}  	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE  	{ @@ -150,6 +153,7 @@ struct SetundefPass : public Pass {  		bool undriven_mode = false;  		bool expose_mode = false;  		bool init_mode = false; +		bool params_mode = false;  		SetundefWorker worker;  		log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); @@ -199,6 +203,10 @@ struct SetundefPass : public Pass {  				init_mode = true;  				continue;  			} +			if (args[argidx] == "-params") { +				params_mode = true; +				continue; +			}  			if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {  				got_value = true;  				worker.next_bit_mode = MODE_RANDOM; @@ -228,6 +236,18 @@ struct SetundefPass : public Pass {  		for (auto module : design->selected_modules())  		{ +			if (params_mode) +			{ +				for (auto *cell : module->selected_cells()) { +					for (auto ¶meter : cell->parameters) { +						for (auto &bit : parameter.second.bits) { +							if (bit > RTLIL::State::S1) +								bit = worker.next_bit(); +						} +					} +				} +			} +  			if (undriven_mode)  			{  				if (!module->processes.empty()) diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 0e28dbca2..88c339e8c 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -140,6 +140,23 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,  	}  } +// Return the "basic" type for an array item. +std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) { +	std::string basicType = celltype; +	if (celltype.substr(0, 7) == "$array:") { +		int pos_idx = celltype.find_first_of(':'); +		int pos_num = celltype.find_first_of(':', pos_idx + 1); +		int pos_type = celltype.find_first_of(':', pos_num + 1); +		basicType = celltype.substr(pos_type + 1); +		if (pos != nullptr) { +			pos[0] = pos_idx; +			pos[1] = pos_num; +			pos[2] = pos_type; +		} +	} +	return basicType; +} +  bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)  {  	bool did_something = false; @@ -178,9 +195,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check  		std::vector<RTLIL::SigSpec> connections_to_add_signal;  		if (cell->type.substr(0, 7) == "$array:") { -			int pos_idx = cell->type.str().find_first_of(':'); -			int pos_num = cell->type.str().find_first_of(':', pos_idx + 1); -			int pos_type = cell->type.str().find_first_of(':', pos_num + 1); +			int pos[3]; +			basic_cell_type(cell->type.str(), pos); +			int pos_idx = pos[0]; +			int pos_num = pos[1]; +			int pos_type = pos[2];  			int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());  			int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());  			array_cells[cell] = std::pair<int, int>(idx, num); @@ -439,10 +458,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::  	for (auto cell : mod->cells()) {  		std::string celltype = cell->type.str();  		if (celltype.substr(0, 7) == "$array:") { -			int pos_idx = celltype.find_first_of(':'); -			int pos_num = celltype.find_first_of(':', pos_idx + 1); -			int pos_type = celltype.find_first_of(':', pos_num + 1); -			celltype = celltype.substr(pos_type + 1); +			celltype = basic_cell_type(celltype);  		}  		if (design->module(celltype))  			hierarchy_worker(design, used, design->module(celltype), indent+4); @@ -502,9 +518,19 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)  	if (db.count(module) == 0) {  		int score = 0;  		db[module] = 0; -		for (auto cell : module->cells()) -			if (design->module(cell->type)) -				score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1); +		for (auto cell : module->cells()) { +			std::string celltype = cell->type.str(); +			// Is this an array instance +			if (celltype.substr(0, 7) == "$array:") { +				celltype = basic_cell_type(celltype); +			} +			// Is this cell a module instance? +			auto instModule = design->module(celltype); +			// If there is no instance for this, issue a warning. +			if (instModule != nullptr) { +				score = max(score, find_top_mod_score(design, instModule, db) + 1); +			} +		}  		db[module] = score;  	}  	return db.at(module); @@ -884,7 +910,7 @@ struct HierarchyPass : public Pass {  			if (m == nullptr)  				continue; -			if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) { +			if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {  				IdString new_m_name = m->derive(design, cell->parameters, true);  				if (new_m_name.empty())  					continue; diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 26a3ca7bc..a05db2a4f 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -155,6 +155,13 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ  			new_b.append_bit(it.first.second);  		} +		if (cell->type.in("$and", "$or") && i == GRP_CONST_A) { +			log("  Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a)); +			module->connect(new_y, new_b); +			module->connect(new_conn); +			continue; +		} +  		RTLIL::Cell *c = module->addCell(NEW_ID, cell->type);  		c->setPort("\\A", new_a); diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 5880254c1..e8570f0eb 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)  			cell->unsetParam("\\CLR_POLARITY");  			cell->unsetPort("\\SET");  			cell->unsetPort("\\CLR"); - -			return true;  		}  		else  		{ @@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)  			cell->unsetParam("\\CLR_POLARITY");  			cell->unsetPort("\\SET");  			cell->unsetPort("\\CLR"); - -			return true;  		} + +		return true;  	} -	else + +	if (!hasreset)  	{  		IdString new_type; @@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)  		cell->unsetPort("\\S");  		cell->unsetPort("\\R"); -		return did_something; +		return true;  	} + +	return did_something;  }  bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 8063b86a6..1f7222e49 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -38,7 +38,8 @@ struct WreduceConfig  			"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",  			"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",  			"$add", "$sub", "$mul", // "$div", "$mod", "$pow", -			"$mux", "$pmux" +			"$mux", "$pmux", +			"$dff", "$adff"  		});  	}  }; @@ -52,6 +53,7 @@ struct WreduceWorker  	std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;  	std::set<SigBit> work_queue_bits;  	pool<SigBit> keep_bits; +	dict<SigBit, State> init_bits;  	WreduceWorker(WreduceConfig *config, Module *module) :  			config(config), module(module), mi(module) { } @@ -134,6 +136,88 @@ struct WreduceWorker  		module->connect(sig_y.extract(n_kept, n_removed), sig_removed);  	} +	void run_cell_dff(Cell *cell) +	{ +		// Reduce size of FF if inputs are just sign/zero extended or output bit is not used + +		SigSpec sig_d = mi.sigmap(cell->getPort("\\D")); +		SigSpec sig_q = mi.sigmap(cell->getPort("\\Q")); +		Const initval; + +		int width_before = GetSize(sig_q); + +		if (width_before == 0) +			return; + +		bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0; +		bool sign_ext = !zero_ext; + +		for (int i = 0; i < GetSize(sig_q); i++) { +			SigBit bit = sig_q[i]; +			if (init_bits.count(bit)) +				initval.bits.push_back(init_bits.at(bit)); +			else +				initval.bits.push_back(State::Sx); +		} + +		for (int i = GetSize(sig_q)-1; i >= 0; i--) +		{ +			if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) { +				module->connect(sig_q[i], State::S0); +				sig_d.remove(i); +				sig_q.remove(i); +				continue; +			} + +			if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) { +				module->connect(sig_q[i], sig_q[i-1]); +				sig_d.remove(i); +				sig_q.remove(i); +				continue; +			} + +			auto info = mi.query(sig_q[i]); +			if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) { +				sig_d.remove(i); +				sig_q.remove(i); +				zero_ext = false; +				sign_ext = false; +				continue; +			} + +			break; +		} + +		if (width_before == GetSize(sig_q)) +			return; + +		if (GetSize(sig_q) == 0) { +			log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); +			module->remove(cell); +			return; +		} + +		log("Removed top %d bits (of %d) from FF cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before, +				log_id(module), log_id(cell), log_id(cell->type)); + +		for (auto bit : sig_d) +			work_queue_bits.insert(bit); + +		for (auto bit : sig_q) +			work_queue_bits.insert(bit); + +		// Narrow ARST_VALUE parameter to new size. +		if (cell->parameters.count("\\ARST_VALUE")) { +			Const arst_value = cell->getParam("\\ARST_VALUE"); +			arst_value.bits.resize(GetSize(sig_q)); +			cell->setParam("\\ARST_VALUE", arst_value); +		} + +		cell->setPort("\\D", sig_d); +		cell->setPort("\\Q", sig_q); +		cell->fixup_parameters(); +	} +  	void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)  	{  		port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool(); @@ -176,6 +260,9 @@ struct WreduceWorker  		if (cell->type.in("$mux", "$pmux"))  			return run_cell_mux(cell); +		if (cell->type.in("$dff", "$adff")) +			return run_cell_dff(cell); +  		SigSpec sig = mi.sigmap(cell->getPort("\\Y"));  		if (sig.has_const()) @@ -300,10 +387,18 @@ struct WreduceWorker  	void run()  	{ -		for (auto w : module->wires()) +		for (auto w : module->wires()) {  			if (w->get_bool_attribute("\\keep"))  				for (auto bit : mi.sigmap(w))  					keep_bits.insert(bit); +			if (w->attributes.count("\\init")) { +				Const initval = w->attributes.at("\\init"); +				SigSpec initsig = mi.sigmap(w); +				int width = std::min(GetSize(initval), GetSize(initsig)); +				for (int i = 0; i < width; i++) +					init_bits[initsig[i]] = initval[i]; +			} +		}  		for (auto c : module->selected_cells())  			work_queue_cells.insert(c); diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore new file mode 100644 index 000000000..c9263057e --- /dev/null +++ b/passes/pmgen/.gitignore @@ -0,0 +1 @@ +/ice40_dsp_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc new file mode 100644 index 000000000..e0609d9ba --- /dev/null +++ b/passes/pmgen/Makefile.inc @@ -0,0 +1,8 @@ +OBJS += passes/pmgen/ice40_dsp.o + +passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h +EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h +.SECONDARY: passes/pmgen/ice40_dsp_pm.h + +passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg +	$(P) mkdir -p passes/pmgen && python3 $^ $@ diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md new file mode 100644 index 000000000..223b43059 --- /dev/null +++ b/passes/pmgen/README.md @@ -0,0 +1,224 @@ +Pattern Matcher Generator +========================= + +The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and +writes a header-only C++ library that implements that pattern matcher. + +The "patterns" in this context are subgraphs in a Yosys RTLIL netlist. + +The algorithm used in the generated pattern matcher is a simple recursive +search with backtracking. It is left to the author of the `.pmg` file to +determine an efficient cell order for the search that allows for maximum +use of indices and early backtracking. + + +API of Generated Matcher +======================== + +When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing +a class `foobar_pm`. That class is instantiated with an RTLIL module and a +list of cells from that module: + +    foobar_pm pm(module, module->selected_cells()); + +The caller must make sure that none of the cells in the 2nd argument are +deleted for as long as the patter matcher instance is used. + +At any time it is possible to disable cells, preventing them from showing +up in any future matches: + +    pm.blacklist(some_cell); + +The `.run(callback_function)` method searches for all matches and calls the +callback function for each found match: + +    pm.run([&](){ +        log("found matching 'foo' cell: %s\n", log_id(pm.st.foo)); +        log("          with 'bar' cell: %s\n", log_id(pm.st.bar)); +    }); + +The `.pmg` file declares matcher state variables that are accessible via the +`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.) + +Similarly the `.pmg` file declares user data variables that become members of +`.ud`, a struct of type `foobar_pm::udata_t`. + + +The .pmg File Format +==================== + +The `.pmg` file format is a simple line-based file format. For the most part +lines consist of whitespace-separated tokens. + +Lines in `.pmg` files starting with `//` are comments. + +Declaring state variables +------------------------- + +One or more state variables can be declared using the `state` statement, +followed by a C++ type in angle brackets, followed by a whitespace separated +list of variable names. For example: + +    state <bool> flag1 flag2 happy big +    state <SigSpec> sigA sigB sigY + +State variables are automatically managed by the generated backtracking algorithm +and saved and restored as needed. + +They are automatically initialized to the default constructed value of their type +when `.run(callback_function)` is called. + +Declaring udata variables +------------------------- + +Udata (user-data) variables can be used for example to configure the matcher or +the callback function used to perform actions on found matches. + +There is no automatic management of udata variables. For this reason it is +recommended that the user-supplied matcher code treats them as read-only +variables. + +They are declared like state variables, just using the `udata` statement: + +    udata <int> min_data_width max_data_width +    udata <IdString> data_port_name + +They are atomatically initialzed to the default constructed value of their type +when ther pattern matcher object is constructed. + +Embedded C++ code +----------------- + +Many statements in a `.pmg` file contain C++ code. However, there are some +slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to +write matchers: + +- Identifiers starting with a dollar sign or backslash are automatically +  converted to special IdString variables that are initialized when the +  matcher object is constructed. + +- The `port(<cell>, <portname>)` function is a handy alias for +  `sigmap(<cell>->getPort(<portname>))`. + +- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell. + +- The function `nusers(<sigspec>)` returns the number of different cells +  connected to any of the given signal bits, plus one if any of the signal +  bits is also a primary input or primary output. + +- In `code..endcode` blocks there exist `accept`, `reject`, and `branch` +  statements. + +- In `index` statements there is a special `===` operator for the index +  lookup. + +Matching cells +-------------- + +Cells are matched using `match..endmatch` blocks. For example: + +    match mul +        if ff +        select mul->type == $mul +        select nusers(port(mul, \Y) == 2 +        index <SigSpec> port(mul, \Y) === port(ff, \D) +        filter some_weird_function(mul) < other_weird_function(ff) +        optional +    endmatch + +A `match` block starts with `match <statevar>` and implicitly generates +a state variable `<statevar>` of type `RTLIL::Cell*`. + +All statements in the match block are optional. (An empty match block +would simply match each and every cell in the module.) + +The `if <expression>` statement makes the match block conditional. If +`<expression>` evaluates to `false` then the match block will be ignored +and the corresponding state variable is set to `nullptr`. In our example +we only try to match the `mul` cell if the `ff` state variable points +to a cell. (Presumably `ff` is provided by a prior `match` block.) + +The `select` lines are evaluated once for each cell when the matcher is +initialized. A `match` block will only consider cells for which all `select` +expressions evaluated to `true`. Note that the state variable corresponding to +the match (in the example `mul`) is the only state variable that may be used +in `select` lines. + +Index lines are using the `index <type> expr1 === expr2` syntax.  `expr1` is +evaluated during matcher initialization and the same restrictions apply as for +`select` expressions. `expr2` is evaluated when the match is calulated. It is a +function of any state variables assigned to by previous blocks. Both expression +are converted to the given type and compared for equality. Only cells for which +all `index` statements in the block pass are considered by the match. + +Note that `select` and `index` are fast operations. Thus `select` and `index` +should be used whenever possible to create efficient matchers. + +Finally, `filter <expression>` narrows down the remaining list of cells. For +performance reasons `filter` statements should only be used for things that +can't be done using `select` and `index`. + +The `optional` statement marks optional matches. I.e. the matcher will also +explore the case where `mul` is set to `nullptr`. Without the `optional` +statement a match may only be assigned nullptr when one of the `if` expressions +evaluates to `false`. + +Additional code +--------------- + +Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks. +Such a block starts with the keyword `code` followed by a list of state variables +that the block may modify. For example: + +    code addAB sigS +        if (addA) { +            addAB = addA; +            sigS = port(addA, \B); +        } +        if (addB) { +            addAB = addB; +            sigS = port(addB, \A); +        } +    endcode + +The special keyword `reject` can be used to reject the current state and +backtrack. For example: + +    code +        if (ffA && ffB) { +            if (port(ffA, \CLK) != port(ffB, \CLK)) +                reject; +            if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY)) +                reject; +        } +    endcode + +Similarly, the special keyword `accept` can be used to accept the current +state. (`accept` will not backtrack. This means it continues with the current +branch and may accept a larger match later.) + +The special keyword `branch` can be used to explore different cases. Note that +each code block has an implicit `branch` at the end. So most use-cases of the +`branch` keyword need to end the block with `reject` to avoid the implicit +branch at the end. For example: + +    state <int> mode + +    code mode +        for (mode = 0; mode < 8; mode++) +            branch; +        reject; +    endcode + +But in some cases it is more natural to utilize the implicit branch statement: + +    state <IdString> portAB + +    code portAB +        portAB = \A; +        branch; +        portAB = \B; +    endcode + +There is an implicit `code..endcode` block at the end of each `.pgm` file +that just accepts everything that gets all the way there. diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc new file mode 100644 index 000000000..3a054a463 --- /dev/null +++ b/passes/pmgen/ice40_dsp.cc @@ -0,0 +1,237 @@ +/* + *  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 "passes/pmgen/ice40_dsp_pm.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void create_ice40_dsp(ice40_dsp_pm &pm) +{ +#if 0 +	log("\n"); +	log("ffA:   %s\n", log_id(pm.st.ffA, "--")); +	log("ffB:   %s\n", log_id(pm.st.ffB, "--")); +	log("mul:   %s\n", log_id(pm.st.mul, "--")); +	log("ffY:   %s\n", log_id(pm.st.ffY, "--")); +	log("addAB: %s\n", log_id(pm.st.addAB, "--")); +	log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); +	log("ffS:   %s\n", log_id(pm.st.ffS, "--")); +#endif + +	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul)); + +	if (GetSize(pm.st.sigA) > 16) { +		log("  input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); +		return; +	} + +	if (GetSize(pm.st.sigB) > 16) { +		log("  input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); +		return; +	} + +	if (GetSize(pm.st.sigS) > 32) { +		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); +		return; +	} + +	if (GetSize(pm.st.sigY) > 32) { +		log("  output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); +		return; +	} + +	bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); + +	if (mul_signed) { +		log("  inference of signed iCE40 DSP arithmetic is currently not supported.\n"); +		return; +	} + +	log("  replacing $mul with SB_MAC16 cell.\n"); + +	Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); +	pm.module->swap_names(cell, pm.st.mul); + +	// SB_MAC16 Input Interface + +	SigSpec A = pm.st.sigA; +	A.extend_u0(16, mul_signed); + +	SigSpec B = pm.st.sigB; +	B.extend_u0(16, mul_signed); + +	SigSpec CD; +	if (pm.st.muxA) +		CD = pm.st.muxA->getPort("\\B"); +	if (pm.st.muxB) +		CD = pm.st.muxB->getPort("\\A"); +	CD.extend_u0(32, mul_signed); + +	cell->setPort("\\A", A); +	cell->setPort("\\B", B); +	cell->setPort("\\C", CD.extract(0, 16)); +	cell->setPort("\\D", CD.extract(16, 16)); + +	cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0); +	cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); + +	cell->setPort("\\AHOLD", State::S0); +	cell->setPort("\\BHOLD", State::S0); +	cell->setPort("\\CHOLD", State::S0); +	cell->setPort("\\DHOLD", State::S0); + +	cell->setPort("\\IRSTTOP", State::S0); +	cell->setPort("\\IRSTBOT", State::S0); + +	if (pm.st.clock_vld) +	{ +		cell->setPort("\\CLK", pm.st.clock); +		cell->setPort("\\CE", State::S1); +		cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1); + +		log("  clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge"); + +		if (pm.st.ffA) +			log(" ffA:%s", log_id(pm.st.ffA)); + +		if (pm.st.ffB) +			log(" ffB:%s", log_id(pm.st.ffB)); + +		if (pm.st.ffY) +			log(" ffY:%s", log_id(pm.st.ffY)); + +		if (pm.st.ffS) +			log(" ffS:%s", log_id(pm.st.ffS)); + +		log("\n"); +	} +	else +	{ +		cell->setPort("\\CLK", State::S0); +		cell->setPort("\\CE", State::S0); +		cell->setParam("\\NEG_TRIGGER", State::S0); +	} + +	// SB_MAC16 Cascade Interface + +	cell->setPort("\\SIGNEXTIN", State::Sx); +	cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID)); + +	cell->setPort("\\CI", State::Sx); +	cell->setPort("\\CO", pm.module->addWire(NEW_ID)); + +	cell->setPort("\\ACCUMCI", State::Sx); +	cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID)); + +	// SB_MAC16 Output Interface + +	SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY; +	if (GetSize(O) < 32) +		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); + +	cell->setPort("\\O", O); + +	if (pm.st.addAB) { +		log("  accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); +		cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); +		cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); +	} else { +		cell->setPort("\\ADDSUBTOP", State::S0); +		cell->setPort("\\ADDSUBBOT", State::S0); +	} + +	cell->setPort("\\ORSTTOP", State::S0); +	cell->setPort("\\ORSTBOT", State::S0); + +	cell->setPort("\\OHOLDTOP", State::S0); +	cell->setPort("\\OHOLDBOT", State::S0); + +	SigSpec acc_reset = State::S0; +	if (pm.st.muxA) +		acc_reset = pm.st.muxA->getPort("\\S"); +	if (pm.st.muxB) +		acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); + +	cell->setPort("\\OLOADTOP", acc_reset); +	cell->setPort("\\OLOADBOT", acc_reset); + +	// SB_MAC16 Remaining Parameters + +	cell->setParam("\\C_REG", State::S0); +	cell->setParam("\\D_REG", State::S0); + +	cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); +	cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); +	cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0); +	cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); + +	cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); +	cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); +	cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); +	cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); + +	cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); +	cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); +	cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); +	cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); + +	cell->setParam("\\MODE_8x8", State::S0); +	cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); +	cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); + +	pm.autoremove(pm.st.mul); +	pm.autoremove(pm.st.ffY); +	pm.autoremove(pm.st.ffS); +} + +struct Ice40DspPass : public Pass { +	Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    ice40_dsp [options] [selection]\n"); +		log("\n"); +		log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing ICE40_DSP pass (map multipliers).\n"); + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			// if (args[argidx] == "-singleton") { +			// 	singleton_mode = true; +			// 	continue; +			// } +			break; +		} +		extra_args(args, argidx, design); + +		for (auto module : design->selected_modules()) +			ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); +	} +} Ice40DspPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg new file mode 100644 index 000000000..96c62e313 --- /dev/null +++ b/passes/pmgen/ice40_dsp.pmg @@ -0,0 +1,160 @@ +state <SigBit> clock +state <bool> clock_pol clock_vld +state <SigSpec> sigA sigB sigY sigS +state <Cell*> addAB muxAB + +match mul +	select mul->type.in($mul) +	select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 +	select GetSize(mul->getPort(\Y)) > 10 +endmatch + +match ffA +	select ffA->type.in($dff) +	// select nusers(port(ffA, \Q)) == 2 +	index <SigSpec> port(ffA, \Q) === port(mul, \A) +	optional +endmatch + +code sigA clock clock_pol clock_vld +	sigA = port(mul, \A); + +	if (ffA) { +		sigA = port(ffA, \D); + +		clock = port(ffA, \CLK).as_bit(); +		clock_pol = param(ffA, \CLK_POLARITY).as_bool(); +		clock_vld = true; +	} +endcode + +match ffB +	select ffB->type.in($dff) +	// select nusers(port(ffB, \Q)) == 2 +	index <SigSpec> port(ffB, \Q) === port(mul, \B) +	optional +endmatch + +code sigB clock clock_pol clock_vld +	sigB = port(mul, \B); + +	if (ffB) { +		sigB = port(ffB, \D); +		SigBit c = port(ffB, \CLK).as_bit(); +		bool cp = param(ffB, \CLK_POLARITY).as_bool(); + +		if (clock_vld && (c != clock || cp != clock_pol)) +			reject; + +		clock = c; +		clock_pol = cp; +		clock_vld = true; +	} +endcode + +match ffY +	select ffY->type.in($dff) +	select nusers(port(ffY, \D)) == 2 +	index <SigSpec> port(ffY, \D) === port(mul, \Y) +	optional +endmatch + +code sigY clock clock_pol clock_vld +	sigY = port(mul, \Y); + +	if (ffY) { +		sigY = port(ffY, \Q); +		SigBit c = port(ffY, \CLK).as_bit(); +		bool cp = param(ffY, \CLK_POLARITY).as_bool(); + +		if (clock_vld && (c != clock || cp != clock_pol)) +			reject; + +		clock = c; +		clock_pol = cp; +		clock_vld = true; +	} +endcode + +match addA +	select addA->type.in($add) +	select nusers(port(addA, \A)) == 2 +	index <SigSpec> port(addA, \A) === sigY +	optional +endmatch + +match addB +	if !addA +	select addB->type.in($add, $sub) +	select nusers(port(addB, \B)) == 2 +	index <SigSpec> port(addB, \B) === sigY +	optional +endmatch + +code addAB sigS +	if (addA) { +		addAB = addA; +		sigS = port(addA, \B); +	} +	if (addB) { +		addAB = addB; +		sigS = port(addB, \A); +	} +	if (addAB) { +		int natural_mul_width = GetSize(sigA) + GetSize(sigB); +		int actual_mul_width = GetSize(sigY); +		int actual_acc_width = GetSize(sigS); + +		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) +			reject; +		if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool())) +			reject; +	} +endcode + +match muxA +	if addAB +	select muxA->type.in($mux) +	select nusers(port(muxA, \A)) == 2 +	index <SigSpec> port(muxA, \A) === port(addAB, \Y) +	optional +endmatch + +match muxB +	if addAB +	if !muxA +	select muxB->type.in($mux) +	select nusers(port(muxB, \B)) == 2 +	index <SigSpec> port(muxB, \B) === port(addAB, \Y) +	optional +endmatch + +code muxAB +	muxAB = addAB; +	if (muxA) +		muxAB = muxA; +	if (muxB) +		muxAB = muxB; +endcode + +match ffS +	if muxAB +	select ffS->type.in($dff) +	select nusers(port(ffS, \D)) == 2 +	index <SigSpec> port(ffS, \D) === port(muxAB, \Y) +	index <SigSpec> port(ffS, \Q) === sigS +endmatch + +code clock clock_pol clock_vld +	if (ffS) { +		SigBit c = port(ffS, \CLK).as_bit(); +		bool cp = param(ffS, \CLK_POLARITY).as_bool(); + +		if (clock_vld && (c != clock || cp != clock_pol)) +			reject; + +		clock = c; +		clock_pol = cp; +		clock_vld = true; +	} +endcode diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py new file mode 100644 index 000000000..d9747b065 --- /dev/null +++ b/passes/pmgen/pmgen.py @@ -0,0 +1,486 @@ +#!/usr/bin/env python3 + +import re +import sys +import pprint + +pp = pprint.PrettyPrinter(indent=4) + +pmgfile = sys.argv[1] +assert pmgfile.endswith(".pmg") +prefix = pmgfile[0:-4] +prefix = prefix.split('/')[-1] +outfile = sys.argv[2] + +state_types = dict() +udata_types = dict() +blocks = list() +ids = dict() + +def rewrite_cpp(s): +    t = list() +    i = 0 +    while i < len(s): +        if s[i] in ("'", '"') and i + 1 < len(s): +            j = i + 1 +            while j + 1 < len(s) and s[j] != s[i]: +                if s[j] == '\\' and j + 1 < len(s): +                    j += 1 +                j += 1 +            t.append(s[i:j+1]) +            i = j + 1 +            continue + +        if s[i] in ('$', '\\') and i + 1 < len(s): +            j = i + 1 +            while True: +                if j == len(s): +                    j -= 1 +                    break +                if ord('a') <= ord(s[j]) <= ord('z'): +                    j += 1 +                    continue +                if ord('A') <= ord(s[j]) <= ord('Z'): +                    j += 1 +                    continue +                if ord('0') <= ord(s[j]) <= ord('9'): +                    j += 1 +                    continue +                if s[j] == '_': +                    j += 1 +                    continue +                j -= 1 +                break + +            n = s[i:j+1] +            i = j + 1 + +            if n[0] == '$': +                v = "id_d_" + n[1:] +            else: +                v = "id_b_" + n[1:] + +            if v not in ids: +                ids[v] = n +            else: +                assert ids[v] == n + +            t.append(v) +            continue + +        if s[i] == "\t": +            t.append("  ") +        else: +            t.append(s[i]) + +        i += 1 + +    return "".join(t) + +with open(pmgfile, "r") as f: +    while True: +        line = f.readline() +        if line == "": break +        line = line.strip() + +        cmd = line.split() +        if len(cmd) == 0 or cmd[0].startswith("//"): continue +        cmd = cmd[0] + +        if cmd == "state": +            m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) +            assert m +            type_str = m.group(1) +            states_str = m.group(2) +            for s in re.split(r"\s+", states_str): +                assert s not in state_types +                state_types[s] = type_str +            continue + +        if cmd == "udata": +            m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) +            assert m +            type_str = m.group(1) +            udatas_str = m.group(2) +            for s in re.split(r"\s+", udatas_str): +                assert s not in udata_types +                udata_types[s] = type_str +            continue + +        if cmd == "match": +            block = dict() +            block["type"] = "match" + +            line = line.split() +            assert len(line) == 2 +            assert line[1] not in state_types +            block["cell"] = line[1] +            state_types[line[1]] = "Cell*"; + +            block["if"] = list() +            block["select"] = list() +            block["index"] = list() +            block["filter"] = list() +            block["optional"] = False + +            while True: +                l = f.readline() +                assert l != "" +                a = l.split() +                if len(a) == 0 or a[0].startswith("//"): continue +                if a[0] == "endmatch": break + +                if a[0] == "if": +                    b = l.lstrip()[2:] +                    block["if"].append(rewrite_cpp(b.strip())) +                    continue + +                if a[0] == "select": +                    b = l.lstrip()[6:] +                    block["select"].append(rewrite_cpp(b.strip())) +                    continue + +                if a[0] == "index": +                    m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l) +                    assert m +                    block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3)))) +                    continue + +                if a[0] == "filter": +                    b = l.lstrip()[6:] +                    block["filter"].append(rewrite_cpp(b.strip())) +                    continue + +                if a[0] == "optional": +                    block["optional"] = True +                    continue + +                assert False + +            blocks.append(block) + +        if cmd == "code": +            block = dict() +            block["type"] = "code" +            block["code"] = list() +            block["states"] = set() + +            for s in line.split()[1:]: +                assert s in state_types +                block["states"].add(s) + +            while True: +                l = f.readline() +                assert l != "" +                a = l.split() +                if len(a) == 0: continue +                if a[0] == "endcode": break + +                block["code"].append(rewrite_cpp(l.rstrip())) + +            blocks.append(block) + +with open(outfile, "w") as f: +    print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) +    print("", file=f) + +    print("#include \"kernel/yosys.h\"", file=f) +    print("#include \"kernel/sigtools.h\"", file=f) +    print("", file=f) + +    print("YOSYS_NAMESPACE_BEGIN", file=f) +    print("", file=f) + +    print("struct {}_pm {{".format(prefix), file=f) +    print("  Module *module;", file=f) +    print("  SigMap sigmap;", file=f) +    print("  std::function<void()> on_accept;".format(prefix), file=f) +    print("", file=f) + +    for index in range(len(blocks)): +        block = blocks[index] +        if block["type"] == "match": +            index_types = list() +            for entry in block["index"]: +                index_types.append(entry[0]) +            print("  typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f) +            print("  dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f) +    print("  dict<SigBit, pool<Cell*>> sigusers;", file=f) +    print("  pool<Cell*> blacklist_cells;", file=f) +    print("  pool<Cell*> autoremove_cells;", file=f) +    print("  bool blacklist_dirty;", file=f) +    print("  int rollback;", file=f) +    print("", file=f) + +    print("  struct state_t {", file=f) +    for s, t in sorted(state_types.items()): +        print("    {} {};".format(t, s), file=f) +    print("  } st;", file=f) +    print("", file=f) + +    print("  struct udata_t {", file=f) +    for s, t in sorted(udata_types.items()): +        print("    {} {};".format(t, s), file=f) +    print("  } ud;", file=f) +    print("", file=f) + +    for v, n in sorted(ids.items()): +        if n[0] == "\\": +            print("  IdString {}{{\"\\{}\"}};".format(v, n), file=f) +        else: +            print("  IdString {}{{\"{}\"}};".format(v, n), file=f) +    print("", file=f) + +    print("  void add_siguser(const SigSpec &sig, Cell *cell) {", file=f) +    print("    for (auto bit : sigmap(sig)) {", file=f) +    print("      if (bit.wire == nullptr) continue;", file=f) +    print("      if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f) +    print("        sigusers[bit].insert(nullptr);", file=f) +    print("      sigusers[bit].insert(cell);", file=f) +    print("    }", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  void blacklist(Cell *cell) {", file=f) +    print("    if (cell != nullptr) {", file=f) +    print("      if (blacklist_cells.insert(cell).second)", file=f) +    print("        blacklist_dirty = true;", file=f) +    print("    }", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  void autoremove(Cell *cell) {", file=f) +    print("    if (cell != nullptr) {", file=f) +    print("      if (blacklist_cells.insert(cell).second)", file=f) +    print("        blacklist_dirty = true;", file=f) +    print("      autoremove_cells.insert(cell);", file=f) +    print("    }", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  void check_blacklist() {", file=f) +    print("    if (!blacklist_dirty)", file=f) +    print("      return;", file=f) +    print("    blacklist_dirty = false;", file=f) +    for index in range(len(blocks)): +        block = blocks[index] +        if block["type"] == "match": +            print("    if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) +            print("      rollback = {};".format(index+1), file=f) +            print("      return;", file=f) +            print("    }", file=f) +    print("    rollback = 0;", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  SigSpec port(Cell *cell, IdString portname) {", file=f) +    print("    return sigmap(cell->getPort(portname));", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  Const param(Cell *cell, IdString paramname) {", file=f) +    print("    return cell->getParam(paramname);", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  int nusers(const SigSpec &sig) {", file=f) +    print("    pool<Cell*> users;", file=f) +    print("    for (auto bit : sigmap(sig))", file=f) +    print("      for (auto user : sigusers[bit])", file=f) +    print("        users.insert(user);", file=f) +    print("    return GetSize(users);", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) +    print("      module(module), sigmap(module) {", file=f) +    for s, t in sorted(udata_types.items()): +        if t.endswith("*"): +            print("    ud.{} = nullptr;".format(s), file=f) +        else: +            print("    ud.{} = {}();".format(s, t), file=f) +    print("    for (auto cell : module->cells()) {", file=f) +    print("      for (auto &conn : cell->connections())", file=f) +    print("        add_siguser(conn.second, cell);", file=f) +    print("    }", file=f) +    print("    for (auto cell : cells) {", file=f) + +    for index in range(len(blocks)): +        block = blocks[index] +        if block["type"] == "match": +            print("      do {", file=f) +            print("        Cell *{} = cell;".format(block["cell"]), file=f) +            for expr in block["select"]: +                print("        if (!({})) break;".format(expr), file=f) +            print("        index_{}_key_type key;".format(index), file=f) +            for field, entry in enumerate(block["index"]): +                print("        std::get<{}>(key) = {};".format(field, entry[1]), file=f) +            print("        index_{}[key].push_back(cell);".format(index), file=f) +            print("      } while (0);", file=f) + +    print("    }", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  ~{}_pm() {{".format(prefix), file=f) +    print("    for (auto cell : autoremove_cells)", file=f) +    print("      module->remove(cell);", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  void run(std::function<void()> on_accept_f) {", file=f) +    print("    on_accept = on_accept_f;", file=f) +    print("    rollback = 0;", file=f) +    print("    blacklist_dirty = false;", file=f) +    for s, t in sorted(state_types.items()): +        if t.endswith("*"): +            print("    st.{} = nullptr;".format(s), file=f) +        else: +            print("    st.{} = {}();".format(s, t), file=f) +    print("    block_0();", file=f) +    print("  }", file=f) +    print("", file=f) + +    print("  void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f) +    print("    run([&](){on_accept_f(*this);});", file=f) +    print("  }", file=f) +    print("", file=f) + +    for index in range(len(blocks)): +        block = blocks[index] + +        print("  void block_{}() {{".format(index), file=f) + +        const_st = set() +        nonconst_st = set() +        restore_st = set() + +        for i in range(index): +            if blocks[i]["type"] == "code": +                for s in blocks[i]["states"]: +                    const_st.add(s) +            elif blocks[i]["type"] == "match": +                const_st.add(blocks[i]["cell"]) +            else: +                assert False + +        if block["type"] == "code": +            for s in block["states"]: +                if s in const_st: +                    const_st.remove(s) +                    restore_st.add(s) +                nonconst_st.add(s) +        elif block["type"] == "match": +            s = block["cell"] +            assert s not in const_st +            nonconst_st.add(s) +        else: +            assert False + +        for s in sorted(const_st): +            t = state_types[s] +            if t.endswith("*"): +                print("    {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) +            else: +                print("    const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + +        for s in sorted(nonconst_st): +            t = state_types[s] +            print("    {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + +        if len(restore_st): +            print("", file=f) +            for s in sorted(restore_st): +                t = state_types[s] +                print("    {} backup_{} = {};".format(t, s, s), file=f) + +        if block["type"] == "code": +            print("", file=f) +            print("    do {", file=f) +            print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) +            print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) +            print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) + +            for line in block["code"]: +                print("    " + line, file=f) + +            print("", file=f) +            print("      block_{}();".format(index+1), file=f) +            print("#undef reject", file=f) +            print("#undef accept", file=f) +            print("#undef branch", file=f) +            print("    } while (0);", file=f) +            print("", file=f) +            print("rollback_label:", file=f) +            print("    YS_ATTRIBUTE(unused);", file=f) + +            if len(restore_st) or len(nonconst_st): +                print("", file=f) +                for s in sorted(restore_st): +                    t = state_types[s] +                    print("    {} = backup_{};".format(s, s), file=f) +                for s in sorted(nonconst_st): +                    if s not in restore_st: +                        t = state_types[s] +                        if t.endswith("*"): +                            print("    {} = nullptr;".format(s), file=f) +                        else: +                            print("    {} = {}();".format(s, t), file=f) + +        elif block["type"] == "match": +            assert len(restore_st) == 0 + +            if len(block["if"]): +                for expr in block["if"]: +                    print("", file=f) +                    print("    if (!({})) {{".format(expr), file=f) +                    print("      {} = nullptr;".format(block["cell"]), file=f) +                    print("      block_{}();".format(index+1), file=f) +                    print("      return;", file=f) +                    print("    }", file=f) + +            print("", file=f) +            print("    index_{}_key_type key;".format(index), file=f) +            for field, entry in enumerate(block["index"]): +                print("    std::get<{}>(key) = {};".format(field, entry[2]), file=f) +            print("    const vector<Cell*> &cells = index_{}[key];".format(index), file=f) + +            print("", file=f) +            print("    for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) +            print("      {} = cells[idx];".format(block["cell"]), file=f) +            print("      if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) +            for expr in block["filter"]: +                print("      if (!({})) continue;".format(expr), file=f) +            print("      block_{}();".format(index+1), file=f) +            print("      if (rollback) {", file=f) +            print("        if (rollback != {}) {{".format(index+1), file=f) +            print("          {} = nullptr;".format(block["cell"]), file=f) +            print("          return;", file=f) +            print("        }", file=f) +            print("        rollback = 0;", file=f) +            print("      }", file=f) +            print("    }", file=f) + +            print("", file=f) +            print("    {} = nullptr;".format(block["cell"]), file=f) + +            if block["optional"]: +                print("    block_{}();".format(index+1), file=f) + +        else: +            assert False + + +        print("  }", file=f) +        print("", file=f) + +    print("  void block_{}() {{".format(len(blocks)), file=f) +    print("    on_accept();", file=f) +    print("    check_blacklist();", file=f) +    print("  }", file=f) +    print("};", file=f) + +    print("", file=f) +    print("YOSYS_NAMESPACE_END", file=f) + +# pp.pprint(blocks) diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index 8ab0280c0..4eced2ff1 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -8,4 +8,7 @@ OBJS += passes/sat/expose.o  OBJS += passes/sat/assertpmux.o  OBJS += passes/sat/clk2fflogic.o  OBJS += passes/sat/async2sync.o +OBJS += passes/sat/supercover.o +OBJS += passes/sat/fmcombine.o +OBJS += passes/sat/mutate.o diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index c92db7118..d045d0dcb 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -39,7 +39,7 @@ struct Async2syncPass : public Pass {  		log("reset value in the next cycle regardless of the data-in value at the time of\n");  		log("the clock edge.\n");  		log("\n"); -		log("Currently only $adff cells are supported by this pass.\n"); +		log("Currently only $adff and $dffsr cells are supported by this pass.\n");  		log("\n");  	}  	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -84,7 +84,7 @@ struct Async2syncPass : public Pass {  					bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool();  					Const arst_val = cell->parameters["\\ARST_VALUE"]; -					SigSpec sig_clk = cell->getPort("\\CLK"); +					// SigSpec sig_clk = cell->getPort("\\CLK");  					SigSpec sig_arst = cell->getPort("\\ARST");  					SigSpec sig_d = cell->getPort("\\D");  					SigSpec sig_q = cell->getPort("\\Q"); @@ -120,6 +120,55 @@ struct Async2syncPass : public Pass {  					cell->type = "$dff";  					continue;  				} + +				if (cell->type.in("$dffsr")) +				{ +					// bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool(); +					bool set_pol = cell->parameters["\\SET_POLARITY"].as_bool(); +					bool clr_pol = cell->parameters["\\CLR_POLARITY"].as_bool(); + +					// SigSpec sig_clk = cell->getPort("\\CLK"); +					SigSpec sig_set = cell->getPort("\\SET"); +					SigSpec sig_clr = cell->getPort("\\CLR"); +					SigSpec sig_d = cell->getPort("\\D"); +					SigSpec sig_q = cell->getPort("\\Q"); + +					log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n", +							log_id(module), log_id(cell), log_id(cell->type), +							log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q)); + +					Const init_val; +					for (int i = 0; i < GetSize(sig_q); i++) { +						SigBit bit = sigmap(sig_q[i]); +						init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx); +						del_initbits.insert(bit); +					} + +					Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d)); +					Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q)); +					new_q->attributes["\\init"] = init_val; + +					if (!set_pol) +						sig_set = module->Not(NEW_ID, sig_set); + +					if (clr_pol) +						sig_clr = module->Not(NEW_ID, sig_clr); + +					SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set); +					module->addAnd(NEW_ID, tmp, sig_clr, new_d); + +					tmp = module->Or(NEW_ID, new_q, sig_set); +					module->addAnd(NEW_ID, tmp, sig_clr, sig_q); + +					cell->setPort("\\D", new_d); +					cell->setPort("\\Q", new_q); +					cell->unsetPort("\\SET"); +					cell->unsetPort("\\CLR"); +					cell->unsetParam("\\SET_POLARITY"); +					cell->unsetParam("\\CLR_POLARITY"); +					cell->type = "$dff"; +					continue; +				}  			}  			for (auto wire : module->wires()) diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc new file mode 100644 index 000000000..cd75ca860 --- /dev/null +++ b/passes/sat/fmcombine.cc @@ -0,0 +1,341 @@ +/* + *  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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct opts_t +{ +	bool fwd = false; +	bool bwd = false; +	bool nop = false; +}; + +struct FmcombineWorker +{ +	const opts_t &opts; +	Design *design; +	Module *original = nullptr; +	Module *module = nullptr; +	IdString orig_type, combined_type; + +	FmcombineWorker(Design *design, IdString orig_type, const opts_t &opts) : +			opts(opts), design(design), original(design->module(orig_type)), +			orig_type(orig_type), combined_type("$fmcombine" + orig_type.str()) +	{ +	} + +	SigSpec import_sig(SigSpec sig, const string &suffix) +	{ +		SigSpec newsig; +		for (auto chunk : sig.chunks()) { +			if (chunk.wire != nullptr) +				chunk.wire = module->wire(chunk.wire->name.str() + suffix); +			newsig.append(chunk); +		} +		return newsig; +	} + +	void import_prim_cell(Cell *cell, const string &suffix) +	{ +		Cell *c = module->addCell(cell->name.str() + suffix, cell->type); +		c->parameters = cell->parameters; +		c->attributes = cell->attributes; + +		for (auto &conn : cell->connections()) +			c->setPort(conn.first, import_sig(conn.second, suffix)); +	} + +	void import_hier_cell(Cell *cell) +	{ +		if (!cell->parameters.empty()) +			log_cmd_error("Cell %s.%s has unresolved instance parameters.\n", log_id(original), log_id(cell)); + +		FmcombineWorker sub_worker(design, cell->type, opts); +		sub_worker.generate(); + +		Cell *c = module->addCell(cell->name.str() + "_combined", sub_worker.combined_type); +		// c->parameters = cell->parameters; +		c->attributes = cell->attributes; + +		for (auto &conn : cell->connections()) { +			c->setPort(conn.first.str() + "_gold", import_sig(conn.second, "_gold")); +			c->setPort(conn.first.str() + "_gate", import_sig(conn.second, "_gate")); +		} +	} + +	void generate() +	{ +		if (design->module(combined_type)) { +			// log("Combined module %s already exists.\n", log_id(combined_type)); +			return; +		} + +		log("Generating combined module %s from module %s.\n", log_id(combined_type), log_id(orig_type)); +		module = design->addModule(combined_type); + +		for (auto wire : original->wires()) { +			module->addWire(wire->name.str() + "_gold", wire); +			module->addWire(wire->name.str() + "_gate", wire); +		} +		module->fixup_ports(); + +		for (auto cell : original->cells()) { +			if (design->module(cell->type) == nullptr) { +				import_prim_cell(cell, "_gold"); +				import_prim_cell(cell, "_gate"); +			} else { +				import_hier_cell(cell); +			} +		} + +		for (auto &conn : original->connections()) { +			module->connect(import_sig(conn.first, "_gold"), import_sig(conn.second, "_gold")); +			module->connect(import_sig(conn.first, "_gate"), import_sig(conn.second, "_gate")); +		} + +		if (opts.nop) +			return; + +		CellTypes ct; +		ct.setup_internals_eval(); +		ct.setup_stdcells_eval(); + +		SigMap sigmap(module); + +		dict<SigBit, SigBit> data_bit_to_eq_net; +		dict<Cell*, SigSpec> cell_to_eq_nets; +		dict<SigSpec, SigSpec> reduce_db; +		dict<SigSpec, SigSpec> invert_db; + +		for (auto cell : original->cells()) +		{ +			if (!ct.cell_known(cell->type)) +				continue; + +			for (auto &conn : cell->connections()) +			{ +				if (!cell->output(conn.first)) +					continue; + +				SigSpec A = import_sig(conn.second, "_gold"); +				SigSpec B = import_sig(conn.second, "_gate"); +				SigBit EQ = module->Eq(NEW_ID, A, B); + +				for (auto bit : sigmap({A, B})) +					data_bit_to_eq_net[bit] = EQ; + +				cell_to_eq_nets[cell].append(EQ); +			} +		} + +		for (auto cell : original->cells()) +		{ +			if (!ct.cell_known(cell->type)) +				continue; + +			bool skip_cell = !cell_to_eq_nets.count(cell); +			pool<SigBit> src_eq_bits; + +			for (auto &conn : cell->connections()) +			{ +				if (skip_cell) +					break; + +				if (cell->output(conn.first)) +					continue; + +				SigSpec A = import_sig(conn.second, "_gold"); +				SigSpec B = import_sig(conn.second, "_gate"); + +				for (auto bit : sigmap({A, B})) { +					if (data_bit_to_eq_net.count(bit)) +						src_eq_bits.insert(data_bit_to_eq_net.at(bit)); +					else +						skip_cell = true; +				} +			} + +			if (!skip_cell) { +				SigSpec antecedent = SigSpec(src_eq_bits); +				antecedent.sort_and_unify(); + +				if (GetSize(antecedent) > 1) { +					if (reduce_db.count(antecedent) == 0) +						reduce_db[antecedent] = module->ReduceAnd(NEW_ID, antecedent); +					antecedent = reduce_db.at(antecedent); +				} + +				SigSpec consequent = cell_to_eq_nets.at(cell); +				consequent.sort_and_unify(); + +				if (GetSize(consequent) > 1) { +					if (reduce_db.count(consequent) == 0) +						reduce_db[consequent] = module->ReduceAnd(NEW_ID, consequent); +					consequent = reduce_db.at(consequent); +				} + +				if (opts.fwd) +					module->addAssume(NEW_ID, consequent, antecedent); + +				if (opts.bwd) +				{ +					if (invert_db.count(antecedent) == 0) +						invert_db[antecedent] = module->Not(NEW_ID, antecedent); + +					if (invert_db.count(consequent) == 0) +						invert_db[consequent] = module->Not(NEW_ID, consequent); + +					module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent)); +				} +			} +		} +	} +}; + +struct FmcombinePass : public Pass { +	FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    fmcombine [options] module_name gold_cell gate_cell\n"); +		// log("    fmcombine [options] @gold_cell @gate_cell\n"); +		log("\n"); +		log("This pass takes two cells, which are instances of the same module, and replaces\n"); +		log("them with one instance of a special 'combined' module, that effectively\n"); +		log("contains two copies of the original module, plus some formal properties.\n"); +		log("\n"); +		log("This is useful for formal test benches that check what differences in behavior\n"); +		log("a slight difference in input causes in a module.\n"); +		log("\n"); +		log("    -fwd\n"); +		log("        Insert forward hint assumptions into the combined module.\n"); +		log("\n"); +		log("    -bwd\n"); +		log("        Insert backward hint assumptions into the combined module.\n"); +		log("        (Backward hints are logically equivalend to fordward hits, but\n"); +		log("        some solvers are faster with bwd hints, or even both -bwd and -fwd.)\n"); +		log("\n"); +		log("    -nop\n"); +		log("        Don't insert hint assumptions into the combined module.\n"); +		log("        (This should not provide any speedup over the original design, but\n"); +		log("        strangely sometimes it does.)\n"); +		log("\n"); +		log("If none of -fwd, -bwd, and -nop is given, then -fwd is used as default.\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		opts_t opts; +		Module *module = nullptr; +		Cell *gold_cell = nullptr; +		Cell *gate_cell = nullptr; + +		log_header(design, "Executing FMCOMBINE pass.\n"); + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			// if (args[argidx] == "-o" && argidx+1 < args.size()) { +			// 	filename = args[++argidx]; +			// 	continue; +			// } +			if (args[argidx] == "-fwd") { +				opts.fwd = true; +				continue; +			} +			if (args[argidx] == "-bwd") { +				opts.bwd = true; +				continue; +			} +			if (args[argidx] == "-nop") { +				opts.nop = true; +				continue; +			} +			break; +		} +		if (argidx+2 == args.size()) +		{ +			string gold_name = args[argidx++]; +			string gate_name = args[argidx++]; +			log_cmd_error("fmcombine @gold_cell @gate_cell call style is not implemented yet."); +		} +		else if (argidx+3 == args.size()) +		{ +			IdString module_name = RTLIL::escape_id(args[argidx++]); +			IdString gold_name = RTLIL::escape_id(args[argidx++]); +			IdString gate_name = RTLIL::escape_id(args[argidx++]); + +			module = design->module(module_name); +			if (module == nullptr) +				log_cmd_error("Module %s not found.\n", log_id(module_name)); + +			gold_cell = module->cell(gold_name); +			if (gold_cell == nullptr) +				log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gold_name), log_id(module)); + +			gate_cell = module->cell(gate_name); +			if (gate_cell == nullptr) +				log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module)); +		} +		else +		{ +			log_cmd_error("Invalid number of arguments.\n"); +		} +		// extra_args(args, argidx, design); + +		if (opts.nop && (opts.fwd || opts.bwd)) +			log_cmd_error("Option -nop can not be combined with -fwd and/or -bwd.\n"); + +		if (!opts.nop && !opts.fwd && !opts.bwd) +			opts.fwd = true; + +		if (gold_cell->type != gate_cell->type) +			log_cmd_error("Types of gold and gate cells do not match.\n"); +		if (!gold_cell->parameters.empty()) +			log_cmd_error("Gold cell has unresolved instance parameters.\n"); +		if (!gate_cell->parameters.empty()) +			log_cmd_error("Gold cell has unresolved instance parameters.\n"); + +		FmcombineWorker worker(design, gold_cell->type, opts); +		worker.generate(); +		IdString combined_cell_name = module->uniquify(stringf("\\%s_%s", log_id(gold_cell), log_id(gate_cell))); + +		Cell *cell = module->addCell(combined_cell_name, worker.combined_type); +		cell->attributes = gold_cell->attributes; +		cell->add_strpool_attribute("\\src", gate_cell->get_strpool_attribute("\\src")); + +		log("Combining cells %s and %s in module %s into new cell %s.\n", log_id(gold_cell), log_id(gate_cell), log_id(module), log_id(cell)); + +		for (auto &conn : gold_cell->connections()) +			cell->setPort(conn.first.str() + "_gold", conn.second); +		module->remove(gold_cell); + +		for (auto &conn : gate_cell->connections()) +			cell->setPort(conn.first.str() + "_gate", conn.second); +		module->remove(gate_cell); +	} +} FmcombinePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc new file mode 100644 index 000000000..9621d2855 --- /dev/null +++ b/passes/sat/mutate.cc @@ -0,0 +1,905 @@ +/* + *  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 mutate_t { +	string mode; +	pool<string> src; +	IdString module, cell; +	IdString port, wire; +	int portbit = -1; +	int ctrlbit = -1; +	int wirebit = -1; +	bool used = false; +}; + +struct mutate_opts_t { +	int seed = 0; +	std::string mode; +	pool<string> src; +	IdString module, cell, port, wire; +	int portbit = -1; +	int ctrlbit = -1; +	int wirebit = -1; + +	IdString ctrl_name; +	int ctrl_width = -1, ctrl_value = -1; + +	int pick_cover_prcnt = 80; + +	int weight_cover = 500; + +	int weight_pq_w = 100; +	int weight_pq_b = 100; +	int weight_pq_c = 100; +	int weight_pq_s = 100; + +	int weight_pq_mw = 100; +	int weight_pq_mb = 100; +	int weight_pq_mc = 100; +	int weight_pq_ms = 100; +}; + +void database_add(std::vector<mutate_t> &database, const mutate_opts_t &opts, const mutate_t &entry) +{ +	if (!opts.mode.empty() && opts.mode != entry.mode) +		return; + +	if (!opts.src.empty()) { +		bool found_match = false; +		for (auto &s : opts.src) { +			if (entry.src.count(s)) +				found_match = true; +		} +		if (!found_match) +			return; +	} + +	if (!opts.module.empty() && opts.module != entry.module) +		return; + +	if (!opts.cell.empty() && opts.cell != entry.cell) +		return; + +	if (!opts.port.empty() && opts.port != entry.port) +		return; + +	if (opts.portbit >= 0 && opts.portbit != entry.portbit) +		return; + +	if (opts.ctrlbit >= 0 && opts.ctrlbit != entry.ctrlbit) +		return; + +	if (!opts.wire.empty() && opts.wire != entry.wire) +		return; + +	if (opts.wirebit >= 0 && opts.wirebit != entry.wirebit) +		return; + +	database.push_back(entry); +} + +struct xs128_t +{ +	uint32_t x = 123456789; +	uint32_t y = 0, z = 0, w = 0; + +	xs128_t(int seed = 0) : w(seed) { +		next(); +		next(); +		next(); +	} + +	void next() { +		uint32_t t = x ^ (x << 11); +		x = y, y = z, z = w; +		w ^= (w >> 19) ^ t ^ (t >> 8); +	} + +	int operator()() { +		next(); +		return w & 0x3fffffff; +	} + +	int operator()(int n) { +		if (n < 2) +			return 0; +		while (1) { +			int k = (*this)(), p = k % n; +			if ((k - p + n) <= 0x40000000) +				return p; +		} +	} +}; + +struct coverdb_t +{ +	dict<string, int> src_db; +	dict<tuple<IdString, IdString>, int> wire_db; +	dict<tuple<IdString, IdString, int>, int> wirebit_db; + +	void insert(const mutate_t &m) { +		if (!m.wire.empty()) { +			wire_db[tuple<IdString, IdString>(m.module, m.wire)] = 0; +			wirebit_db[tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)] = 0; +		} +		for (auto &s : m.src) { +			src_db[s] = 0; +		} +	} + +	void update(const mutate_t &m) { +		if (!m.wire.empty()) { +			wire_db.at(tuple<IdString, IdString>(m.module, m.wire))++; +			wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit))++; +		} +		for (auto &s : m.src) { +			src_db.at(s)++; +		} +	} + +	int score(const mutate_t &m) { +		int this_score = m.src.empty() ? 0 : 1; +		if (!m.wire.empty()) { +			this_score += wire_db.at(tuple<IdString, IdString>(m.module, m.wire)) ? 0 : 5; +			this_score += wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)) ? 0 : 1; +		} +		for (auto &s : m.src) { +			this_score += src_db.at(s) ? 0 : 5; +		} +		return this_score; +	} +}; + +struct mutate_queue_t +{ +	pool<mutate_t*, hash_ptr_ops> db; + +	mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) { +		mutate_t *m = nullptr; +		if (rng(100) < opts.pick_cover_prcnt) { +			vector<mutate_t*> candidates, rmqueue; +			int best_score = -1; +			for (auto p : db) { +				if (p->used) { +					rmqueue.push_back(p); +					continue; +				} +				int this_score = coverdb.score(*p); +				if (this_score > best_score) { +					best_score = this_score; +					candidates.clear(); +				} +				if (best_score == this_score) +					candidates.push_back(p); +			} +			for (auto p : rmqueue) +				db.erase(p); +			if (!candidates.empty()) +				m = candidates[rng(GetSize(candidates))]; +		} +		if (m == nullptr) { +			while (!db.empty()) { +				int i = rng(GetSize(db)); +				auto it = db.element(i); +				mutate_t *p = *it; +				db.erase(it); +				if (p->used == false) { +					m = p; +					break; +				} +			} +		} +		return m; +	} + +	void add(mutate_t *m) { +		db.insert(m); +	} +}; + +template <typename K, typename T> +struct mutate_chain_queue_t +{ +	dict<K, T> db; + +	mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) { +		while (!db.empty()) { +			int i = rng(GetSize(db)); +			auto it = db.element(i); +			mutate_t *m = it->second.pick(rng, coverdb, opts); +			if (m != nullptr) +				return m; +			db.erase(it); +		} +		return nullptr; +	} + +	template<typename... Args> +	void add(mutate_t *m, K key, Args... args) { +		db[key].add(m, args...); +	} +}; + +template <typename K, typename T> +struct mutate_once_queue_t +{ +	dict<K, T> db; + +	mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) { +		while (!db.empty()) { +			int i = rng(GetSize(db)); +			auto it = db.element(i); +			mutate_t *m = it->second.pick(rng, coverdb, opts); +			db.erase(it); +			if (m != nullptr) +				return m; +		} +		return nullptr; +	} + +	template<typename... Args> +	void add(mutate_t *m, K key, Args... args) { +		db[key].add(m, args...); +	} +}; + +void database_reduce(std::vector<mutate_t> &database, const mutate_opts_t &opts, int N, xs128_t &rng) +{ +	std::vector<mutate_t> new_database; +	coverdb_t coverdb; + +	int total_weight = opts.weight_cover + opts.weight_pq_w + opts.weight_pq_b + opts.weight_pq_c + opts.weight_pq_s; +	total_weight += opts.weight_pq_mw + opts.weight_pq_mb + opts.weight_pq_mc + opts.weight_pq_ms; + +	if (N >= GetSize(database)) +		return; + +	mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_wire; +	mutate_once_queue_t<tuple<IdString, IdString, int>, mutate_queue_t> primary_queue_bit; +	mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_cell; +	mutate_once_queue_t<string, mutate_queue_t> primary_queue_src; + +	mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_wire; +	mutate_chain_queue_t<IdString, mutate_once_queue_t<pair<IdString, int>, mutate_queue_t>> primary_queue_module_bit; +	mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_cell; +	mutate_chain_queue_t<IdString, mutate_once_queue_t<string, mutate_queue_t>> primary_queue_module_src; + +	for (auto &m : database) +	{ +		coverdb.insert(m); + +		if (!m.wire.empty()) { +			primary_queue_wire.add(&m, tuple<IdString, IdString>(m.module, m.wire)); +			primary_queue_bit.add(&m, tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)); +			primary_queue_module_wire.add(&m, m.module, m.wire); +			primary_queue_module_bit.add(&m, m.module, pair<IdString, int>(m.wire, m.wirebit)); +		} + +		primary_queue_cell.add(&m, tuple<IdString, IdString>(m.module, m.cell)); +		primary_queue_module_cell.add(&m, m.module, m.cell); + +		for (auto &s : m.src) { +			primary_queue_src.add(&m, s); +			primary_queue_module_src.add(&m, m.module, s); +		} +	} + +	vector<mutate_t*> cover_candidates; +	int best_cover_score = -1; +	bool skip_cover = false; + +	while (GetSize(new_database) < N) +	{ +		int k = rng(total_weight); + +		k -= opts.weight_cover; +		if (k < 0) { +			while (!skip_cover) { +				if (cover_candidates.empty()) { +					best_cover_score = -1; +					for (auto &m : database) { +						if (m.used || m.src.empty()) +							continue; +						int this_score = -1; +						for (auto &s : m.src) { +							if (this_score == -1 || this_score > coverdb.src_db.at(s)) +								this_score = coverdb.src_db.at(s); +						} +						log_assert(this_score != -1); +						if (best_cover_score == -1 || this_score < best_cover_score) { +							cover_candidates.clear(); +							best_cover_score = this_score; +						} +						if (best_cover_score == this_score) +							cover_candidates.push_back(&m); +					} +					if (best_cover_score == -1) { +						skip_cover = true; +						break; +					} +				} + +				mutate_t *m = nullptr; +				while (!cover_candidates.empty()) +				{ +					int idx = rng(GetSize(cover_candidates)); +					mutate_t *p = cover_candidates[idx]; +					cover_candidates[idx] = cover_candidates.back(); +					cover_candidates.pop_back(); + +					if (p->used) +						continue; + +					int this_score = -1; +					for (auto &s : p->src) { +						if (this_score == -1 || this_score > coverdb.src_db.at(s)) +							this_score = coverdb.src_db.at(s); +					} + +					if (this_score != best_cover_score) +						continue; + +					m = p; +					break; +				} + +				if (m != nullptr) { +					m->used = true; +					coverdb.update(*m); +					new_database.push_back(*m); +					break; +				} +			} +			continue; +		} + +#define X(__wght, __queue)                               \ +    k -= __wght;                                         \ +    if (k < 0) {                                         \ +      mutate_t *m = __queue.pick(rng, coverdb, opts);    \ +      if (m != nullptr) {                                \ +        m->used = true;                                  \ +        coverdb.update(*m);                              \ +        new_database.push_back(*m);                      \ +      };                                                 \ +      continue;                                          \ +    } + +		X(opts.weight_pq_w, primary_queue_wire) +		X(opts.weight_pq_b, primary_queue_bit) +		X(opts.weight_pq_c, primary_queue_cell) +		X(opts.weight_pq_s, primary_queue_src) + +		X(opts.weight_pq_mw, primary_queue_module_wire) +		X(opts.weight_pq_mb, primary_queue_module_bit) +		X(opts.weight_pq_mc, primary_queue_module_cell) +		X(opts.weight_pq_ms, primary_queue_module_src) +#undef X +	} + +	std::swap(new_database, database); + +	int covered_src_cnt = 0; +	int covered_wire_cnt = 0; +	int covered_wirebit_cnt = 0; + +	for (auto &it : coverdb.src_db) +		if (it.second) +			covered_src_cnt++; + +	for (auto &it : coverdb.wire_db) +		if (it.second) +			covered_wire_cnt++; + +	for (auto &it : coverdb.wirebit_db) +		if (it.second) +			covered_wirebit_cnt++; + +	log("Covered %d/%d src attributes (%.2f%%).\n", covered_src_cnt, GetSize(coverdb.src_db), 100.0 * covered_src_cnt / GetSize(coverdb.src_db)); +	log("Covered %d/%d wires (%.2f%%).\n", covered_wire_cnt, GetSize(coverdb.wire_db), 100.0 * covered_wire_cnt / GetSize(coverdb.wire_db)); +	log("Covered %d/%d wire bits (%.2f%%).\n", covered_wirebit_cnt, GetSize(coverdb.wirebit_db), 100.0 * covered_wirebit_cnt / GetSize(coverdb.wirebit_db)); +} + +void mutate_list(Design *design, const mutate_opts_t &opts, const string &filename, int N) +{ +	std::vector<mutate_t> database; +	xs128_t rng(opts.seed); + +	for (auto module : design->selected_modules()) +	{ +		if (!opts.module.empty() && module->name != opts.module) +			continue; + +		SigMap sigmap(module); +		dict<SigBit, int> bit_user_cnt; + +		for (auto wire : module->wires()) { +			if (wire->name[0] == '\\' && wire->attributes.count("\\src")) +				sigmap.add(wire); +		} + +		for (auto cell : module->cells()) { +			for (auto &conn : cell->connections()) { +				if (cell->output(conn.first)) +					continue; +				for (auto bit : sigmap(conn.second)) +					bit_user_cnt[bit]++; +			} +		} + +		for (auto wire : module->selected_wires()) +		{ +			for (SigBit bit : SigSpec(wire)) +			{ +				SigBit sigbit = sigmap(bit); + +				if (bit.wire == nullptr || sigbit.wire == nullptr) +					continue; + +				if (!bit.wire->port_id != !sigbit.wire->port_id) { +					if (bit.wire->port_id) +						sigmap.add(bit); +					continue; +				} + +				if (!bit.wire->name[0] != !sigbit.wire->name[0]) { +					if (bit.wire->name[0] == '\\') +						sigmap.add(bit); +					continue; +				} +			} +		} + +		for (auto cell : module->selected_cells()) +		{ +			if (!opts.cell.empty() && cell->name != opts.cell) +				continue; + +			for (auto &conn : cell->connections()) +			{ +				for (int i = 0; i < GetSize(conn.second); i++) { +					mutate_t entry; +					entry.module = module->name; +					entry.cell = cell->name; +					entry.port = conn.first; +					entry.portbit = i; + +					for (auto &s : cell->get_strpool_attribute("\\src")) +						entry.src.insert(s); + +					SigBit bit = sigmap(conn.second[i]); +					if (bit.wire && bit.wire->name[0] == '\\' && (cell->output(conn.first) || bit_user_cnt[bit] == 1)) { +						for (auto &s : bit.wire->get_strpool_attribute("\\src")) +							entry.src.insert(s); +						entry.wire = bit.wire->name; +						entry.wirebit = bit.offset; +					} + +					entry.mode = "inv"; +					database_add(database, opts, entry); + +					entry.mode = "const0"; +					database_add(database, opts, entry); + +					entry.mode = "const1"; +					database_add(database, opts, entry); + +					entry.mode = "cnot0"; +					entry.ctrlbit = rng(GetSize(conn.second)); +					if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire) +						database_add(database, opts, entry); + +					entry.mode = "cnot1"; +					entry.ctrlbit = rng(GetSize(conn.second)); +					if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire) +						database_add(database, opts, entry); +				} +			} +		} +	} + +	log("Raw database size: %d\n", GetSize(database)); +	if (N != 0) { +		database_reduce(database, opts, N, rng); +		log("Reduced database size: %d\n", GetSize(database)); +	} + +	std::ofstream fout; + +	if (!filename.empty()) { +		fout.open(filename, std::ios::out | std::ios::trunc); +		if (!fout.is_open()) +			log_error("Could not open file \"%s\" with write access.\n", filename.c_str()); +	} + +	int ctrl_value = opts.ctrl_value; + +	for (auto &entry : database) { +		string str = "mutate"; +		if (!opts.ctrl_name.empty()) +			str += stringf(" -ctrl %s %d %d", log_id(opts.ctrl_name), opts.ctrl_width, ctrl_value++); +		str += stringf(" -mode %s", entry.mode.c_str()); +		if (!entry.module.empty()) +			str += stringf(" -module %s", log_id(entry.module)); +		if (!entry.cell.empty()) +			str += stringf(" -cell %s", log_id(entry.cell)); +		if (!entry.port.empty()) +			str += stringf(" -port %s", log_id(entry.port)); +		if (entry.portbit >= 0) +			str += stringf(" -portbit %d", entry.portbit); +		if (entry.ctrlbit >= 0) +			str += stringf(" -ctrlbit %d", entry.ctrlbit); +		if (!entry.wire.empty()) +			str += stringf(" -wire %s", log_id(entry.wire)); +		if (entry.wirebit >= 0) +			str += stringf(" -wirebit %d", entry.wirebit); +		for (auto &s : entry.src) +			str += stringf(" -src %s", s.c_str()); +		if (filename.empty()) +			log("%s\n", str.c_str()); +		else +			fout << str << std::endl; +	} +} + +SigSpec mutate_ctrl_sig(Module *module, IdString name, int width) +{ +	Wire *ctrl_wire = module->wire(name); + +	if (ctrl_wire == nullptr) +	{ +		log("Adding ctrl port %s to module %s.\n", log_id(name), log_id(module)); + +		ctrl_wire = module->addWire(name, width); +		ctrl_wire->port_input = true; +		module->fixup_ports(); + +		for (auto mod : module->design->modules()) +		for (auto cell : mod->cells()) +		{ +			if (cell->type != module->name) +				continue; + +			SigSpec ctrl = mutate_ctrl_sig(mod, name, width); + +			log("Connecting ctrl port to cell %s in module %s.\n", log_id(cell), log_id(mod)); +			cell->setPort(name, ctrl); +		} +	} + +	log_assert(GetSize(ctrl_wire) == width); +	return ctrl_wire; +} + +SigBit mutate_ctrl(Module *module, const mutate_opts_t &opts) +{ +	if (opts.ctrl_name.empty()) +		return State::S1; + +	SigSpec sig = mutate_ctrl_sig(module, opts.ctrl_name, opts.ctrl_width); +	return module->Eq(NEW_ID, sig, Const(opts.ctrl_value, GetSize(sig))); +} + +SigSpec mutate_ctrl_mux(Module *module, const mutate_opts_t &opts, SigSpec unchanged_sig, SigSpec changed_sig) +{ +	SigBit ctrl_bit = mutate_ctrl(module, opts); +	if (ctrl_bit == State::S0) +		return unchanged_sig; +	if (ctrl_bit == State::S1) +		return changed_sig; +	return module->Mux(NEW_ID, unchanged_sig, changed_sig, ctrl_bit); +} + +void mutate_inv(Design *design, const mutate_opts_t &opts) +{ +	Module *module = design->module(opts.module); +	Cell *cell = module->cell(opts.cell); + +	SigBit bit = cell->getPort(opts.port)[opts.portbit]; +	SigBit inbit, outbit; + +	if (cell->input(opts.port)) +	{ +		log("Add input inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit); +		SigBit outbit = module->Not(NEW_ID, bit); +		bit = mutate_ctrl_mux(module, opts, bit, outbit); +	} +	else +	{ +		log("Add output inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit); +		SigBit inbit = module->addWire(NEW_ID); +		SigBit outbit = module->Not(NEW_ID, inbit); +		module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit)); +		bit = inbit; +	} + +	SigSpec s = cell->getPort(opts.port); +	s[opts.portbit] = bit; +	cell->setPort(opts.port, s); +} + +void mutate_const(Design *design, const mutate_opts_t &opts, bool one) +{ +	Module *module = design->module(opts.module); +	Cell *cell = module->cell(opts.cell); + +	SigBit bit = cell->getPort(opts.port)[opts.portbit]; +	SigBit inbit, outbit; + +	if (cell->input(opts.port)) +	{ +		log("Add input constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit); +		SigBit outbit = one ? State::S1 : State::S0; +		bit = mutate_ctrl_mux(module, opts, bit, outbit); +	} +	else +	{ +		log("Add output constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit); +		SigBit inbit = module->addWire(NEW_ID); +		SigBit outbit = one ? State::S1 : State::S0; +		module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit)); +		bit = inbit; +	} + +	SigSpec s = cell->getPort(opts.port); +	s[opts.portbit] = bit; +	cell->setPort(opts.port, s); +} + +void mutate_cnot(Design *design, const mutate_opts_t &opts, bool one) +{ +	Module *module = design->module(opts.module); +	Cell *cell = module->cell(opts.cell); + +	SigBit bit = cell->getPort(opts.port)[opts.portbit]; +	SigBit ctrl = cell->getPort(opts.port)[opts.ctrlbit]; +	SigBit inbit, outbit; + +	if (cell->input(opts.port)) +	{ +		log("Add input cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit); +		SigBit outbit = one ? module->Xor(NEW_ID, bit, ctrl) : module->Xnor(NEW_ID, bit, ctrl); +		bit = mutate_ctrl_mux(module, opts, bit, outbit); +	} +	else +	{ +		log("Add output cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit); +		SigBit inbit = module->addWire(NEW_ID); +		SigBit outbit = one ? module->Xor(NEW_ID, inbit, ctrl) : module->Xnor(NEW_ID, inbit, ctrl); +		module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit)); +		bit = inbit; +	} + +	SigSpec s = cell->getPort(opts.port); +	s[opts.portbit] = bit; +	cell->setPort(opts.port, s); +} + +struct MutatePass : public Pass { +	MutatePass() : Pass("mutate", "generate or apply design mutations") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    mutate -list N [options] [selection]\n"); +		log("\n"); +		log("Create a list of N mutations using an even sampling.\n"); +		log("\n"); +		log("    -o filename\n"); +		log("        Write list to this file instead of console output\n"); +		log("\n"); +		log("    -seed N\n"); +		log("        RNG seed for selecting mutations\n"); +		log("\n"); +		log("    -ctrl name width value\n"); +		log("        Add -ctrl options to the output. Use 'value' for first mutation, then\n"); +		log("        simply count up from there.\n"); +		log("\n"); +		log("    -mode name\n"); +		log("    -module name\n"); +		log("    -cell name\n"); +		log("    -port name\n"); +		log("    -portbit int\n"); +		log("    -ctrlbit int\n"); +		log("    -wire name\n"); +		log("    -wirebit int\n"); +		log("    -src string\n"); +		log("        Filter list of mutation candidates to those matching\n"); +		log("        the given parameters.\n"); +		log("\n"); +		log("    -cfg option int\n"); +		log("        Set a configuration option. Options available:\n"); +		log("          weight_pq_w weight_pq_b weight_pq_c weight_pq_s\n"); +		log("          weight_pq_mw weight_pq_mb weight_pq_mc weight_pq_ms\n"); +		log("          weight_cover pick_cover_prcnt\n"); +		log("\n"); +		log("\n"); +		log("    mutate -mode MODE [options]\n"); +		log("\n"); +		log("Apply the given mutation.\n"); +		log("\n"); +		log("    -ctrl name width value\n"); +		log("        Add a control signal with the given name and width. The mutation is\n"); +		log("        activated if the control signal equals the given value.\n"); +		log("\n"); +		log("    -module name\n"); +		log("    -cell name\n"); +		log("    -port name\n"); +		log("    -portbit int\n"); +		log("    -ctrlbit int\n"); +		log("        Mutation parameters, as generated by 'mutate -list N'.\n"); +		log("\n"); +		log("    -wire name\n"); +		log("    -wirebit int\n"); +		log("    -src string\n"); +		log("        Ignored. (They are generated by -list for documentation purposes.)\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		mutate_opts_t opts; +		string filename; +		int N = -1; + +		log_header(design, "Executing MUTATE pass.\n"); + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			if (args[argidx] == "-list" && argidx+1 < args.size()) { +				N = atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-o" && argidx+1 < args.size()) { +				filename = args[++argidx]; +				continue; +			} +			if (args[argidx] == "-seed" && argidx+1 < args.size()) { +				opts.seed = atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-mode" && argidx+1 < args.size()) { +				opts.mode = args[++argidx]; +				continue; +			} +			if (args[argidx] == "-ctrl" && argidx+3 < args.size()) { +				opts.ctrl_name = RTLIL::escape_id(args[++argidx]); +				opts.ctrl_width = atoi(args[++argidx].c_str()); +				opts.ctrl_value = atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-module" && argidx+1 < args.size()) { +				opts.module = RTLIL::escape_id(args[++argidx]); +				continue; +			} +			if (args[argidx] == "-cell" && argidx+1 < args.size()) { +				opts.cell = RTLIL::escape_id(args[++argidx]); +				continue; +			} +			if (args[argidx] == "-port" && argidx+1 < args.size()) { +				opts.port = RTLIL::escape_id(args[++argidx]); +				continue; +			} +			if (args[argidx] == "-portbit" && argidx+1 < args.size()) { +				opts.portbit = atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-ctrlbit" && argidx+1 < args.size()) { +				opts.ctrlbit = atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-wire" && argidx+1 < args.size()) { +				opts.wire = RTLIL::escape_id(args[++argidx]); +				continue; +			} +			if (args[argidx] == "-wirebit" && argidx+1 < args.size()) { +				opts.wirebit = atoi(args[++argidx].c_str()); +				continue; +			} +			if (args[argidx] == "-src" && argidx+1 < args.size()) { +				opts.src.insert(args[++argidx]); +				continue; +			} +			if (args[argidx] == "-cfg" && argidx+2 < args.size()) { +				if (args[argidx+1] == "pick_cover_prcnt") { +					opts.pick_cover_prcnt = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_cover") { +					opts.weight_cover = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_w") { +					opts.weight_pq_w = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_b") { +					opts.weight_pq_b = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_c") { +					opts.weight_pq_c = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_s") { +					opts.weight_pq_s = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_mw") { +					opts.weight_pq_mw = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_mb") { +					opts.weight_pq_mb = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_mc") { +					opts.weight_pq_mc = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +				if (args[argidx+1] == "weight_pq_ms") { +					opts.weight_pq_ms = atoi(args[argidx+2].c_str()); +					argidx += 2; +					continue; +				} +			} +			break; +		} +		extra_args(args, argidx, design); + +		if (N >= 0) { +			mutate_list(design, opts, filename, N); +			return; +		} + +		if (opts.mode == "inv") { +			mutate_inv(design, opts); +			return; +		} + +		if (opts.mode == "const0" || opts.mode == "const1") { +			mutate_const(design, opts, opts.mode == "const1"); +			return; +		} + +		if (opts.mode == "cnot0" || opts.mode == "cnot1") { +			mutate_cnot(design, opts, opts.mode == "cnot1"); +			return; +		} + +		log_cmd_error("Invalid mode: %s\n", opts.mode.c_str()); +	} +} MutatePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc new file mode 100644 index 000000000..ba44f02d8 --- /dev/null +++ b/passes/sat/supercover.cc @@ -0,0 +1,92 @@ +/* + *  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 SupercoverPass : public Pass { +	SupercoverPass() : Pass("supercover", "add hi/lo cover cells for each wire bit") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    supercover [options] [selection]\n"); +		log("\n"); +		log("This command adds two cover cells for each bit of each selected wire, one\n"); +		log("checking for a hi signal level and one checking for lo level.\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		// bool flag_noinit = false; + +		log_header(design, "Executing SUPERCOVER pass.\n"); + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			// if (args[argidx] == "-noinit") { +			// 	flag_noinit = true; +			// 	continue; +			// } +			break; +		} +		extra_args(args, argidx, design); + +		for (auto module : design->selected_modules()) +		{ +			SigMap sigmap(module); +			pool<SigBit> handled_bits; + +			int cnt_wire = 0, cnt_bits = 0; +			log("Adding cover cells to module %s.\n", log_id(module)); +			for (auto wire : module->selected_wires()) +			{ +				bool counted_wire = false; +				std::string src = wire->get_src_attribute(); + +				for (auto bit : sigmap(SigSpec(wire))) +				{ +					if (bit.wire == nullptr) +						continue; + +					if (handled_bits.count(bit)) +						continue; + +					SigSpec inv = module->Not(NEW_ID, bit); +					module->addCover(NEW_ID, bit, State::S1, src); +					module->addCover(NEW_ID, inv, State::S1, src); + +					handled_bits.insert(bit); +					if (!counted_wire) { +						counted_wire = false; +						cnt_wire++; +					} +					cnt_bits++; +				} +			} +			log("  added cover cells to %d wires, %d bits.\n", cnt_wire, cnt_bits); +		} +	} +} SupercoverPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index d2d15a4a9..21b70f492 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -327,8 +327,26 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)  	}  } -std::string remap_name(RTLIL::IdString abc_name) +std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)  { +	std::string abc_sname = abc_name.substr(1); +	if (abc_sname.substr(0, 5) == "ys__n") { +		int sid = std::stoi(abc_sname.substr(5)); +		bool inv = abc_sname.back() == 'v'; +		for (auto sig : signal_list) { +			if (sig.id == sid && sig.bit.wire != nullptr) { +				std::stringstream sstr; +				sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1); +				if (sig.bit.wire->width != 1) +					sstr << "[" << sig.bit.offset << "]"; +				if (inv) +					sstr << "_inv"; +				if (orig_wire != nullptr) +					*orig_wire = sig.bit.wire; +				return sstr.str(); +			} +		} +	}  	std::stringstream sstr;  	sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);  	return sstr.str(); @@ -353,12 +371,12 @@ void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std:  	}  	for (auto n : nodes) -		fprintf(f, "  n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit), +		fprintf(f, "  ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),  				n, in_counts[n], workpool.count(n) ? ", shape=box" : "");  	for (auto &e : edges)  	for (auto n : e.second) -		fprintf(f, "  n%d -> n%d;\n", e.first, n); +		fprintf(f, "  ys__n%d -> ys__n%d;\n", e.first, n);  	fprintf(f, "}\n");  } @@ -624,7 +642,7 @@ struct abc_output_filter  void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,  		std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,  		bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, -		const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode) +		const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress)  {  	module = current_module;  	map_autoidx = autoidx++; @@ -728,7 +746,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  	for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))  		abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3); - +	if (abc_dress) +		abc_script += "; dress";  	abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());  	abc_script = add_echos_to_abc_cmd(abc_script); @@ -784,7 +803,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  	for (auto &si : signal_list) {  		if (!si.is_port || si.type != G(NONE))  			continue; -		fprintf(f, " n%d", si.id); +		fprintf(f, " ys__n%d", si.id);  		pi_map[count_input++] = log_signal(si.bit);  	}  	if (count_input == 0) @@ -796,17 +815,17 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  	for (auto &si : signal_list) {  		if (!si.is_port || si.type == G(NONE))  			continue; -		fprintf(f, " n%d", si.id); +		fprintf(f, " ys__n%d", si.id);  		po_map[count_output++] = log_signal(si.bit);  	}  	fprintf(f, "\n");  	for (auto &si : signal_list) -		fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit)); +		fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit));  	for (auto &si : signal_list) {  		if (si.bit.wire == NULL) { -			fprintf(f, ".names n%d\n", si.id); +			fprintf(f, ".names ys__n%d\n", si.id);  			if (si.bit == RTLIL::State::S1)  				fprintf(f, "1\n");  		} @@ -815,68 +834,68 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  	int count_gates = 0;  	for (auto &si : signal_list) {  		if (si.type == G(BUF)) { -			fprintf(f, ".names n%d n%d\n", si.in1, si.id); +			fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);  			fprintf(f, "1 1\n");  		} else if (si.type == G(NOT)) { -			fprintf(f, ".names n%d n%d\n", si.in1, si.id); +			fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);  			fprintf(f, "0 1\n");  		} else if (si.type == G(AND)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "11 1\n");  		} else if (si.type == G(NAND)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "0- 1\n");  			fprintf(f, "-0 1\n");  		} else if (si.type == G(OR)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "-1 1\n");  			fprintf(f, "1- 1\n");  		} else if (si.type == G(NOR)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "00 1\n");  		} else if (si.type == G(XOR)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "01 1\n");  			fprintf(f, "10 1\n");  		} else if (si.type == G(XNOR)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "00 1\n");  			fprintf(f, "11 1\n");  		} else if (si.type == G(ANDNOT)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "10 1\n");  		} else if (si.type == G(ORNOT)) { -			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);  			fprintf(f, "1- 1\n");  			fprintf(f, "-0 1\n");  		} else if (si.type == G(MUX)) { -			fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);  			fprintf(f, "1-0 1\n");  			fprintf(f, "-11 1\n");  		} else if (si.type == G(AOI3)) { -			fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);  			fprintf(f, "-00 1\n");  			fprintf(f, "0-0 1\n");  		} else if (si.type == G(OAI3)) { -			fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);  			fprintf(f, "00- 1\n");  			fprintf(f, "--0 1\n");  		} else if (si.type == G(AOI4)) { -			fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);  			fprintf(f, "-0-0 1\n");  			fprintf(f, "-00- 1\n");  			fprintf(f, "0--0 1\n");  			fprintf(f, "0-0- 1\n");  		} else if (si.type == G(OAI4)) { -			fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); +			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);  			fprintf(f, "00-- 1\n");  			fprintf(f, "--00 1\n");  		} else if (si.type == G(FF)) {  			if (si.init == State::S0 || si.init == State::S1) { -				fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0); +				fprintf(f, ".latch ys__n%d ys__n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);  				recover_init = true;  			} else -				fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id); +				fprintf(f, ".latch ys__n%d ys__n%d 2\n", si.in1, si.id);  		} else if (si.type != G(NONE))  			log_abort();  		if (si.type != G(NONE)) @@ -889,7 +908,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  	log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",  			count_gates, GetSize(signal_list), count_input, count_output);  	log_push(); -  	if (count_output > 0)  	{  		log_header(design, "Executing ABC.\n"); @@ -988,7 +1006,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  			log_error("ABC output file does not contain a module `netlist'.\n");  		for (auto &it : mapped_mod->wires_) {  			RTLIL::Wire *w = it.second; -			RTLIL::Wire *wire = module->addWire(remap_name(w->name)); +			RTLIL::Wire *orig_wire = nullptr; +			RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); +			if (orig_wire != nullptr && orig_wire->attributes.count("\\src")) +				wire->attributes["\\src"] = orig_wire->attributes["\\src"];  			if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;  			design->select(module, wire);  		} @@ -1213,7 +1234,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin  		for (auto &si : signal_list)  			if (si.is_port) {  				char buffer[100]; -				snprintf(buffer, 100, "\\n%d", si.id); +				snprintf(buffer, 100, "\\ys__n%d", si.id);  				RTLIL::SigSig conn;  				if (si.type != G(NONE)) {  					conn.first = si.bit; @@ -1407,6 +1428,11 @@ struct AbcPass : public Pass {  		log("        this attribute is a unique integer for each ABC process started. This\n");  		log("        is useful for debugging the partitioning of clock domains.\n");  		log("\n"); +		log("    -dress\n"); +		log("        run the 'dress' command after all other ABC commands. This aims to\n"); +		log("        preserve naming by an equivalence check between the original and post-ABC\n"); +		log("        netlists (experimental).\n"); +		log("\n");  		log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n");  		log("loaded into ABC before the ABC script is executed.\n");  		log("\n"); @@ -1441,6 +1467,7 @@ struct AbcPass : public Pass {  		std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";  		bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;  		bool show_tempdir = false, sop_mode = false; +		bool abc_dress = false;  		vector<int> lut_costs;  		markgroups = false; @@ -1555,6 +1582,10 @@ struct AbcPass : public Pass {  				map_mux16 = true;  				continue;  			} +			if (arg == "-dress") { +				abc_dress = true; +				continue; +			}  			if (arg == "-g" && argidx+1 < args.size()) {  				for (auto g : split_tokens(args[++argidx], ",")) {  					vector<string> gate_list; @@ -1704,7 +1735,7 @@ struct AbcPass : public Pass {  			if (!dff_mode || !clk_str.empty()) {  				abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, -						delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); +						delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress);  				continue;  			} @@ -1849,7 +1880,7 @@ struct AbcPass : public Pass {  				en_polarity = std::get<2>(it.first);  				en_sig = assign_map(std::get<3>(it.first));  				abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", -						keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode); +						keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress);  				assign_map.set(mod);  			}  		} diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index b0528d473..274177a68 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -660,8 +660,8 @@ struct DfflibmapPass : public Pass {  		map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_");  		map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_"); - 		log("  final dff cell mappings:\n"); - 		logmap_all(); +		log("  final dff cell mappings:\n"); +		logmap_all();  		for (auto &it : design->modules_)  			if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox")) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index ddbd7bf5d..0b7931e48 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -132,9 +132,9 @@ static void dump_dot_graph(string filename,                             pool<RTLIL::SigBit> nodes, dict<RTLIL::SigBit, pool<RTLIL::SigBit>> edges,                             pool<RTLIL::SigBit> inputs, pool<RTLIL::SigBit> outputs,                             std::function<GraphStyle(RTLIL::SigBit)> node_style = -                           		[](RTLIL::SigBit) { return GraphStyle{}; }, +                                   [](RTLIL::SigBit) { return GraphStyle{}; },                             std::function<GraphStyle(RTLIL::SigBit, RTLIL::SigBit)> edge_style = -                           		[](RTLIL::SigBit, RTLIL::SigBit) { return GraphStyle{}; }, +                                   [](RTLIL::SigBit, RTLIL::SigBit) { return GraphStyle{}; },                             string name = "")  {  	FILE *f = fopen(filename.c_str(), "w"); | 
