mirror of
https://github.com/YosysHQ/yosys
synced 2025-08-05 19:00:26 +00:00
initial import
This commit is contained in:
commit
7764d0ba1d
481 changed files with 54634 additions and 0 deletions
11
passes/fsm/Makefile.inc
Normal file
11
passes/fsm/Makefile.inc
Normal file
|
@ -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
|
||||
|
91
passes/fsm/fsm.cc
Normal file
91
passes/fsm/fsm.cc
Normal file
|
@ -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;
|
||||
|
160
passes/fsm/fsm_detect.cc
Normal file
160
passes/fsm/fsm_detect.cc
Normal file
|
@ -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;
|
||||
|
255
passes/fsm/fsm_expand.cc
Normal file
255
passes/fsm/fsm_expand.cc
Normal file
|
@ -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;
|
||||
|
103
passes/fsm/fsm_export.cc
Normal file
103
passes/fsm/fsm_export.cc
Normal file
|
@ -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;
|
359
passes/fsm/fsm_extract.cc
Normal file
359
passes/fsm/fsm_extract.cc
Normal file
|
@ -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;
|
||||
|
46
passes/fsm/fsm_info.cc
Normal file
46
passes/fsm/fsm_info.cc
Normal file
|
@ -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;
|
||||
|
356
passes/fsm/fsm_map.cc
Normal file
356
passes/fsm/fsm_map.cc
Normal file
|
@ -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;
|
||||
|
285
passes/fsm/fsm_opt.cc
Normal file
285
passes/fsm/fsm_opt.cc
Normal file
|
@ -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;
|
||||
|
114
passes/fsm/fsm_recode.cc
Normal file
114
passes/fsm/fsm_recode.cc
Normal file
|
@ -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;
|
||||
|
177
passes/fsm/fsmdata.h
Normal file
177
passes/fsm/fsmdata.h
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue