mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-20 14:20:32 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			368 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			368 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *  yosys -- Yosys Open SYnthesis Suite
 | |
|  *
 | |
|  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com>
 | |
|  *                2019  Eddie Hung    <eddie@fpgeh.com>
 | |
|  *
 | |
|  *  Permission to use, copy, modify, and/or distribute this software for any
 | |
|  *  purpose with or without fee is hereby granted, provided that the above
 | |
|  *  copyright notice and this permission notice appear in all copies.
 | |
|  *
 | |
|  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | |
|  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | |
|  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | |
|  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | |
|  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | |
|  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | |
|  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "kernel/yosys.h"
 | |
| #include "kernel/sigtools.h"
 | |
| 
 | |
| USING_YOSYS_NAMESPACE
 | |
| PRIVATE_NAMESPACE_BEGIN
 | |
| 
 | |
| struct ExclusiveDatabase
 | |
| {
 | |
| 	Module *module;
 | |
| 	const SigMap &sigmap;
 | |
| 
 | |
| 	dict<SigBit, std::pair<SigSpec,std::vector<Const>>> sig_cmp_prev;
 | |
| 
 | |
| 	ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
 | |
| 	{
 | |
| 		SigSpec const_sig, nonconst_sig;
 | |
| 		SigBit y_port;
 | |
| 		pool<Cell*> reduce_or;
 | |
| 		for (auto cell : module->cells()) {
 | |
| 			if (cell->type == ID($eq)) {
 | |
| 				nonconst_sig = sigmap(cell->getPort(ID::A));
 | |
| 				const_sig = sigmap(cell->getPort(ID::B));
 | |
| 				if (!const_sig.is_fully_const()) {
 | |
| 					if (!nonconst_sig.is_fully_const())
 | |
| 						continue;
 | |
| 					std::swap(nonconst_sig, const_sig);
 | |
| 				}
 | |
| 				y_port = sigmap(cell->getPort(ID::Y));
 | |
| 			}
 | |
| 			else if (cell->type == ID($logic_not)) {
 | |
| 				nonconst_sig = sigmap(cell->getPort(ID::A));
 | |
| 				const_sig = Const(State::S0, GetSize(nonconst_sig));
 | |
| 				y_port = sigmap(cell->getPort(ID::Y));
 | |
| 			}
 | |
| 			else if (cell->type == ID($reduce_or)) {
 | |
| 				reduce_or.insert(cell);
 | |
| 				continue;
 | |
| 			}
 | |
| 			else continue;
 | |
| 
 | |
| 			log_assert(!nonconst_sig.empty());
 | |
| 			log_assert(!const_sig.empty());
 | |
| 			sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::vector<Const>{const_sig.as_const()});
 | |
| 		}
 | |
| 
 | |
| 		for (auto cell : reduce_or) {
 | |
| 			nonconst_sig = SigSpec();
 | |
| 			std::vector<Const> values;
 | |
| 			SigSpec a_port = sigmap(cell->getPort(ID::A));
 | |
| 			for (auto bit : a_port) {
 | |
| 				auto it = sig_cmp_prev.find(bit);
 | |
| 				if (it == sig_cmp_prev.end()) {
 | |
| 					nonconst_sig = SigSpec();
 | |
| 					break;
 | |
| 				}
 | |
| 				if (nonconst_sig.empty())
 | |
| 					nonconst_sig = it->second.first;
 | |
| 				else if (nonconst_sig != it->second.first) {
 | |
| 					nonconst_sig = SigSpec();
 | |
| 					break;
 | |
| 				}
 | |
| 				for (auto value : it->second.second)
 | |
| 					values.push_back(value);
 | |
| 			}
 | |
| 			if (nonconst_sig.empty())
 | |
| 				continue;
 | |
| 			y_port = sigmap(cell->getPort(ID::Y));
 | |
| 			sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	bool query(const SigSpec &sig) const
 | |
| 	{
 | |
| 		SigSpec nonconst_sig;
 | |
| 		pool<Const> const_values;
 | |
| 
 | |
| 		for (auto bit : sig) {
 | |
| 			auto it = sig_cmp_prev.find(bit);
 | |
| 			if (it == sig_cmp_prev.end())
 | |
| 				return false;
 | |
| 
 | |
| 			if (nonconst_sig.empty())
 | |
| 				nonconst_sig = it->second.first;
 | |
| 			else if (nonconst_sig != it->second.first)
 | |
| 				return false;
 | |
| 
 | |
| 			for (auto value : it->second.second)
 | |
| 				if (!const_values.insert(value).second)
 | |
| 					return false;
 | |
| 		}
 | |
| 
 | |
| 		return true;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| struct MuxpackWorker
 | |
| {
 | |
| 	Module *module;
 | |
| 	SigMap sigmap;
 | |
| 
 | |
| 	int mux_count, pmux_count;
 | |
| 
 | |
| 	pool<Cell*> remove_cells;
 | |
| 
 | |
| 	dict<SigSpec, Cell*> sig_chain_next;
 | |
| 	dict<SigSpec, Cell*> sig_chain_prev;
 | |
| 	pool<SigBit> sigbit_with_non_chain_users;
 | |
| 	pool<Cell*> chain_start_cells;
 | |
| 	pool<Cell*> candidate_cells;
 | |
| 
 | |
| 	ExclusiveDatabase excl_db;
 | |
| 
 | |
| 	void make_sig_chain_next_prev()
 | |
| 	{
 | |
| 		for (auto wire : module->wires())
 | |
| 		{
 | |
| 			if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
 | |
| 				for (auto bit : sigmap(wire))
 | |
| 					sigbit_with_non_chain_users.insert(bit);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		for (auto cell : module->cells())
 | |
| 		{
 | |
| 			if (cell->type.in(ID($mux), ID($pmux)) && !cell->get_bool_attribute(ID::keep))
 | |
| 			{
 | |
| 				SigSpec a_sig = sigmap(cell->getPort(ID::A));
 | |
| 				SigSpec b_sig;
 | |
| 				if (cell->type == ID($mux))
 | |
| 					b_sig = sigmap(cell->getPort(ID::B));
 | |
| 				SigSpec y_sig = sigmap(cell->getPort(ID::Y));
 | |
|    
 | |
| 				if (sig_chain_next.count(a_sig))
 | |
| 					for (auto a_bit : a_sig)
 | |
| 						sigbit_with_non_chain_users.insert(a_bit);
 | |
| 				else {
 | |
| 					sig_chain_next[a_sig] = cell;
 | |
| 					candidate_cells.insert(cell);
 | |
| 				}
 | |
| 
 | |
| 				if (!b_sig.empty()) {
 | |
| 					if (sig_chain_next.count(b_sig))
 | |
| 						for (auto b_bit : b_sig)
 | |
| 							sigbit_with_non_chain_users.insert(b_bit);
 | |
| 					else {
 | |
| 						sig_chain_next[b_sig] = cell;
 | |
| 						candidate_cells.insert(cell);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				sig_chain_prev[y_sig] = cell;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			for (auto conn : cell->connections())
 | |
| 				if (cell->input(conn.first))
 | |
| 					for (auto bit : sigmap(conn.second))
 | |
| 						sigbit_with_non_chain_users.insert(bit);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void find_chain_start_cells()
 | |
| 	{
 | |
| 		for (auto cell : candidate_cells)
 | |
| 		{
 | |
| 			log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type));
 | |
| 
 | |
| 			SigSpec a_sig = sigmap(cell->getPort(ID::A));
 | |
| 			if (cell->type == ID($mux)) {
 | |
| 				SigSpec b_sig = sigmap(cell->getPort(ID::B));
 | |
| 				if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1)
 | |
| 					goto start_cell;
 | |
| 
 | |
| 				if (!sig_chain_prev.count(a_sig))
 | |
| 					a_sig = b_sig;
 | |
| 			}
 | |
| 			else if (cell->type == ID($pmux)) {
 | |
| 				if (!sig_chain_prev.count(a_sig))
 | |
| 					goto start_cell;
 | |
| 			}
 | |
| 			else log_abort();
 | |
| 
 | |
| 			for (auto bit : a_sig)
 | |
| 				if (sigbit_with_non_chain_users.count(bit))
 | |
| 					goto start_cell;
 | |
| 
 | |
| 			{
 | |
| 				Cell *prev_cell = sig_chain_prev.at(a_sig);
 | |
| 				log_assert(prev_cell);
 | |
| 				SigSpec s_sig = sigmap(cell->getPort(ID::S));
 | |
| 				s_sig.append(sigmap(prev_cell->getPort(ID::S)));
 | |
| 				if (!excl_db.query(s_sig))
 | |
| 					goto start_cell;
 | |
| 			}
 | |
| 
 | |
| 			continue;
 | |
| 
 | |
| 		start_cell:
 | |
| 			chain_start_cells.insert(cell);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	vector<Cell*> create_chain(Cell *start_cell)
 | |
| 	{
 | |
| 		vector<Cell*> chain;
 | |
| 
 | |
| 		Cell *c = start_cell;
 | |
| 		while (c != nullptr)
 | |
| 		{
 | |
| 			chain.push_back(c);
 | |
| 
 | |
| 			SigSpec y_sig = sigmap(c->getPort(ID::Y));
 | |
| 
 | |
| 			if (sig_chain_next.count(y_sig) == 0)
 | |
| 				break;
 | |
| 
 | |
| 			c = sig_chain_next.at(y_sig);
 | |
| 			if (chain_start_cells.count(c) != 0)
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		return chain;
 | |
| 	}
 | |
| 
 | |
| 	void process_chain(vector<Cell*> &chain)
 | |
| 	{
 | |
| 		if (GetSize(chain) < 2)
 | |
| 			return;
 | |
| 
 | |
| 		int cursor = 0;
 | |
| 		while (cursor < GetSize(chain))
 | |
| 		{
 | |
| 			int cases = GetSize(chain) - cursor;
 | |
| 
 | |
| 			Cell *first_cell = chain[cursor];
 | |
| 			dict<int, SigBit> taps_dict;
 | |
| 
 | |
| 			if (cases < 2) {
 | |
| 				cursor++;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			Cell *last_cell = chain[cursor+cases-1];
 | |
| 
 | |
| 			log("Converting %s.%s ... %s.%s to a pmux with %d cases.\n",
 | |
| 				log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), cases);
 | |
| 
 | |
| 			mux_count += cases;
 | |
| 			pmux_count += 1;
 | |
| 
 | |
| 			first_cell->type = ID($pmux);
 | |
| 			SigSpec b_sig = first_cell->getPort(ID::B);
 | |
| 			SigSpec s_sig = first_cell->getPort(ID::S);
 | |
| 
 | |
| 			for (int i = 1; i < cases; i++) {
 | |
| 				Cell* prev_cell = chain[cursor+i-1];
 | |
| 				Cell* cursor_cell = chain[cursor+i];
 | |
| 				if (sigmap(prev_cell->getPort(ID::Y)) == sigmap(cursor_cell->getPort(ID::A))) {
 | |
| 					b_sig.append(cursor_cell->getPort(ID::B));
 | |
| 					s_sig.append(cursor_cell->getPort(ID::S));
 | |
| 				}
 | |
| 				else {
 | |
| 					log_assert(cursor_cell->type == ID($mux));
 | |
| 					b_sig.append(cursor_cell->getPort(ID::A));
 | |
| 					s_sig.append(module->LogicNot(NEW_ID, cursor_cell->getPort(ID::S)));
 | |
| 				}
 | |
| 				remove_cells.insert(cursor_cell);
 | |
| 			}
 | |
| 
 | |
| 			first_cell->setPort(ID::B, b_sig);
 | |
| 			first_cell->setPort(ID::S, s_sig);
 | |
| 			first_cell->setParam(ID::S_WIDTH, GetSize(s_sig));
 | |
| 			first_cell->setPort(ID::Y, last_cell->getPort(ID::Y));
 | |
| 
 | |
| 			cursor += cases;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void cleanup()
 | |
| 	{
 | |
| 		for (auto cell : remove_cells)
 | |
| 			module->remove(cell);
 | |
| 
 | |
| 		remove_cells.clear();
 | |
| 		sig_chain_next.clear();
 | |
| 		sig_chain_prev.clear();
 | |
| 		chain_start_cells.clear();
 | |
| 		candidate_cells.clear();
 | |
| 	}
 | |
| 
 | |
| 	MuxpackWorker(Module *module) :
 | |
| 			module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap)
 | |
| 	{
 | |
| 		make_sig_chain_next_prev();
 | |
| 		find_chain_start_cells();
 | |
| 
 | |
| 		for (auto c : chain_start_cells) {
 | |
| 			vector<Cell*> chain = create_chain(c);
 | |
| 			process_chain(chain);
 | |
| 		}
 | |
| 
 | |
| 		cleanup();
 | |
| 	}
 | |
| };
 | |
| 
 | |
| struct MuxpackPass : public Pass {
 | |
| 	MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { }
 | |
| 	void help() override
 | |
| 	{
 | |
| 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | |
| 		log("\n");
 | |
| 		log("    muxpack [selection]\n");
 | |
| 		log("\n");
 | |
| 		log("This pass converts cascaded chains of $pmux cells (e.g. those create from case\n");
 | |
| 		log("constructs) and $mux cells (e.g. those created by if-else constructs) into\n");
 | |
| 		log("$pmux cells.\n");
 | |
| 		log("\n");
 | |
| 		log("This optimisation is conservative --- it will only pack $mux or $pmux cells\n");
 | |
| 		log("whose select lines are driven by '$eq' cells with other such cells if it can be\n");
 | |
| 		log("certain that their select inputs are mutually exclusive.\n");
 | |
| 		log("\n");
 | |
| 	}
 | |
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) override
 | |
| 	{
 | |
| 		log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n");
 | |
| 
 | |
| 		size_t argidx;
 | |
| 		for (argidx = 1; argidx < args.size(); argidx++)
 | |
| 		{
 | |
| 			break;
 | |
| 		}
 | |
| 		extra_args(args, argidx, design);
 | |
| 
 | |
| 		int mux_count = 0;
 | |
| 		int pmux_count = 0;
 | |
| 
 | |
| 		for (auto module : design->selected_modules()) {
 | |
| 			MuxpackWorker worker(module);
 | |
| 			mux_count += worker.mux_count;
 | |
| 			pmux_count += worker.pmux_count;
 | |
| 		}
 | |
| 
 | |
| 		log("Converted %d (p)mux cells into %d pmux cells.\n", mux_count, pmux_count);
 | |
| 	}
 | |
| } MuxpackPass;
 | |
| 
 | |
| PRIVATE_NAMESPACE_END
 |