mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-26 09:24:37 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			353 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			353 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *  yosys -- Yosys Open SYnthesis Suite
 | |
|  *
 | |
|  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.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/log.h"
 | |
| #include "kernel/register.h"
 | |
| #include "kernel/sigtools.h"
 | |
| #include "kernel/consteval.h"
 | |
| #include "kernel/celltypes.h"
 | |
| #include "fsmdata.h"
 | |
| #include <string.h>
 | |
| 
 | |
| USING_YOSYS_NAMESPACE
 | |
| PRIVATE_NAMESPACE_BEGIN
 | |
| 
 | |
| struct FsmOpt
 | |
| {
 | |
| 	FsmData fsm_data;
 | |
| 	RTLIL::Cell *cell;
 | |
| 	RTLIL::Module *module;
 | |
| 
 | |
| 	void opt_unreachable_states()
 | |
| 	{
 | |
| 		while (1)
 | |
| 		{
 | |
| 			std::set<int> unreachable_states;
 | |
| 			std::vector<FsmData::transition_t> new_transition_table;
 | |
| 			std::vector<RTLIL::Const> new_state_table;
 | |
| 			std::map<int, int> old_to_new_state;
 | |
| 
 | |
| 			for (int i = 0; i < GetSize(fsm_data.state_table); i++)
 | |
| 				if (i != fsm_data.reset_state)
 | |
| 					unreachable_states.insert(i);
 | |
| 
 | |
| 			for (auto &trans : fsm_data.transition_table)
 | |
| 				unreachable_states.erase(trans.state_out);
 | |
| 
 | |
| 			if (unreachable_states.empty())
 | |
| 				break;
 | |
| 
 | |
| 			for (int i = 0; i < GetSize(fsm_data.state_table); i++) {
 | |
| 				if (unreachable_states.count(i)) {
 | |
| 					log("  Removing unreachable state %s.\n", log_signal(fsm_data.state_table[i]));
 | |
| 					continue;
 | |
| 				}
 | |
| 				old_to_new_state[i] = GetSize(new_state_table);
 | |
| 				new_state_table.push_back(fsm_data.state_table[i]);
 | |
| 			}
 | |
| 
 | |
| 			for (auto trans : fsm_data.transition_table) {
 | |
| 				if (unreachable_states.count(trans.state_in))
 | |
| 					continue;
 | |
| 				trans.state_in = old_to_new_state.at(trans.state_in);
 | |
| 				trans.state_out = old_to_new_state.at(trans.state_out);
 | |
| 				new_transition_table.push_back(trans);
 | |
| 			}
 | |
| 
 | |
| 			new_transition_table.swap(fsm_data.transition_table);
 | |
| 			new_state_table.swap(fsm_data.state_table);
 | |
| 			if (fsm_data.reset_state != -1)
 | |
| 				fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	bool signal_is_unused(RTLIL::SigSpec sig)
 | |
| 	{
 | |
| 		RTLIL::SigBit bit = sig.as_bit();
 | |
| 
 | |
| 		if (bit.wire == NULL || bit.wire->attributes.count(ID::unused_bits) == 0)
 | |
| 			return false;
 | |
| 
 | |
| 		char *str = strdup(bit.wire->attributes[ID::unused_bits].decode_string().c_str());
 | |
| 		for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) {
 | |
| 			if (tok[0] && bit.offset == atoi(tok)) {
 | |
| 				free(str);
 | |
| 				return true;
 | |
| 			}
 | |
| 		}
 | |
| 		free(str);
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	void opt_const_and_unused_inputs()
 | |
| 	{
 | |
| 		RTLIL::SigSpec ctrl_in = cell->getPort(ID::CTRL_IN);
 | |
| 		std::vector<bool> ctrl_in_used(ctrl_in.size());
 | |
| 
 | |
| 		std::vector<FsmData::transition_t> new_transition_table;
 | |
| 		for (auto &tr : fsm_data.transition_table) {
 | |
| 			for (int i = 0; i < ctrl_in.size(); i++) {
 | |
| 				RTLIL::SigSpec ctrl_bit = ctrl_in.extract(i, 1);
 | |
| 				if (ctrl_bit.is_fully_const()) {
 | |
| 					if (tr.ctrl_in[i] <= RTLIL::State::S1 && RTLIL::SigSpec(tr.ctrl_in[i]) != ctrl_bit)
 | |
| 						goto delete_this_transition;
 | |
| 					continue;
 | |
| 				}
 | |
| 				if (tr.ctrl_in[i] <= RTLIL::State::S1)
 | |
| 					ctrl_in_used[i] = true;
 | |
| 			}
 | |
| 			new_transition_table.push_back(tr);
 | |
| 		delete_this_transition:;
 | |
| 		}
 | |
| 
 | |
| 		for (int i = int(ctrl_in_used.size())-1; i >= 0; i--) {
 | |
| 			if (!ctrl_in_used[i]) {
 | |
| 				log("  Removing unused input signal %s.\n", log_signal(cell->getPort(ID::CTRL_IN).extract(i, 1)));
 | |
| 				for (auto &tr : new_transition_table) {
 | |
| 					RTLIL::SigSpec tmp(tr.ctrl_in);
 | |
| 					tmp.remove(i, 1);
 | |
| 					tr.ctrl_in = tmp.as_const();
 | |
| 				}
 | |
| 				RTLIL::SigSpec new_ctrl_in = cell->getPort(ID::CTRL_IN);
 | |
| 				new_ctrl_in.remove(i, 1);
 | |
| 				cell->setPort(ID::CTRL_IN, new_ctrl_in);
 | |
| 				fsm_data.num_inputs--;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		fsm_data.transition_table.swap(new_transition_table);
 | |
| 		new_transition_table.clear();
 | |
| 	}
 | |
| 
 | |
| 	void opt_unused_outputs()
 | |
| 	{
 | |
| 		for (int i = 0; i < fsm_data.num_outputs; i++) {
 | |
| 			RTLIL::SigSpec sig = cell->getPort(ID::CTRL_OUT).extract(i, 1);
 | |
| 			if (signal_is_unused(sig)) {
 | |
| 				log("  Removing unused output signal %s.\n", log_signal(sig));
 | |
| 				RTLIL::SigSpec new_ctrl_out = cell->getPort(ID::CTRL_OUT);
 | |
| 				new_ctrl_out.remove(i, 1);
 | |
| 				cell->setPort(ID::CTRL_OUT, new_ctrl_out);
 | |
| 				for (auto &tr : fsm_data.transition_table) {
 | |
| 					RTLIL::SigSpec tmp(tr.ctrl_out);
 | |
| 					tmp.remove(i, 1);
 | |
| 					tr.ctrl_out = tmp.as_const();
 | |
| 				}
 | |
| 				fsm_data.num_outputs--;
 | |
| 				i--;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void opt_alias_inputs()
 | |
| 	{
 | |
| 		RTLIL::SigSpec &ctrl_in = cell->connections_[ID::CTRL_IN];
 | |
| 
 | |
| 		for (int i = 0; i < ctrl_in.size(); i++)
 | |
| 		for (int j = i+1; j < ctrl_in.size(); j++)
 | |
| 			if (ctrl_in.extract(i, 1) == ctrl_in.extract(j, 1))
 | |
| 			{
 | |
| 				log("  Optimize handling of signal %s that is connected to inputs %d and %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j);
 | |
| 				std::vector<FsmData::transition_t> new_transition_table;
 | |
| 
 | |
| 				for (auto tr : fsm_data.transition_table)
 | |
| 				{
 | |
| 					RTLIL::State si = tr.ctrl_in[i];
 | |
| 					RTLIL::State sj = tr.ctrl_in[j];
 | |
| 
 | |
| 					if (si > RTLIL::State::S1) {
 | |
| 						si = sj;
 | |
| 						tr.ctrl_in.set(i, si);
 | |
| 					} else if (sj > RTLIL::State::S1) {
 | |
| 						sj = si;
 | |
| 						tr.ctrl_in.set(j, sj);
 | |
| 					}
 | |
| 
 | |
| 					if (si == sj) {
 | |
| 						RTLIL::SigSpec tmp(tr.ctrl_in);
 | |
| 						tmp.remove(j, 1);
 | |
| 						tr.ctrl_in = tmp.as_const();
 | |
| 						new_transition_table.push_back(tr);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				ctrl_in.remove(j--, 1);
 | |
| 				fsm_data.num_inputs--;
 | |
| 
 | |
| 				fsm_data.transition_table.swap(new_transition_table);
 | |
| 				new_transition_table.clear();
 | |
| 			}
 | |
| 	}
 | |
| 
 | |
| 	void opt_feedback_inputs()
 | |
| 	{
 | |
| 		RTLIL::SigSpec &ctrl_in = cell->connections_[ID::CTRL_IN];
 | |
| 		RTLIL::SigSpec &ctrl_out = cell->connections_[ID::CTRL_OUT];
 | |
| 
 | |
| 		for (int j = 0; j < ctrl_out.size(); j++)
 | |
| 		for (int i = 0; i < ctrl_in.size(); i++)
 | |
| 			if (ctrl_in.extract(i, 1) == ctrl_out.extract(j, 1))
 | |
| 			{
 | |
| 				log("  Optimize handling of signal %s that is connected to input %d and output %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j);
 | |
| 				std::vector<FsmData::transition_t> new_transition_table;
 | |
| 
 | |
| 				for (auto tr : fsm_data.transition_table)
 | |
| 				{
 | |
| 					RTLIL::State si = tr.ctrl_in[i];
 | |
| 					RTLIL::State sj = tr.ctrl_out[j];
 | |
| 
 | |
| 					if (si > RTLIL::State::S1 || si == sj) {
 | |
| 						RTLIL::SigSpec tmp(tr.ctrl_in);
 | |
| 						tmp.remove(i, 1);
 | |
| 						tr.ctrl_in = tmp.as_const();
 | |
| 						new_transition_table.push_back(tr);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				ctrl_in.remove(i--, 1);
 | |
| 				fsm_data.num_inputs--;
 | |
| 
 | |
| 				fsm_data.transition_table.swap(new_transition_table);
 | |
| 				new_transition_table.clear();
 | |
| 			}
 | |
| 	}
 | |
| 
 | |
| 	void opt_find_dont_care_worker(std::set<RTLIL::Const> &set, int bit, FsmData::transition_t &tr, bool &did_something)
 | |
| 	{
 | |
| 		std::set<RTLIL::Const> new_set;
 | |
| 
 | |
| 		for (auto &pattern : set)
 | |
| 		{
 | |
| 			if (pattern[bit] > RTLIL::State::S1) {
 | |
| 				new_set.insert(pattern);
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			RTLIL::Const other_pattern = pattern;
 | |
| 
 | |
| 			if (pattern[bit] == RTLIL::State::S1)
 | |
| 				other_pattern.set(bit, RTLIL::State::S0);
 | |
| 			else
 | |
| 				other_pattern.set(bit, RTLIL::State::S1);
 | |
| 
 | |
| 			if (set.count(other_pattern) > 0) {
 | |
| 				log("  Merging pattern %s and %s from group (%d %d %s).\n", log_signal(pattern), log_signal(other_pattern),
 | |
| 						tr.state_in, tr.state_out, log_signal(tr.ctrl_out));
 | |
| 				other_pattern.set(bit, RTLIL::State::Sa);
 | |
| 				new_set.insert(other_pattern);
 | |
| 				did_something = true;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			new_set.insert(pattern);
 | |
| 		}
 | |
| 
 | |
| 		set.swap(new_set);
 | |
| 	}
 | |
| 
 | |
| 	void opt_find_dont_care()
 | |
| 	{
 | |
| 		typedef std::pair<std::pair<int, int>, RTLIL::Const> group_t;
 | |
| 		std::map<group_t, std::set<RTLIL::Const>> transitions_by_group;
 | |
| 
 | |
| 		for (auto &tr : fsm_data.transition_table) {
 | |
| 			group_t group(std::pair<int, int>(tr.state_in, tr.state_out), tr.ctrl_out);
 | |
| 			transitions_by_group[group].insert(tr.ctrl_in);
 | |
| 		}
 | |
| 
 | |
| 		fsm_data.transition_table.clear();
 | |
| 		for (auto &it : transitions_by_group)
 | |
| 		{
 | |
| 			FsmData::transition_t tr;
 | |
| 			tr.state_in = it.first.first.first;
 | |
| 			tr.state_out = it.first.first.second;
 | |
| 			tr.ctrl_out = it.first.second;
 | |
| 
 | |
| 			bool did_something = true;
 | |
| 			while (did_something) {
 | |
| 				did_something = false;
 | |
| 				for (int i = 0; i < fsm_data.num_inputs; i++)
 | |
| 					opt_find_dont_care_worker(it.second, i, tr, did_something);
 | |
| 			}
 | |
| 
 | |
| 			for (auto &ci : it.second) {
 | |
| 				tr.ctrl_in = ci;
 | |
| 				fsm_data.transition_table.push_back(tr);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module)
 | |
| 	{
 | |
| 		log("Optimizing FSM `%s' from module `%s'.\n", cell->name, module->name);
 | |
| 
 | |
| 		fsm_data.copy_from_cell(cell);
 | |
| 		this->cell = cell;
 | |
| 		this->module = module;
 | |
| 
 | |
| 		opt_unreachable_states();
 | |
| 
 | |
| 		opt_unused_outputs();
 | |
| 
 | |
| 		opt_alias_inputs();
 | |
| 		opt_feedback_inputs();
 | |
| 		opt_find_dont_care();
 | |
| 
 | |
| 		opt_const_and_unused_inputs();
 | |
| 
 | |
| 		fsm_data.copy_to_cell(cell);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| PRIVATE_NAMESPACE_END
 | |
| 
 | |
| void YOSYS_NAMESPACE_PREFIX FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module)
 | |
| {
 | |
| 	FsmOpt fsmopt(cell, module);
 | |
| }
 | |
| 
 | |
| PRIVATE_NAMESPACE_BEGIN
 | |
| 
 | |
| struct FsmOptPass : public Pass {
 | |
| 	FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { }
 | |
| 	void help() override
 | |
| 	{
 | |
| 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | |
| 		log("\n");
 | |
| 		log("    fsm_opt [selection]\n");
 | |
| 		log("\n");
 | |
| 		log("This pass optimizes FSM cells. It detects which output signals are actually\n");
 | |
| 		log("not used and removes them from the FSM. This pass is usually used in\n");
 | |
| 		log("combination with the 'opt_clean' pass (see also 'help fsm').\n");
 | |
| 		log("\n");
 | |
| 	}
 | |
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) override
 | |
| 	{
 | |
| 		log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n");
 | |
| 		extra_args(args, 1, design);
 | |
| 
 | |
| 		for (auto mod : design->selected_modules())
 | |
| 			for (auto cell : mod->selected_cells())
 | |
| 				if (cell->type == ID($fsm))
 | |
| 					FsmData::optimize_fsm(cell, mod);
 | |
| 	}
 | |
| } FsmOptPass;
 | |
| 
 | |
| PRIVATE_NAMESPACE_END
 |