mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			367 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			367 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *  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 EquivStructWorker
 | 
						|
{
 | 
						|
	Module *module;
 | 
						|
	SigMap sigmap;
 | 
						|
	SigMap equiv_bits;
 | 
						|
	bool mode_fwd;
 | 
						|
	bool mode_icells;
 | 
						|
	int merge_count;
 | 
						|
 | 
						|
	const pool<IdString> &fwonly_cells;
 | 
						|
 | 
						|
	struct merge_key_t
 | 
						|
	{
 | 
						|
		IdString type;
 | 
						|
		vector<pair<IdString, Const>> parameters;
 | 
						|
		vector<pair<IdString, int>> port_sizes;
 | 
						|
		vector<tuple<IdString, int, SigBit>> connections;
 | 
						|
 | 
						|
		bool operator==(const merge_key_t &other) const {
 | 
						|
			return type == other.type && connections == other.connections &&
 | 
						|
					parameters == other.parameters && port_sizes == other.port_sizes;
 | 
						|
		}
 | 
						|
 | 
						|
		unsigned int hash() const {
 | 
						|
			unsigned int h = mkhash_init;
 | 
						|
			h = mkhash(h, mkhash(type));
 | 
						|
			h = mkhash(h, mkhash(parameters));
 | 
						|
			h = mkhash(h, mkhash(connections));
 | 
						|
			return h;
 | 
						|
		}
 | 
						|
	};
 | 
						|
 | 
						|
	dict<merge_key_t, pool<IdString>> merge_cache;
 | 
						|
	pool<merge_key_t> fwd_merge_cache, bwd_merge_cache;
 | 
						|
 | 
						|
	void merge_cell_pair(Cell *cell_a, Cell *cell_b)
 | 
						|
	{
 | 
						|
		SigMap merged_map;
 | 
						|
		merge_count++;
 | 
						|
 | 
						|
		SigSpec inputs_a, inputs_b;
 | 
						|
		vector<string> input_names;
 | 
						|
 | 
						|
		for (auto &port_a : cell_a->connections())
 | 
						|
		{
 | 
						|
			SigSpec bits_a = sigmap(port_a.second);
 | 
						|
			SigSpec bits_b = sigmap(cell_b->getPort(port_a.first));
 | 
						|
 | 
						|
			log_assert(GetSize(bits_a) == GetSize(bits_b));
 | 
						|
 | 
						|
			if (!cell_a->output(port_a.first))
 | 
						|
				for (int i = 0; i < GetSize(bits_a); i++)
 | 
						|
					if (bits_a[i] != bits_b[i]) {
 | 
						|
						inputs_a.append(bits_a[i]);
 | 
						|
						inputs_b.append(bits_b[i]);
 | 
						|
						input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
 | 
						|
								stringf("%s[%d]", log_id(port_a.first), i));
 | 
						|
					}
 | 
						|
		}
 | 
						|
 | 
						|
		for (int i = 0; i < GetSize(inputs_a); i++) {
 | 
						|
			SigBit bit_a = inputs_a[i], bit_b = inputs_b[i];
 | 
						|
			SigBit bit_y = module->addWire(NEW_ID);
 | 
						|
			log("        New $equiv for input %s: A: %s, B: %s, Y: %s\n",
 | 
						|
					input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y));
 | 
						|
			module->addEquiv(NEW_ID, bit_a, bit_b, bit_y);
 | 
						|
			merged_map.add(bit_a, bit_y);
 | 
						|
			merged_map.add(bit_b, bit_y);
 | 
						|
		}
 | 
						|
 | 
						|
		std::vector<IdString> outport_names, inport_names;
 | 
						|
 | 
						|
		for (auto &port_a : cell_a->connections())
 | 
						|
			if (cell_a->output(port_a.first))
 | 
						|
				outport_names.push_back(port_a.first);
 | 
						|
			else
 | 
						|
				inport_names.push_back(port_a.first);
 | 
						|
 | 
						|
		for (auto &pn : inport_names)
 | 
						|
			cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn))));
 | 
						|
 | 
						|
		for (auto &pn : outport_names) {
 | 
						|
			SigSpec sig_a = cell_a->getPort(pn);
 | 
						|
			SigSpec sig_b = cell_b->getPort(pn);
 | 
						|
			module->connect(sig_b, sig_a);
 | 
						|
		}
 | 
						|
 | 
						|
		auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
 | 
						|
		merged_attr.insert(log_id(cell_b));
 | 
						|
		cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
 | 
						|
		module->remove(cell_b);
 | 
						|
	}
 | 
						|
 | 
						|
	EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells, const pool<IdString> &fwonly_cells, int iter_num) :
 | 
						|
			module(module), sigmap(module), equiv_bits(module),
 | 
						|
			mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0), fwonly_cells(fwonly_cells)
 | 
						|
	{
 | 
						|
		log("  Starting iteration %d.\n", iter_num);
 | 
						|
 | 
						|
		pool<SigBit> equiv_inputs;
 | 
						|
		pool<IdString> cells;
 | 
						|
 | 
						|
		for (auto cell : module->selected_cells())
 | 
						|
			if (cell->type == "$equiv") {
 | 
						|
				SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
 | 
						|
				SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
 | 
						|
				equiv_bits.add(sig_b, sig_a);
 | 
						|
				equiv_inputs.insert(sig_a);
 | 
						|
				equiv_inputs.insert(sig_b);
 | 
						|
				cells.insert(cell->name);
 | 
						|
			} else {
 | 
						|
				if (mode_icells || module->design->module(cell->type))
 | 
						|
					cells.insert(cell->name);
 | 
						|
			}
 | 
						|
 | 
						|
		for (auto cell : module->selected_cells())
 | 
						|
			if (cell->type == "$equiv") {
 | 
						|
				SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
 | 
						|
				SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
 | 
						|
				SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
 | 
						|
				if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
 | 
						|
					log("    Purging redundant $equiv cell %s.\n", log_id(cell));
 | 
						|
					module->connect(sig_y, sig_a);
 | 
						|
					module->remove(cell);
 | 
						|
					merge_count++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
		if (merge_count > 0)
 | 
						|
			return;
 | 
						|
 | 
						|
		for (auto cell_name : cells)
 | 
						|
		{
 | 
						|
			merge_key_t key;
 | 
						|
			vector<tuple<IdString, int, SigBit>> fwd_connections;
 | 
						|
 | 
						|
			Cell *cell = module->cell(cell_name);
 | 
						|
			key.type = cell->type;
 | 
						|
 | 
						|
			for (auto &it : cell->parameters)
 | 
						|
				key.parameters.push_back(it);
 | 
						|
			std::sort(key.parameters.begin(), key.parameters.end());
 | 
						|
 | 
						|
			for (auto &it : cell->connections())
 | 
						|
				key.port_sizes.push_back(make_pair(it.first, GetSize(it.second)));
 | 
						|
			std::sort(key.port_sizes.begin(), key.port_sizes.end());
 | 
						|
 | 
						|
			for (auto &conn : cell->connections())
 | 
						|
			{
 | 
						|
				if (cell->input(conn.first)) {
 | 
						|
					SigSpec sig = sigmap(conn.second);
 | 
						|
					for (int i = 0; i < GetSize(sig); i++)
 | 
						|
						fwd_connections.push_back(make_tuple(conn.first, i, sig[i]));
 | 
						|
				}
 | 
						|
 | 
						|
				if (cell->output(conn.first)) {
 | 
						|
					SigSpec sig = equiv_bits(conn.second);
 | 
						|
					for (int i = 0; i < GetSize(sig); i++) {
 | 
						|
						key.connections.clear();
 | 
						|
						key.connections.push_back(make_tuple(conn.first, i, sig[i]));
 | 
						|
 | 
						|
						if (merge_cache.count(key))
 | 
						|
							bwd_merge_cache.insert(key);
 | 
						|
						merge_cache[key].insert(cell_name);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			std::sort(fwd_connections.begin(), fwd_connections.end());
 | 
						|
			key.connections.swap(fwd_connections);
 | 
						|
 | 
						|
			if (merge_cache.count(key))
 | 
						|
				fwd_merge_cache.insert(key);
 | 
						|
			merge_cache[key].insert(cell_name);
 | 
						|
		}
 | 
						|
 | 
						|
		for (int phase = 0; phase < 2; phase++)
 | 
						|
		{
 | 
						|
			auto &queue = phase ? bwd_merge_cache : fwd_merge_cache;
 | 
						|
 | 
						|
			for (auto &key : queue)
 | 
						|
			{
 | 
						|
				const char *strategy = nullptr;
 | 
						|
				vector<Cell*> gold_cells, gate_cells, other_cells;
 | 
						|
				vector<pair<Cell*, Cell*>> cell_pairs;
 | 
						|
				IdString cells_type;
 | 
						|
 | 
						|
				for (auto cell_name : merge_cache[key]) {
 | 
						|
					Cell *c = module->cell(cell_name);
 | 
						|
					if (c != nullptr) {
 | 
						|
						string n = cell_name.str();
 | 
						|
						cells_type = c->type;
 | 
						|
						if (GetSize(n) > 5 && n.compare(GetSize(n)-5, std::string::npos, "_gold") == 0)
 | 
						|
							gold_cells.push_back(c);
 | 
						|
						else if (GetSize(n) > 5 && n.compare(GetSize(n)-5, std::string::npos, "_gate") == 0)
 | 
						|
							gate_cells.push_back(c);
 | 
						|
						else
 | 
						|
							other_cells.push_back(c);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (phase && fwonly_cells.count(cells_type))
 | 
						|
					continue;
 | 
						|
 | 
						|
				if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1)
 | 
						|
				{
 | 
						|
					strategy = "deduplicate";
 | 
						|
					for (int i = 0; i+1 < GetSize(gold_cells); i += 2)
 | 
						|
						cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1]));
 | 
						|
					for (int i = 0; i+1 < GetSize(gate_cells); i += 2)
 | 
						|
						cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1]));
 | 
						|
					for (int i = 0; i+1 < GetSize(other_cells); i += 2)
 | 
						|
						cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1]));
 | 
						|
					goto run_strategy;
 | 
						|
				}
 | 
						|
 | 
						|
				if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1)
 | 
						|
				{
 | 
						|
					strategy = "gold-gate-pairs";
 | 
						|
					cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0]));
 | 
						|
					goto run_strategy;
 | 
						|
				}
 | 
						|
 | 
						|
				if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1)
 | 
						|
				{
 | 
						|
					strategy = "gold-guess";
 | 
						|
					cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0]));
 | 
						|
					goto run_strategy;
 | 
						|
				}
 | 
						|
 | 
						|
				if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1)
 | 
						|
				{
 | 
						|
					strategy = "gate-guess";
 | 
						|
					cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0]));
 | 
						|
					goto run_strategy;
 | 
						|
				}
 | 
						|
 | 
						|
				log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2);
 | 
						|
				continue;
 | 
						|
 | 
						|
			run_strategy:
 | 
						|
				int total_group_size = GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells);
 | 
						|
				log("    %s merging %d %s cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd",
 | 
						|
						2*GetSize(cell_pairs), log_id(cells_type), total_group_size, strategy);
 | 
						|
				for (auto it : cell_pairs) {
 | 
						|
					log("      Merging cells %s and %s.\n", log_id(it.first),  log_id(it.second));
 | 
						|
					merge_cell_pair(it.first, it.second);
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (merge_count > 0)
 | 
						|
				return;
 | 
						|
		}
 | 
						|
 | 
						|
		log("    Nothing to merge.\n");
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
struct EquivStructPass : public Pass {
 | 
						|
	EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
 | 
						|
	void help() YS_OVERRIDE
 | 
						|
	{
 | 
						|
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
						|
		log("\n");
 | 
						|
		log("    equiv_struct [options] [selection]\n");
 | 
						|
		log("\n");
 | 
						|
		log("This command adds additional $equiv cells based on the assumption that the\n");
 | 
						|
		log("gold and gate circuit are structurally equivalent. Note that this can introduce\n");
 | 
						|
		log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n");
 | 
						|
		log("for example when analyzing circuits with cells with commutative inputs. This\n");
 | 
						|
		log("command will also de-duplicate gates.\n");
 | 
						|
		log("\n");
 | 
						|
		log("    -fwd\n");
 | 
						|
		log("        by default this command performans forward sweeps until nothing can\n");
 | 
						|
		log("        be merged by forwards sweeps, then backward sweeps until forward\n");
 | 
						|
		log("        sweeps are effective again. with this option set only forward sweeps\n");
 | 
						|
		log("        are performed.\n");
 | 
						|
		log("\n");
 | 
						|
		log("    -fwonly <cell_type>\n");
 | 
						|
		log("        add the specified cell type to the list of cell types that are only\n");
 | 
						|
		log("        merged in forward sweeps and never in backward sweeps. $equiv is in\n");
 | 
						|
		log("        this list automatically.\n");
 | 
						|
		log("\n");
 | 
						|
		log("    -icells\n");
 | 
						|
		log("        by default, the internal RTL and gate cell types are ignored. add\n");
 | 
						|
		log("        this option to also process those cell types with this command.\n");
 | 
						|
		log("\n");
 | 
						|
		log("    -maxiter <N>\n");
 | 
						|
		log("        maximum number of iterations to run before aborting\n");
 | 
						|
		log("\n");
 | 
						|
	}
 | 
						|
	void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
 | 
						|
	{
 | 
						|
		pool<IdString> fwonly_cells({ "$equiv" });
 | 
						|
		bool mode_icells = false;
 | 
						|
		bool mode_fwd = false;
 | 
						|
		int max_iter = -1;
 | 
						|
 | 
						|
		log_header(design, "Executing EQUIV_STRUCT pass.\n");
 | 
						|
 | 
						|
		size_t argidx;
 | 
						|
		for (argidx = 1; argidx < args.size(); argidx++) {
 | 
						|
			if (args[argidx] == "-fwd") {
 | 
						|
				mode_fwd = true;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (args[argidx] == "-icells") {
 | 
						|
				mode_icells = true;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (args[argidx] == "-fwonly" && argidx+1 < args.size()) {
 | 
						|
				fwonly_cells.insert(RTLIL::escape_id(args[++argidx]));
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (args[argidx] == "-maxiter" && argidx+1 < args.size()) {
 | 
						|
				max_iter = atoi(args[++argidx].c_str());
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		extra_args(args, argidx, design);
 | 
						|
 | 
						|
		for (auto module : design->selected_modules()) {
 | 
						|
			int module_merge_count = 0;
 | 
						|
			log("Running equiv_struct on module %s:\n", log_id(module));
 | 
						|
			for (int iter = 0;; iter++) {
 | 
						|
				if (iter == max_iter) {
 | 
						|
					log("  Reached iteration limit of %d.\n", iter);
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				EquivStructWorker worker(module, mode_fwd, mode_icells, fwonly_cells, iter+1);
 | 
						|
				if (worker.merge_count == 0)
 | 
						|
					break;
 | 
						|
				module_merge_count += worker.merge_count;
 | 
						|
			}
 | 
						|
			if (module_merge_count)
 | 
						|
				log("  Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module));
 | 
						|
		}
 | 
						|
	}
 | 
						|
} EquivStructPass;
 | 
						|
 | 
						|
PRIVATE_NAMESPACE_END
 |