aboutsummaryrefslogtreecommitdiffstats
path: root/passes/fsm
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2013-01-05 11:13:26 +0100
committerClifford Wolf <clifford@clifford.at>2013-01-05 11:13:26 +0100
commit7764d0ba1dcf064ae487ee985c43083a0909e7f4 (patch)
tree18c05b8729df381af71b707748ce1d605e0df764 /passes/fsm
downloadyosys-7764d0ba1dcf064ae487ee985c43083a0909e7f4.tar.gz
yosys-7764d0ba1dcf064ae487ee985c43083a0909e7f4.tar.bz2
yosys-7764d0ba1dcf064ae487ee985c43083a0909e7f4.zip
initial import
Diffstat (limited to 'passes/fsm')
-rw-r--r--passes/fsm/Makefile.inc11
-rw-r--r--passes/fsm/fsm.cc91
-rw-r--r--passes/fsm/fsm_detect.cc160
-rw-r--r--passes/fsm/fsm_expand.cc255
-rw-r--r--passes/fsm/fsm_export.cc103
-rw-r--r--passes/fsm/fsm_extract.cc359
-rw-r--r--passes/fsm/fsm_info.cc46
-rw-r--r--passes/fsm/fsm_map.cc356
-rw-r--r--passes/fsm/fsm_opt.cc285
-rw-r--r--passes/fsm/fsm_recode.cc114
-rw-r--r--passes/fsm/fsmdata.h177
11 files changed, 1957 insertions, 0 deletions
diff --git a/passes/fsm/Makefile.inc b/passes/fsm/Makefile.inc
new file mode 100644
index 000000000..38623e49e
--- /dev/null
+++ b/passes/fsm/Makefile.inc
@@ -0,0 +1,11 @@
+
+OBJS += passes/fsm/fsm.o
+OBJS += passes/fsm/fsm_detect.o
+OBJS += passes/fsm/fsm_extract.o
+OBJS += passes/fsm/fsm_opt.o
+OBJS += passes/fsm/fsm_expand.o
+OBJS += passes/fsm/fsm_recode.o
+OBJS += passes/fsm/fsm_info.o
+OBJS += passes/fsm/fsm_export.o
+OBJS += passes/fsm/fsm_map.o
+
diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc
new file mode 100644
index 000000000..61322fbd3
--- /dev/null
+++ b/passes/fsm/fsm.cc
@@ -0,0 +1,91 @@
+/*
+ * 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/register.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+struct FsmPass : public Pass {
+ FsmPass() : Pass("fsm") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_nomap = false;
+ bool flag_norecode = false;
+ bool flag_expand = false;
+ bool flag_export = false;
+ std::string fm_set_fsm_file_opt;
+
+ log_header("Executing FSM pass (extract and optimize FSM).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file_opt.empty()) {
+ fm_set_fsm_file_opt = " -fm_set_fsm_file " + args[++argidx];
+ continue;
+ }
+ if (arg == "-norecode") {
+ flag_norecode = true;
+ continue;
+ }
+ if (arg == "-nomap") {
+ flag_nomap = true;
+ continue;
+ }
+ if (arg == "-expand") {
+ flag_expand = true;
+ continue;
+ }
+ if (arg == "-export") {
+ flag_export = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ Pass::call(design, "fsm_detect");
+ Pass::call(design, "fsm_extract");
+
+ Pass::call(design, "fsm_opt");
+ Pass::call(design, "opt_rmunused");
+ Pass::call(design, "fsm_opt");
+
+ if (flag_expand) {
+ Pass::call(design, "fsm_expand");
+ Pass::call(design, "opt_rmunused");
+ Pass::call(design, "fsm_opt");
+ }
+
+ if (!flag_norecode)
+ Pass::call(design, "fsm_recode" + fm_set_fsm_file_opt);
+ Pass::call(design, "fsm_info");
+
+ if (!flag_nomap)
+ Pass::call(design, "fsm_map");
+
+ if (flag_export)
+ Pass::call(design, "fsm_export");
+
+ log_pop();
+ }
+} FsmPass;
+
diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc
new file mode 100644
index 000000000..067ed171f
--- /dev/null
+++ b/passes/fsm/fsm_detect.cc
@@ -0,0 +1,160 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+
+static RTLIL::Module *module;
+static SigMap assign_map;
+typedef std::pair<RTLIL::Cell*,std::string> sig2driver_entry_t;
+static SigSet<sig2driver_entry_t> sig2driver, sig2user;
+static std::set<RTLIL::Cell*> muxtree_cells;
+static SigPool sig_at_port;
+
+static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig)
+{
+ if (sig_at_port.check_any(assign_map(sig)))
+ return false;
+
+ if (sig.is_fully_const() || old_sig == sig)
+ return true;
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(sig, cellport_list);
+ for (auto &cellport : cellport_list) {
+ if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux" && cellport.first->type != "$safe_pmux") || cellport.second != "\\Y")
+ return false;
+ RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]);
+ RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]);
+ if (!check_state_mux_tree(old_sig, sig_a))
+ return false;
+ for (int i = 0; i < sig_b.width; i += sig_a.width)
+ if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.width)))
+ return false;
+ muxtree_cells.insert(cellport.first);
+ }
+
+ return true;
+}
+
+static bool check_state_users(RTLIL::SigSpec sig)
+{
+ if (sig_at_port.check_any(assign_map(sig)))
+ return false;
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2user.find(sig, cellport_list);
+ for (auto &cellport : cellport_list) {
+ RTLIL::Cell *cell = cellport.first;
+ if (muxtree_cells.count(cell) > 0)
+ continue;
+ if (cellport.second != "\\A" && cellport.second != "\\B")
+ return false;
+ if (cell->connections.count("\\A") == 0 || cell->connections.count("\\B") == 0 || cell->connections.count("\\Y") == 0)
+ return false;
+ for (auto &port_it : cell->connections)
+ if (port_it.first != "\\A" && port_it.first != "\\B" && port_it.first != "\\Y")
+ return false;
+ if (assign_map(cell->connections["\\A"]) == sig && cell->connections["\\B"].is_fully_const())
+ continue;
+ if (assign_map(cell->connections["\\B"]) == sig && cell->connections["\\A"].is_fully_const())
+ continue;
+ return false;
+ }
+
+ return true;
+}
+
+static void detect_fsm(RTLIL::Wire *wire)
+{
+ if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1)
+ return;
+ if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire))))
+ return;
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(RTLIL::SigSpec(wire), cellport_list);
+ for (auto &cellport : cellport_list) {
+ if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q")
+ continue;
+ muxtree_cells.clear();
+ RTLIL::SigSpec sig_q = assign_map(cellport.first->connections["\\Q"]);
+ RTLIL::SigSpec sig_d = assign_map(cellport.first->connections["\\D"]);
+ if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d) && check_state_users(sig_q)) {
+ log("Found FSM state register %s in module %s.\n", wire->name.c_str(), module->name.c_str());
+ wire->attributes["\\fsm_encoding"] = RTLIL::Const("auto");
+ return;
+ }
+ }
+}
+
+struct FsmDetectPass : public Pass {
+ FsmDetectPass() : Pass("fsm_detect") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing FSM_DETECT pass (finding FSMs in design).\n");
+ extra_args(args, 1, design);
+
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_internals_mem();
+ ct.setup_stdcells();
+ ct.setup_stdcells_mem();
+
+ for (auto &mod_it : design->modules)
+ {
+ module = mod_it.second;
+ assign_map.set(module);
+
+ sig2driver.clear();
+ sig2user.clear();
+ sig_at_port.clear();
+ for (auto &cell_it : module->cells)
+ for (auto &conn_it : cell_it.second->connections) {
+ if (ct.cell_output(cell_it.second->type, conn_it.first)) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2driver.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
+ }
+ if (!ct.cell_known(cell_it.second->type) || ct.cell_input(cell_it.second->type, conn_it.first)) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2user.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
+ }
+ }
+
+ for (auto &wire_it : module->wires)
+ if (wire_it.second->port_id != 0)
+ sig_at_port.add(assign_map(RTLIL::SigSpec(wire_it.second)));
+
+ for (auto &wire_it : module->wires)
+ detect_fsm(wire_it.second);
+ }
+
+ assign_map.clear();
+ sig2driver.clear();
+ sig2user.clear();
+ muxtree_cells.clear();
+ }
+} FsmDetectPass;
+
diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc
new file mode 100644
index 000000000..0d5bc7695
--- /dev/null
+++ b/passes/fsm/fsm_expand.cc
@@ -0,0 +1,255 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+struct FsmExpand
+{
+ RTLIL::Module *module;
+ RTLIL::Cell *fsm_cell;
+ SigMap assign_map;
+ SigSet<RTLIL::Cell*> sig2driver, sig2user;
+ CellTypes ct;
+
+ std::set<RTLIL::Cell*> merged_set;
+ std::set<RTLIL::Cell*> current_set;
+ std::set<RTLIL::Cell*> no_candidate_set;
+
+ bool already_optimized;
+ int limit_transitions;
+
+ bool is_cell_merge_candidate(RTLIL::Cell *cell)
+ {
+ RTLIL::SigSpec new_signals;
+ if (cell->connections.count("\\A") > 0)
+ new_signals.append(assign_map(cell->connections["\\A"]));
+ if (cell->connections.count("\\B") > 0)
+ new_signals.append(assign_map(cell->connections["\\B"]));
+ if (cell->connections.count("\\S") > 0)
+ new_signals.append(assign_map(cell->connections["\\S"]));
+
+ new_signals.sort_and_unify();
+ new_signals.remove_const();
+
+ if (new_signals.width > 4)
+ return false;
+
+ new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"]));
+ new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"]));
+
+ if (cell->connections.count("\\Y") > 0) {
+ new_signals.append(assign_map(cell->connections["\\Y"]));
+ new_signals.sort_and_unify();
+ new_signals.remove_const();
+ new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"]));
+ new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"]));
+ }
+
+ if (new_signals.width > 2)
+ return false;
+
+ return true;
+ }
+
+ void create_current_set()
+ {
+ std::vector<RTLIL::Cell*> cell_list;
+
+ for (auto c : sig2driver.find(assign_map(fsm_cell->connections["\\CTRL_IN"])))
+ cell_list.push_back(c);
+
+ for (auto c : sig2user.find(assign_map(fsm_cell->connections["\\CTRL_OUT"])))
+ cell_list.push_back(c);
+
+ current_set.clear();
+ for (auto c : cell_list)
+ {
+ if (merged_set.count(c) > 0 || current_set.count(c) > 0 || no_candidate_set.count(c) > 0)
+ continue;
+ for (auto &p : c->connections) {
+ if (p.first != "\\A" && p.first != "\\B" && p.first != "\\S" && p.first != "\\Y")
+ goto next_cell;
+ }
+ if (!is_cell_merge_candidate(c)) {
+ no_candidate_set.insert(c);
+ continue;
+ }
+ current_set.insert(c);
+ next_cell:;
+ }
+ }
+
+ void optimze_as_needed()
+ {
+ if (already_optimized)
+ return;
+
+ int trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int();
+ if (trans_num > limit_transitions)
+ {
+ log(" grown transition table to %d entries -> optimize.\n", trans_num);
+ FsmData::optimize_fsm(fsm_cell, module);
+ already_optimized = true;
+
+ trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int();
+ log(" transition table size after optimizaton: %d\n", trans_num);
+ limit_transitions = 16 * trans_num;
+ }
+ }
+
+ void merge_cell_into_fsm(RTLIL::Cell *cell)
+ {
+ optimze_as_needed();
+
+ log(" merging %s cell %s.\n", cell->type.c_str(), cell->name.c_str());
+ merged_set.insert(cell);
+ already_optimized = false;
+
+ RTLIL::SigSpec input_sig, output_sig;
+
+ for (auto &p : cell->connections)
+ if (ct.cell_output(cell->type, p.first))
+ output_sig.append(assign_map(p.second));
+ else
+ input_sig.append(assign_map(p.second));
+ input_sig.sort_and_unify();
+ input_sig.remove_const();
+
+ assert(input_sig.width <= 4);
+ std::vector<RTLIL::Const> truth_tab;
+
+ for (int i = 0; i < (1 << input_sig.width); i++) {
+ RTLIL::Const in_val(i, input_sig.width);
+ RTLIL::SigSpec A, B, S;
+ if (cell->connections.count("\\A") > 0)
+ A = assign_map(cell->connections["\\A"]);
+ if (cell->connections.count("\\B") > 0)
+ B = assign_map(cell->connections["\\B"]);
+ if (cell->connections.count("\\S") > 0)
+ S = assign_map(cell->connections["\\S"]);
+ A.replace(input_sig, RTLIL::SigSpec(in_val));
+ B.replace(input_sig, RTLIL::SigSpec(in_val));
+ S.replace(input_sig, RTLIL::SigSpec(in_val));
+ assert(A.is_fully_const());
+ assert(B.is_fully_const());
+ assert(S.is_fully_const());
+ truth_tab.push_back(ct.eval(cell, A.as_const(), B.as_const(), S.as_const()));
+ }
+
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(fsm_cell);
+
+ fsm_data.num_inputs += input_sig.width;
+ fsm_cell->connections["\\CTRL_IN"].append(input_sig);
+
+ fsm_data.num_outputs += output_sig.width;
+ fsm_cell->connections["\\CTRL_OUT"].append(output_sig);
+
+ std::vector<FsmData::transition_t> new_transition_table;
+ for (auto &tr : fsm_data.transition_table) {
+ for (int i = 0; i < (1 << input_sig.width); i++) {
+ FsmData::transition_t new_tr = tr;
+ RTLIL::Const in_val(i, input_sig.width);
+ RTLIL::Const out_val = truth_tab[i];
+ RTLIL::SigSpec ctrl_in = new_tr.ctrl_in;
+ RTLIL::SigSpec ctrl_out = new_tr.ctrl_out;
+ ctrl_in.append(in_val);
+ ctrl_out.append(out_val);
+ new_tr.ctrl_in = ctrl_in.as_const();
+ new_tr.ctrl_out = ctrl_out.as_const();
+ new_transition_table.push_back(new_tr);
+ }
+ }
+ fsm_data.transition_table.swap(new_transition_table);
+ new_transition_table.clear();
+
+ fsm_data.copy_to_cell(fsm_cell);
+ }
+
+ FsmExpand(RTLIL::Cell *cell, RTLIL::Module *mod)
+ {
+ module = mod;
+ fsm_cell = cell;
+
+ assign_map.set(module);
+ ct.setup_internals();
+
+ for (auto &cell_it : module->cells) {
+ RTLIL::Cell *c = cell_it.second;
+ if (ct.cell_known(c->type))
+ for (auto &p : c->connections) {
+ if (ct.cell_output(c->type, p.first))
+ sig2driver.insert(assign_map(p.second), c);
+ else
+ sig2user.insert(assign_map(p.second), c);
+ }
+ }
+ }
+
+ void execute()
+ {
+ log("\n");
+ log("Expanding FSM `%s' from module `%s':\n", fsm_cell->name.c_str(), module->name.c_str());
+
+ already_optimized = false;
+ limit_transitions = 16 * fsm_cell->parameters["\\TRANS_NUM"].as_int();
+
+ for (create_current_set(); current_set.size() > 0; create_current_set()) {
+ for (auto c : current_set)
+ merge_cell_into_fsm(c);
+ }
+
+ for (auto c : merged_set) {
+ module->cells.erase(c->name);
+ delete c;
+ }
+
+ if (merged_set.size() > 0 && !already_optimized)
+ FsmData::optimize_fsm(fsm_cell, module);
+
+ log(" merged %zd cells into FSM.\n", merged_set.size());
+ }
+};
+
+struct FsmExpandPass : public Pass {
+ FsmExpandPass() : Pass("fsm_expand") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing FSM_EXPAND pass (re-assigning FSM state encoding).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules) {
+ std::vector<RTLIL::Cell*> fsm_cells;
+ for (auto &cell_it : mod_it.second->cells)
+ if (cell_it.second->type == "$fsm")
+ fsm_cells.push_back(cell_it.second);
+ for (auto c : fsm_cells) {
+ FsmExpand fsm_expand(c, mod_it.second);
+ fsm_expand.execute();
+ }
+ }
+ }
+} FsmExpandPass;
+
diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc
new file mode 100644
index 000000000..0219f4ebd
--- /dev/null
+++ b/passes/fsm/fsm_export.cc
@@ -0,0 +1,103 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string>
+#include <iostream>
+#include <fstream>
+
+/**
+ * Convert signal into a KISS-compatible textual representation.
+ */
+std::string kiss_convert_signal(const RTLIL::SigSpec &sig) {
+ if (!sig.is_fully_const()) {
+ throw 0;
+ }
+
+ return sig.as_const().as_string();
+}
+
+/**
+ * Exports each Finite State Machine (FSM) in the design to a file in KISS2 format.
+ */
+struct FsmExportPass : public Pass {
+ FsmExportPass() : Pass("fsm_export") {
+ }
+
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ FsmData fsm_data;
+ std::string kiss_name;
+ std::ofstream kiss_file;
+ size_t i;
+ FsmData::transition_t tr;
+
+ log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules)
+ for (auto &cell_it : mod_it.second->cells)
+ if (cell_it.second->type == "$fsm") {
+ kiss_name.assign(mod_it.first.c_str());
+ kiss_name.append("-" + cell_it.second->name + ".kiss2");
+ fsm_data.copy_from_cell(cell_it.second);
+
+ log("\n");
+ log("Exporting FSM `%s' from module `%s' to file `%s'.\n",
+ cell_it.second->name.c_str(),
+ mod_it.first.c_str(),
+ kiss_name.c_str());
+
+ kiss_file.open(kiss_name, std::ios::out | std::ios::trunc);
+
+ if (!kiss_file.is_open()) {
+ log_error("Could not open file \"%s\" with write access.\n", kiss_name.c_str());
+ return;
+ }
+
+ kiss_file << ".start_kiss" << std::endl;
+ kiss_file << ".i " << std::dec << fsm_data.num_inputs << std::endl;
+ kiss_file << ".o " << std::dec << fsm_data.num_outputs << std::endl;
+ kiss_file << ".r s" << std::dec << fsm_data.reset_state << std::endl;
+
+ for (i = 0; i < fsm_data.transition_table.size(); i++) {
+ tr = fsm_data.transition_table[i];
+
+ try {
+ kiss_file << kiss_convert_signal(tr.ctrl_in) << ' ';
+ kiss_file << 's' << tr.state_in << ' ';
+ kiss_file << 's' << tr.state_out << ' ';
+ kiss_file << kiss_convert_signal(tr.ctrl_out) << std::endl;
+ }
+ catch (int) {
+ log_error("exporting an FSM input or output signal failed.\n");
+ }
+ }
+
+ kiss_file << ".end_kiss" << std::endl << ".end" << std::endl;
+ kiss_file.close();
+ }
+ }
+} FsmExportPass;
diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc
new file mode 100644
index 000000000..bdcb1d45a
--- /dev/null
+++ b/passes/fsm/fsm_extract.cc
@@ -0,0 +1,359 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+
+static RTLIL::Module *module;
+static SigMap assign_map;
+typedef std::pair<RTLIL::Cell*,std::string> sig2driver_entry_t;
+static SigSet<sig2driver_entry_t> sig2driver, sig2trigger;
+
+static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL::SigSpec &ctrl, std::map<RTLIL::Const, int> &states, RTLIL::Const *reset_state = NULL)
+{
+ sig.extend(dff_out.width, false);
+
+ if (sig == dff_out)
+ return true;
+
+ assign_map.apply(sig);
+ if (sig.is_fully_const()) {
+ sig.optimize();
+ assert(sig.chunks.size() == 1);
+ if (states.count(sig.chunks[0].data) == 0) {
+ log(" found state code: %s\n", log_signal(sig));
+ states[sig.chunks[0].data] = -1;
+ }
+ return true;
+ }
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(sig, cellport_list);
+ for (auto &cellport : cellport_list) {
+ if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux" && cellport.first->type != "$safe_pmux") || cellport.second != "\\Y") {
+ log(" unexpected cell type %s (%s) found in state selection tree.\n",
+ cellport.first->type.c_str(), cellport.first->name.c_str());
+ return false;
+ }
+ RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]);
+ RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]);
+ RTLIL::SigSpec sig_s = assign_map(cellport.first->connections["\\S"]);
+ if (reset_state && RTLIL::SigSpec(*reset_state).is_fully_undef())
+ do {
+ if (sig_a.is_fully_def())
+ *reset_state = sig_a.as_const();
+ else if (sig_b.is_fully_def())
+ *reset_state = sig_b.as_const();
+ else
+ break;
+ log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state));
+ } while (0);
+ if (ctrl.extract(sig_s).width == 0) {
+ log(" found ctrl input: %s\n", log_signal(sig_s));
+ ctrl.append(sig_s);
+ }
+ if (!find_states(sig_a, dff_out, ctrl, states))
+ return false;
+ for (int i = 0; i < sig_b.width/sig_a.width; i++) {
+ if (!find_states(sig_b.extract(i*sig_a.width, sig_a.width), dff_out, ctrl, states))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static RTLIL::Const sig2const(ConstEval &ce, RTLIL::SigSpec sig, RTLIL::State noconst_state, RTLIL::SigSpec dont_care = RTLIL::SigSpec())
+{
+ if (dont_care.width > 0) {
+ sig.expand();
+ for (auto &chunk : sig.chunks) {
+ assert(chunk.width == 1);
+ if (dont_care.extract(chunk).width > 0)
+ chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state);
+ }
+ sig.optimize();
+ }
+
+ ce.assign_map.apply(sig);
+ ce.values_map.apply(sig);
+
+ sig.expand();
+ for (auto &chunk : sig.chunks) {
+ assert(chunk.width == 1);
+ if (chunk.wire != NULL)
+ chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state);
+ }
+ sig.optimize();
+
+ if (sig.width == 0)
+ return RTLIL::Const();
+ assert(sig.chunks.size() == 1 && sig.chunks[0].wire == NULL);
+ return sig.chunks[0].data;
+}
+
+static void find_transitions(ConstEval &ce, ConstEval &ce_nostop, FsmData &fsm_data, std::map<RTLIL::Const, int> &states, int state_in, RTLIL::SigSpec ctrl_in, RTLIL::SigSpec ctrl_out, RTLIL::SigSpec dff_in, RTLIL::SigSpec dont_care)
+{
+ RTLIL::SigSpec undef, constval;
+
+ if (ce.eval(ctrl_out, undef) && ce.eval(dff_in, undef)) {
+ assert(ctrl_out.is_fully_const() && dff_in.is_fully_const());
+ FsmData::transition_t tr;
+ tr.state_in = state_in;
+ tr.state_out = states[ce.values_map(ce.assign_map(dff_in)).as_const()];
+ tr.ctrl_in = sig2const(ce, ctrl_in, RTLIL::State::Sa, dont_care);
+ tr.ctrl_out = sig2const(ce, ctrl_out, RTLIL::State::Sx);
+ RTLIL::Const log_state_in = RTLIL::Const(RTLIL::State::Sx, fsm_data.state_bits);
+ if (state_in >= 0)
+ log_state_in = fsm_data.state_table[tr.state_in];
+ if (dff_in.is_fully_def()) {
+ fsm_data.transition_table.push_back(tr);
+ log(" transition: %10s %s -> %10s %s\n",
+ log_signal(log_state_in), log_signal(tr.ctrl_in),
+ log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
+ } else {
+ log(" transition: %10s %s -> %10s %s <ignored undef transistion!>\n",
+ log_signal(log_state_in), log_signal(tr.ctrl_in),
+ log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
+ }
+ return;
+ }
+
+ assert(undef.width > 0);
+ assert(ce.stop_signals.check_all(undef));
+
+ undef = undef.extract(0, 1);
+ constval = undef;
+
+ if (ce_nostop.eval(constval))
+ {
+ ce.push();
+ dont_care.append(undef);
+ ce.set(undef, constval.as_const());
+ find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
+ ce.pop();
+ }
+ else
+ {
+ ce.push(), ce_nostop.push();
+ ce.set(undef, RTLIL::Const(0, 1));
+ ce_nostop.set(undef, RTLIL::Const(0, 1));
+ find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
+ ce.pop(), ce_nostop.pop();
+
+ ce.push(), ce_nostop.push();
+ ce.set(undef, RTLIL::Const(1, 1));
+ ce_nostop.set(undef, RTLIL::Const(1, 1));
+ find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
+ ce.pop(), ce_nostop.pop();
+ }
+}
+
+static void extract_fsm(RTLIL::Wire *wire)
+{
+ log("Extracting FSM `%s' from module `%s'.\n", wire->name.c_str(), module->name.c_str());
+
+ // get input and output signals for state ff
+
+ RTLIL::SigSpec dff_out = assign_map(RTLIL::SigSpec(wire));
+ RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width);
+ RTLIL::Const reset_state(RTLIL::State::Sx, wire->width);
+
+ RTLIL::SigSpec clk = RTLIL::SigSpec(0, 1);
+ RTLIL::SigSpec arst = RTLIL::SigSpec(0, 1);
+ bool clk_polarity = true;
+ bool arst_polarity = true;
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(dff_out, cellport_list);
+ for (auto &cellport : cellport_list) {
+ if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q")
+ continue;
+ log(" found %s cell for state register: %s\n", cellport.first->type.c_str(), cellport.first->name.c_str());
+ RTLIL::SigSpec sig_q = assign_map(cellport.first->connections["\\Q"]);
+ RTLIL::SigSpec sig_d = assign_map(cellport.first->connections["\\D"]);
+ clk = cellport.first->connections["\\CLK"];
+ clk_polarity = cellport.first->parameters["\\CLK_POLARITY"].as_bool();
+ if (cellport.first->type == "$adff") {
+ arst = cellport.first->connections["\\ARST"];
+ arst_polarity = cellport.first->parameters["\\ARST_POLARITY"].as_bool();
+ reset_state = cellport.first->parameters["\\ARST_VALUE"];
+ }
+ sig_q.replace(dff_out, sig_d, &dff_in);
+ break;
+ }
+
+ log(" root of input selection tree: %s\n", log_signal(dff_in));
+ if (dff_in.has_marked_bits()) {
+ log(" fsm extraction failed: incomplete input selection tree root.\n");
+ return;
+ }
+
+ // find states and control inputs
+
+ RTLIL::SigSpec ctrl_in;
+ std::map<RTLIL::Const, int> states;
+ if (!arst.is_fully_const()) {
+ log(" found reset state: %s (from async reset)\n", log_signal(reset_state));
+ states[reset_state] = -1;
+ }
+ if (!find_states(dff_in, dff_out, ctrl_in, states, &reset_state)) {
+ log(" fsm extraction failed: state selection tree is not closed.\n");
+ return;
+ }
+
+ // find control outputs
+ // (add the state signals to the list of control outputs. if everything goes right, this signals
+ // become unused and can then be removed from the fsm control output)
+
+ RTLIL::SigSpec ctrl_out = dff_in;
+ cellport_list.clear();
+ sig2trigger.find(dff_out, cellport_list);
+ for (auto &cellport : cellport_list) {
+ RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]);
+ RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]);
+ RTLIL::SigSpec sig_y = assign_map(cellport.first->connections["\\Y"]);
+ if (cellport.second == "\\A" && !sig_b.is_fully_const())
+ continue;
+ if (cellport.second == "\\B" && !sig_a.is_fully_const())
+ continue;
+ log(" found ctrl output: %s\n", log_signal(sig_y));
+ ctrl_out.append(sig_y);
+ }
+ ctrl_in.remove(ctrl_out);
+
+ log(" ctrl inputs: %s\n", log_signal(ctrl_in));
+ log(" ctrl outputs: %s\n", log_signal(ctrl_out));
+
+ // Initialize fsm data struct
+
+ FsmData fsm_data;
+ fsm_data.num_inputs = ctrl_in.width;
+ fsm_data.num_outputs = ctrl_out.width;
+ fsm_data.state_bits = wire->width;
+ fsm_data.reset_state = -1;
+ for (auto &it : states) {
+ it.second = fsm_data.state_table.size();
+ fsm_data.state_table.push_back(it.first);
+ }
+ if (!arst.is_fully_const() || RTLIL::SigSpec(reset_state).is_fully_def())
+ fsm_data.reset_state = states[reset_state];
+
+ // Create transition table
+
+ ConstEval ce(module), ce_nostop(module);
+ ce.stop(ctrl_in);
+ for (int state_idx = 0; state_idx < int(fsm_data.state_table.size()); state_idx++) {
+ ce.push(), ce_nostop.push();
+ ce.set(dff_out, fsm_data.state_table[state_idx]);
+ ce_nostop.set(dff_out, fsm_data.state_table[state_idx]);
+ find_transitions(ce, ce_nostop, fsm_data, states, state_idx, ctrl_in, ctrl_out, dff_in, RTLIL::SigSpec());
+ ce.pop(), ce_nostop.pop();
+ }
+
+ // create fsm cell
+
+ RTLIL::Cell *fsm_cell = new RTLIL::Cell;
+ fsm_cell->name = stringf("$fsm$%s$%d", wire->name.c_str(), RTLIL::autoidx++);
+ fsm_cell->type = "$fsm";
+ fsm_cell->connections["\\CLK"] = clk;
+ fsm_cell->connections["\\ARST"] = arst;
+ fsm_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity ? 1 : 0, 1);
+ fsm_cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity ? 1 : 0, 1);
+ fsm_cell->connections["\\CTRL_IN"] = ctrl_in;
+ fsm_cell->connections["\\CTRL_OUT"] = ctrl_out;
+ fsm_cell->parameters["\\NAME"] = RTLIL::Const(wire->name);
+ fsm_data.copy_to_cell(fsm_cell);
+ module->cells[fsm_cell->name] = fsm_cell;
+
+ // rename original state wire
+
+ module->wires.erase(wire->name);
+ wire->attributes.erase("\\fsm_encoding");
+ wire->name = stringf("$fsm$oldstate%s", wire->name.c_str());
+ module->wires[wire->name] = wire;
+
+ // unconnect control outputs from old drivers
+
+ cellport_list.clear();
+ sig2driver.find(ctrl_out, cellport_list);
+ for (auto &cellport : cellport_list) {
+ RTLIL::SigSpec port_sig = assign_map(cellport.first->connections[cellport.second]);
+ RTLIL::SigSpec unconn_sig = port_sig.extract(ctrl_out);
+ RTLIL::Wire *unconn_wire = new RTLIL::Wire;
+ unconn_wire->name = stringf("$fsm_unconnect$%s$%d", log_signal(unconn_sig), RTLIL::autoidx++);
+ unconn_wire->width = unconn_sig.width;
+ module->wires[unconn_wire->name] = unconn_wire;
+ port_sig.replace(unconn_sig, RTLIL::SigSpec(unconn_wire), &cellport.first->connections[cellport.second]);
+ }
+}
+
+struct FsmExtractPass : public Pass {
+ FsmExtractPass() : Pass("fsm_extract") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing FSM_EXTRACT pass (extracting FSM from design).\n");
+ extra_args(args, 1, design);
+
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_internals_mem();
+ ct.setup_stdcells();
+ ct.setup_stdcells_mem();
+
+ for (auto &mod_it : design->modules)
+ {
+ module = mod_it.second;
+ assign_map.set(module);
+
+ sig2driver.clear();
+ sig2trigger.clear();
+ for (auto &cell_it : module->cells)
+ for (auto &conn_it : cell_it.second->connections) {
+ if (ct.cell_output(cell_it.second->type, conn_it.first)) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2driver.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
+ }
+ if (ct.cell_input(cell_it.second->type, conn_it.first) && cell_it.second->connections.count("\\Y") > 0 &&
+ cell_it.second->connections["\\Y"].width == 1 && (conn_it.first == "\\A" || conn_it.first == "\\B")) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2trigger.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
+ }
+ }
+
+ std::vector<RTLIL::Wire*> wire_list;
+ for (auto &wire_it : module->wires)
+ if (wire_it.second->attributes.count("\\fsm_encoding") > 0 && wire_it.second->attributes["\\fsm_encoding"].str != "none")
+ wire_list.push_back(wire_it.second);
+ for (auto wire : wire_list)
+ extract_fsm(wire);
+ }
+
+ assign_map.clear();
+ sig2driver.clear();
+ sig2trigger.clear();
+ }
+} FsmExtractPass;
+
diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc
new file mode 100644
index 000000000..83f065760
--- /dev/null
+++ b/passes/fsm/fsm_info.cc
@@ -0,0 +1,46 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+struct FsmInfoPass : public Pass {
+ FsmInfoPass() : Pass("fsm_info") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules)
+ for (auto &cell_it : mod_it.second->cells)
+ if (cell_it.second->type == "$fsm") {
+ log("\n");
+ log("FSM `%s' from module `%s':\n", cell_it.second->name.c_str(), mod_it.first.c_str());
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(cell_it.second);
+ fsm_data.log_info(cell_it.second);
+ }
+ }
+} FsmInfoPass;
+
diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc
new file mode 100644
index 000000000..4319dc044
--- /dev/null
+++ b/passes/fsm/fsm_map.cc
@@ -0,0 +1,356 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const, std::set<int>> &pattern_cache, std::set<int> &fullstate_cache, int num_states, RTLIL::Wire *state_onehot, RTLIL::SigSpec &ctrl_in, RTLIL::SigSpec output)
+{
+ RTLIL::SigSpec cases_vector;
+
+ for (int in_state : fullstate_cache)
+ cases_vector.append(RTLIL::SigSpec(state_onehot, 1, in_state));
+
+ for (auto &it : pattern_cache)
+ {
+ RTLIL::Const pattern = it.first;
+ RTLIL::SigSpec eq_sig_a, eq_sig_b, or_sig;
+
+ for (size_t j = 0; j < pattern.bits.size(); j++)
+ if (pattern.bits[j] == RTLIL::State::S0 || pattern.bits[j] == RTLIL::State::S1) {
+ eq_sig_a.append(ctrl_in.extract(j, 1));
+ eq_sig_b.append(RTLIL::SigSpec(pattern.bits[j]));
+ }
+ eq_sig_a.optimize();
+ eq_sig_b.optimize();
+
+ for (int in_state : it.second)
+ if (fullstate_cache.count(in_state) == 0)
+ or_sig.append(RTLIL::SigSpec(state_onehot, 1, in_state));
+ or_sig.optimize();
+
+ if (or_sig.width == 0)
+ continue;
+
+ RTLIL::SigSpec and_sig;
+
+ if (eq_sig_a.width > 0)
+ {
+ RTLIL::Wire *eq_wire = new RTLIL::Wire;
+ eq_wire->name = NEW_ID;
+ module->add(eq_wire);
+
+ RTLIL::Cell *eq_cell = new RTLIL::Cell;
+ eq_cell->name = NEW_ID;
+ eq_cell->type = "$eq";
+ eq_cell->connections["\\A"] = eq_sig_a;
+ eq_cell->connections["\\B"] = eq_sig_b;
+ eq_cell->connections["\\Y"] = RTLIL::SigSpec(eq_wire);
+ eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(eq_sig_a.width);
+ eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(eq_sig_b.width);
+ eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ module->add(eq_cell);
+
+ and_sig.append(RTLIL::SigSpec(eq_wire));
+ }
+
+ if (or_sig.width == 1)
+ {
+ and_sig.append(or_sig);
+ }
+ else if (or_sig.width < num_states && int(it.second.size()) < num_states)
+ {
+ RTLIL::Wire *or_wire = new RTLIL::Wire;
+ or_wire->name = NEW_ID;
+ module->add(or_wire);
+
+ RTLIL::Cell *or_cell = new RTLIL::Cell;
+ or_cell->name = NEW_ID;
+ or_cell->type = "$reduce_or";
+ or_cell->connections["\\A"] = or_sig;
+ or_cell->connections["\\Y"] = RTLIL::SigSpec(or_wire);
+ or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(or_sig.width);
+ or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ module->add(or_cell);
+
+ and_sig.append(RTLIL::SigSpec(or_wire));
+ }
+
+ switch (and_sig.width)
+ {
+ case 2:
+ {
+ RTLIL::Wire *and_wire = new RTLIL::Wire;
+ and_wire->name = NEW_ID;
+ module->add(and_wire);
+
+ RTLIL::Cell *and_cell = new RTLIL::Cell;
+ and_cell->name = NEW_ID;
+ and_cell->type = "$and";
+ and_cell->connections["\\A"] = and_sig.extract(0, 1);
+ and_cell->connections["\\B"] = and_sig.extract(1, 1);
+ and_cell->connections["\\Y"] = RTLIL::SigSpec(and_wire);
+ and_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ and_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
+ and_cell->parameters["\\A_WIDTH"] = RTLIL::Const(1);
+ and_cell->parameters["\\B_WIDTH"] = RTLIL::Const(1);
+ and_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ module->add(and_cell);
+
+ cases_vector.append(RTLIL::SigSpec(and_wire));
+ break;
+ }
+ case 1:
+ cases_vector.append(and_sig);
+ break;
+ case 0:
+ cases_vector.append(RTLIL::SigSpec(1, 1));
+ break;
+ default:
+ assert(!"This should never happen!");
+ }
+ }
+
+ if (cases_vector.width > 1) {
+ RTLIL::Cell *or_cell = new RTLIL::Cell;
+ or_cell->name = NEW_ID;
+ or_cell->type = "$reduce_or";
+ or_cell->connections["\\A"] = cases_vector;
+ or_cell->connections["\\Y"] = output;
+ or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cases_vector.width);
+ or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ module->add(or_cell);
+ } else if (cases_vector.width == 1) {
+ module->connections.push_back(RTLIL::SigSig(output, cases_vector));
+ } else {
+ module->connections.push_back(RTLIL::SigSig(output, RTLIL::SigSpec(0, 1)));
+ }
+}
+
+static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module)
+{
+ log("Mapping FSM `%s' from module `%s'.\n", fsm_cell->name.c_str(), module->name.c_str());
+
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(fsm_cell);
+
+ RTLIL::SigSpec ctrl_in = fsm_cell->connections["\\CTRL_IN"];
+ RTLIL::SigSpec ctrl_out = fsm_cell->connections["\\CTRL_OUT"];
+
+ // create state register
+
+ RTLIL::Wire *state_wire = new RTLIL::Wire;
+ state_wire->name = fsm_cell->parameters["\\NAME"].str;
+ while (module->count_id(state_wire->name) > 0)
+ state_wire->name += "_";
+ state_wire->width = fsm_data.state_bits;
+ module->add(state_wire);
+
+ RTLIL::Wire *next_state_wire = new RTLIL::Wire;
+ next_state_wire->name = NEW_ID;
+ next_state_wire->width = fsm_data.state_bits;
+ module->add(next_state_wire);
+
+ RTLIL::Cell *state_dff = new RTLIL::Cell;
+ state_dff->name = NEW_ID;
+ if (fsm_cell->connections["\\ARST"].is_fully_const()) {
+ state_dff->type = "$dff";
+ } else {
+ state_dff->type = "$adff";
+ state_dff->parameters["\\ARST_POLARITY"] = fsm_cell->parameters["\\ARST_POLARITY"];
+ state_dff->parameters["\\ARST_VALUE"] = fsm_data.state_table[fsm_data.reset_state];
+ state_dff->connections["\\ARST"] = fsm_cell->connections["\\ARST"];
+ }
+ state_dff->parameters["\\WIDTH"] = RTLIL::Const(fsm_data.state_bits);
+ state_dff->parameters["\\CLK_POLARITY"] = fsm_cell->parameters["\\CLK_POLARITY"];
+ state_dff->connections["\\CLK"] = fsm_cell->connections["\\CLK"];
+ state_dff->connections["\\D"] = RTLIL::SigSpec(next_state_wire);
+ state_dff->connections["\\Q"] = RTLIL::SigSpec(state_wire);
+ module->add(state_dff);
+
+ // decode state register
+
+ bool encoding_is_onehot = true;
+
+ RTLIL::Wire *state_onehot = new RTLIL::Wire;
+ state_onehot->name = NEW_ID;
+ state_onehot->width = fsm_data.state_table.size();
+ module->add(state_onehot);
+
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++)
+ {
+ RTLIL::Const state = fsm_data.state_table[i];
+ RTLIL::SigSpec sig_a, sig_b;
+
+ for (size_t j = 0; j < state.bits.size(); j++)
+ if (state.bits[j] == RTLIL::State::S0 || state.bits[j] == RTLIL::State::S1) {
+ sig_a.append(RTLIL::SigSpec(state_wire, 1, j));
+ sig_b.append(RTLIL::SigSpec(state.bits[j]));
+ }
+ sig_a.optimize();
+ sig_b.optimize();
+
+ if (sig_b == RTLIL::SigSpec(RTLIL::State::S1))
+ {
+ module->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(state_onehot, 1, i), sig_a));
+ }
+ else
+ {
+ if (sig_b.as_bool() || sig_b.width != fsm_data.state_bits)
+ encoding_is_onehot = false;
+
+ RTLIL::Cell *eq_cell = new RTLIL::Cell;
+ eq_cell->name = NEW_ID;
+ eq_cell->type = "$eq";
+ eq_cell->connections["\\A"] = sig_a;
+ eq_cell->connections["\\B"] = sig_b;
+ eq_cell->connections["\\Y"] = RTLIL::SigSpec(state_onehot, 1, i);
+ eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_a.width);
+ eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(sig_b.width);
+ eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ module->add(eq_cell);
+ }
+ }
+
+ // generate next_state signal
+
+ RTLIL::Wire *next_state_onehot = new RTLIL::Wire;
+ next_state_onehot->name = NEW_ID;
+ next_state_onehot->width = fsm_data.state_table.size();
+ module->add(next_state_onehot);
+
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++)
+ {
+ std::map<RTLIL::Const, std::set<int>> pattern_cache;
+ std::set<int> fullstate_cache;
+
+ for (size_t j = 0; j < fsm_data.state_table.size(); j++)
+ fullstate_cache.insert(j);
+
+ for (auto &tr : fsm_data.transition_table) {
+ if (tr.state_out == int(i))
+ pattern_cache[tr.ctrl_in].insert(tr.state_in);
+ else
+ fullstate_cache.erase(tr.state_in);
+ }
+
+ implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, RTLIL::SigSpec(next_state_onehot, 1, i));
+ }
+
+ if (encoding_is_onehot)
+ {
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
+ RTLIL::Const state = fsm_data.state_table[i];
+ int bit_idx = -1;
+ for (size_t j = 0; j < state.bits.size(); j++)
+ if (state.bits[j] == RTLIL::State::S1)
+ bit_idx = j;
+ if (bit_idx >= 0)
+ module->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(next_state_wire, 1, bit_idx), RTLIL::SigSpec(next_state_onehot, 1, i)));
+ }
+ }
+ else
+ {
+ RTLIL::SigSpec sig_a, sig_b, sig_s;
+ int reset_state = fsm_data.reset_state;
+ if (reset_state < 0)
+ reset_state = 0;
+
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
+ RTLIL::Const state = fsm_data.state_table[i];
+ if (int(i) == fsm_data.reset_state) {
+ sig_a = RTLIL::SigSpec(state);
+ } else {
+ sig_b.append(RTLIL::SigSpec(state));
+ sig_s.append(RTLIL::SigSpec(next_state_onehot, 1, i));
+ }
+ }
+
+ RTLIL::Cell *mux_cell = new RTLIL::Cell;
+ mux_cell->name = NEW_ID;
+ mux_cell->type = "$safe_pmux";
+ mux_cell->connections["\\A"] = sig_a;
+ mux_cell->connections["\\B"] = sig_b;
+ mux_cell->connections["\\S"] = sig_s;
+ mux_cell->connections["\\Y"] = RTLIL::SigSpec(next_state_wire);
+ mux_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_a.width);
+ mux_cell->parameters["\\S_WIDTH"] = RTLIL::Const(sig_s.width);
+ module->add(mux_cell);
+ }
+
+ // Generate ctrl_out signal
+
+ RTLIL::Wire *ctrl_out_wire = new RTLIL::Wire;
+ ctrl_out_wire->name = NEW_ID;
+ ctrl_out_wire->width = fsm_data.num_outputs;
+ module->add(ctrl_out_wire);
+
+ for (int i = 0; i < fsm_data.num_outputs; i++)
+ {
+ std::map<RTLIL::Const, std::set<int>> pattern_cache;
+ std::set<int> fullstate_cache;
+
+ for (size_t j = 0; j < fsm_data.state_table.size(); j++)
+ fullstate_cache.insert(j);
+
+ for (auto &tr : fsm_data.transition_table) {
+ if (tr.ctrl_out.bits[i] == RTLIL::State::S1)
+ pattern_cache[tr.ctrl_in].insert(tr.state_in);
+ else
+ fullstate_cache.erase(tr.state_in);
+ }
+
+ implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, ctrl_out.extract(i, 1));
+ }
+
+ // Remove FSM cell
+
+ module->cells.erase(fsm_cell->name);
+ delete fsm_cell;
+}
+
+struct FsmMapPass : public Pass {
+ FsmMapPass() : Pass("fsm_map") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing FSM_MAP pass (simple optimizations of FSMs).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules) {
+ std::vector<RTLIL::Cell*> fsm_cells;
+ for (auto &cell_it : mod_it.second->cells)
+ if (cell_it.second->type == "$fsm")
+ fsm_cells.push_back(cell_it.second);
+ for (auto cell : fsm_cells)
+ map_fsm(cell, mod_it.second);
+ }
+ }
+} FsmMapPass;
+
diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc
new file mode 100644
index 000000000..8ba9679fd
--- /dev/null
+++ b/passes/fsm/fsm_opt.cc
@@ -0,0 +1,285 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+struct FsmOpt
+{
+ FsmData fsm_data;
+ RTLIL::Cell *cell;
+ RTLIL::Module *module;
+
+ bool signal_is_unused(RTLIL::SigSpec sig)
+ {
+ assert(sig.width == 1);
+ sig.optimize();
+
+ RTLIL::Wire *wire = sig.chunks[0].wire;
+ int bit = sig.chunks[0].offset;
+
+ if (!wire || wire->attributes.count("\\unused_bits") == 0)
+ return false;
+
+ char *str = strdup(wire->attributes["\\unused_bits"].str.c_str());
+ for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) {
+ if (tok[0] && bit == atoi(tok))
+ return true;
+ }
+ free(str);
+
+ return false;
+ }
+
+ void opt_const_and_unused_inputs()
+ {
+ RTLIL::SigSpec ctrl_in = cell->connections["\\CTRL_IN"];
+ std::vector<bool> ctrl_in_used(ctrl_in.width);
+
+ std::vector<FsmData::transition_t> new_transition_table;
+ for (auto &tr : fsm_data.transition_table) {
+ for (int i = 0; i < ctrl_in.width; i++) {
+ RTLIL::SigSpec ctrl_bit = ctrl_in.extract(i, 1);
+ if (ctrl_bit.is_fully_const()) {
+ if (tr.ctrl_in.bits[i] <= RTLIL::State::S1 && RTLIL::SigSpec(tr.ctrl_in.bits[i]) != ctrl_bit)
+ goto delete_this_transition;
+ continue;
+ }
+ if (tr.ctrl_in.bits[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->connections["\\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();
+ }
+ cell->connections["\\CTRL_IN"].remove(i, 1);
+ 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->connections["\\CTRL_OUT"].extract(i, 1);
+ if (signal_is_unused(sig)) {
+ log(" Removing unused output signal %s.\n", log_signal(sig));
+ cell->connections["\\CTRL_OUT"].remove(i, 1);
+ 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["\\CTRL_IN"];
+
+ for (int i = 0; i < ctrl_in.width; i++)
+ for (int j = i+1; j < ctrl_in.width; 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.bits[i];
+ RTLIL::State &sj = tr.ctrl_in.bits[j];
+
+ if (si > RTLIL::State::S1)
+ si = sj;
+ else if (sj > RTLIL::State::S1)
+ sj = si;
+
+ 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["\\CTRL_IN"];
+ RTLIL::SigSpec &ctrl_out = cell->connections["\\CTRL_OUT"];
+
+ for (int j = 0; j < ctrl_out.width; j++)
+ for (int i = 0; i < ctrl_in.width; 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.bits[i];
+ RTLIL::State &sj = tr.ctrl_out.bits[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.bits[bit] > RTLIL::State::S1) {
+ new_set.insert(pattern);
+ continue;
+ }
+
+ RTLIL::Const other_pattern = pattern;
+
+ if (pattern.bits[bit] == RTLIL::State::S1)
+ other_pattern.bits[bit] = RTLIL::State::S0;
+ else
+ other_pattern.bits[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.bits[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.c_str(), module->name.c_str());
+
+ fsm_data.copy_from_cell(cell);
+ this->cell = cell;
+ this->module = module;
+
+ opt_unused_outputs();
+
+ opt_alias_inputs();
+ opt_feedback_inputs();
+ opt_find_dont_care();
+
+ opt_const_and_unused_inputs();
+
+ fsm_data.copy_to_cell(cell);
+ }
+};
+
+void FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module)
+{
+ FsmOpt fsmopt(cell, module);
+}
+
+struct FsmOptPass : public Pass {
+ FsmOptPass() : Pass("fsm_opt") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules)
+ for (auto &cell_it : mod_it.second->cells) {
+ if (cell_it.second->type == "$fsm")
+ FsmData::optimize_fsm(cell_it.second, mod_it.second);
+ }
+ }
+} FsmOptPass;
+
diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc
new file mode 100644
index 000000000..5e258f269
--- /dev/null
+++ b/passes/fsm/fsm_recode.cc
@@ -0,0 +1,114 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &fsm_data, const char *prefix, FILE *f)
+{
+ fprintf(f, "set_fsm_state_vector {");
+ for (int i = fsm_data.state_bits-1; i >= 0; i--)
+ fprintf(f, " %s_reg[%d]", cell->parameters["\\NAME"].str[0] == '\\' ?
+ cell->parameters["\\NAME"].str.substr(1).c_str() : cell->parameters["\\NAME"].str.c_str(), i);
+ fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n",
+ prefix, RTLIL::unescape_id(cell->parameters["\\NAME"].str).c_str(),
+ prefix, RTLIL::unescape_id(module->name).c_str());
+
+ fprintf(f, "set_fsm_encoding {");
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
+ fprintf(f, " s%zd=2#", i);
+ for (int j = int(fsm_data.state_table[i].bits.size())-1; j >= 0; j--)
+ fprintf(f, "%c", fsm_data.state_table[i].bits[j] == RTLIL::State::S1 ? '1' : '0');
+ }
+ fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n",
+ prefix, RTLIL::unescape_id(cell->parameters["\\NAME"].str).c_str(),
+ prefix, RTLIL::unescape_id(module->name).c_str());
+}
+
+static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fsm_file)
+{
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(cell);
+
+ log("Recoding FSM `%s' from module `%s':\n", cell->name.c_str(), module->name.c_str());
+
+ if (fm_set_fsm_file != NULL)
+ fm_set_fsm_print(cell, module, fsm_data, "r", fm_set_fsm_file);
+
+ fsm_data.state_bits = fsm_data.state_table.size();
+ if (fsm_data.reset_state >= 0)
+ fsm_data.state_bits--;
+
+ int bit_pos = 0;
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++)
+ {
+ RTLIL::Const new_code;
+ if (int(i) == fsm_data.reset_state)
+ new_code = RTLIL::Const(RTLIL::State::S0, fsm_data.state_bits);
+ else {
+ RTLIL::Const state_code(RTLIL::State::Sa, fsm_data.state_bits);
+ state_code.bits[bit_pos++] = RTLIL::State::S1;
+ new_code = state_code;
+ }
+
+ log(" %s -> %s\n", fsm_data.state_table[i].as_string().c_str(), new_code.as_string().c_str());
+ fsm_data.state_table[i] = new_code;
+ }
+
+ if (fm_set_fsm_file != NULL)
+ fm_set_fsm_print(cell, module, fsm_data, "i", fm_set_fsm_file);
+
+ fsm_data.copy_to_cell(cell);
+}
+
+struct FsmRecodePass : public Pass {
+ FsmRecodePass() : Pass("fsm_recode") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ FILE *fm_set_fsm_file = NULL;
+
+ log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n");
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file == NULL) {
+ fm_set_fsm_file = fopen(args[++argidx].c_str(), "w");
+ if (fm_set_fsm_file == NULL)
+ log_error("Can't open fm_set_fsm_file `%s' for writing: %s\n", args[argidx].c_str(), strerror(errno));
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod_it : design->modules)
+ for (auto &cell_it : mod_it.second->cells)
+ if (cell_it.second->type == "$fsm")
+ fsm_recode(cell_it.second, mod_it.second, fm_set_fsm_file);
+
+ if (fm_set_fsm_file != NULL)
+ fclose(fm_set_fsm_file);
+ }
+} FsmRecodePass;
+
diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h
new file mode 100644
index 000000000..f43b25fe9
--- /dev/null
+++ b/passes/fsm/fsmdata.h
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef FSMDATA_H
+#define FSMDATA_H
+
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+struct FsmData
+{
+ int num_inputs, num_outputs, state_bits, reset_state;
+ struct transition_t { int state_in, state_out; RTLIL::Const ctrl_in, ctrl_out; };
+ std::vector<transition_t> transition_table;
+ std::vector<RTLIL::Const> state_table;
+
+ void copy_to_cell(RTLIL::Cell *cell)
+ {
+ cell->parameters["\\CTRL_IN_WIDTH"] = RTLIL::Const(num_inputs);
+ cell->parameters["\\CTRL_OUT_WIDTH"] = RTLIL::Const(num_outputs);
+
+ int state_num_log2 = 0;
+ for (int i = state_table.size(); i > 0; i = i >> 1)
+ state_num_log2++;
+ state_num_log2 = std::max(state_num_log2, 1);
+
+ cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits);
+ cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size());
+ cell->parameters["\\STATE_NUM_LOG2"] = RTLIL::Const(state_num_log2);
+ cell->parameters["\\STATE_RST"] = RTLIL::Const(reset_state);
+ cell->parameters["\\STATE_TABLE"] = RTLIL::Const();
+
+ for (int i = 0; i < int(state_table.size()); i++) {
+ std::vector<RTLIL::State> &bits_table = cell->parameters["\\STATE_TABLE"].bits;
+ std::vector<RTLIL::State> &bits_state = state_table[i].bits;
+ bits_table.insert(bits_table.end(), bits_state.begin(), bits_state.end());
+ }
+
+ cell->parameters["\\TRANS_NUM"] = RTLIL::Const(transition_table.size());
+ cell->parameters["\\TRANS_TABLE"] = RTLIL::Const();
+ for (int i = 0; i < int(transition_table.size()); i++)
+ {
+ std::vector<RTLIL::State> &bits_table = cell->parameters["\\TRANS_TABLE"].bits;
+ transition_t &tr = transition_table[i];
+
+ RTLIL::Const const_state_in = RTLIL::Const(tr.state_in, state_num_log2);
+ RTLIL::Const const_state_out = RTLIL::Const(tr.state_out, state_num_log2);
+ std::vector<RTLIL::State> &bits_state_in = const_state_in.bits;
+ std::vector<RTLIL::State> &bits_state_out = const_state_out.bits;
+
+ std::vector<RTLIL::State> &bits_ctrl_in = tr.ctrl_in.bits;
+ std::vector<RTLIL::State> &bits_ctrl_out = tr.ctrl_out.bits;
+
+ // append lsb first
+ bits_table.insert(bits_table.end(), bits_ctrl_out.begin(), bits_ctrl_out.end());
+ bits_table.insert(bits_table.end(), bits_state_out.begin(), bits_state_out.end());
+ bits_table.insert(bits_table.end(), bits_ctrl_in.begin(), bits_ctrl_in.end());
+ bits_table.insert(bits_table.end(), bits_state_in.begin(), bits_state_in.end());
+ }
+ }
+
+ void copy_from_cell(RTLIL::Cell *cell)
+ {
+ num_inputs = cell->parameters["\\CTRL_IN_WIDTH"].as_int();
+ num_outputs = cell->parameters["\\CTRL_OUT_WIDTH"].as_int();
+
+ state_bits = cell->parameters["\\STATE_BITS"].as_int();
+ reset_state = cell->parameters["\\STATE_RST"].as_int();
+
+ int state_num = cell->parameters["\\STATE_NUM"].as_int();
+ int state_num_log2 = cell->parameters["\\STATE_NUM_LOG2"].as_int();
+ int trans_num = cell->parameters["\\TRANS_NUM"].as_int();
+
+ if (reset_state < 0 || reset_state >= state_num)
+ reset_state = -1;
+
+ RTLIL::Const state_table = cell->parameters["\\STATE_TABLE"];
+ RTLIL::Const trans_table = cell->parameters["\\TRANS_TABLE"];
+
+ for (int i = 0; i < state_num; i++) {
+ RTLIL::Const state_code;
+ int off_begin = i*state_bits, off_end = off_begin + state_bits;
+ state_code.bits.insert(state_code.bits.begin(), state_table.bits.begin()+off_begin, state_table.bits.begin()+off_end);
+ this->state_table.push_back(state_code);
+ }
+
+ for (int i = 0; i < trans_num; i++)
+ {
+ auto off_ctrl_out = trans_table.bits.begin() + i*(num_inputs+num_outputs+2*state_num_log2);
+ auto off_state_out = off_ctrl_out + num_outputs;
+ auto off_ctrl_in = off_state_out + state_num_log2;
+ auto off_state_in = off_ctrl_in + num_inputs;
+ auto off_end = off_state_in + state_num_log2;
+
+ RTLIL::Const state_in, state_out, ctrl_in, ctrl_out;
+ ctrl_out.bits.insert(state_in.bits.begin(), off_ctrl_out, off_state_out);
+ state_out.bits.insert(state_out.bits.begin(), off_state_out, off_ctrl_in);
+ ctrl_in.bits.insert(ctrl_in.bits.begin(), off_ctrl_in, off_state_in);
+ state_in.bits.insert(state_in.bits.begin(), off_state_in, off_end);
+
+ transition_t tr;
+ tr.state_in = state_in.as_int();
+ tr.state_out = state_out.as_int();
+ tr.ctrl_in = ctrl_in;
+ tr.ctrl_out = ctrl_out;
+
+ if (tr.state_in < 0 || tr.state_in >= state_num)
+ tr.state_in = -1;
+ if (tr.state_out < 0 || tr.state_out >= state_num)
+ tr.state_out = -1;
+
+ transition_table.push_back(tr);
+ }
+ }
+
+ void log_info(RTLIL::Cell *cell)
+ {
+ log("-------------------------------------\n");
+ log("\n");
+ log(" Information on FSM %s (%s):\n", cell->name.c_str(), cell->parameters["\\NAME"].str.c_str());
+ log("\n");
+ log(" Number of input signals: %3d\n", num_inputs);
+ log(" Number of output signals: %3d\n", num_outputs);
+ log(" Number of state bits: %3d\n", state_bits);
+
+ log("\n");
+ log(" Input signals:\n");
+ RTLIL::SigSpec sig_in = cell->connections["\\CTRL_IN"];
+ sig_in.expand();
+ for (size_t i = 0; i < sig_in.chunks.size(); i++)
+ log(" %3zd: %s\n", i, log_signal(sig_in.chunks[i]));
+
+ log("\n");
+ log(" Output signals:\n");
+ RTLIL::SigSpec sig_out = cell->connections["\\CTRL_OUT"];
+ sig_out.expand();
+ for (size_t i = 0; i < sig_out.chunks.size(); i++)
+ log(" %3zd: %s\n", i, log_signal(sig_out.chunks[i]));
+
+ log("\n");
+ log(" State encoding:\n");
+ for (size_t i = 0; i < state_table.size(); i++)
+ log(" %3zd: %10s%s\n", i, log_signal(state_table[i], false),
+ int(i) == reset_state ? " <RESET STATE>" : "");
+
+ log("\n");
+ log(" Transition Table (state_in, ctrl_in, state_out, ctrl_out):\n");
+ for (size_t i = 0; i < transition_table.size(); i++) {
+ transition_t &tr = transition_table[i];
+ log(" %5zd: %5d %s -> %5d %s\n", i, tr.state_in, log_signal(tr.ctrl_in), tr.state_out, log_signal(tr.ctrl_out));
+ }
+
+ log("\n");
+ log("-------------------------------------\n");
+ }
+
+ // implemented in fsm_opt.cc
+ static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module);
+};
+
+#endif