2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "kernel/log.h"
21 #include "kernel/register.h"
22 #include "kernel/sigtools.h"
23 #include "kernel/consteval.h"
24 #include "kernel/celltypes.h"
29 PRIVATE_NAMESPACE_BEGIN
33 RTLIL::Module
*module
;
34 RTLIL::Cell
*fsm_cell
;
38 SigSet
<RTLIL::Cell
*, RTLIL::sort_by_name_id
<RTLIL::Cell
>> sig2driver
, sig2user
;
41 std::set
<RTLIL::Cell
*, RTLIL::sort_by_name_id
<RTLIL::Cell
>> merged_set
;
42 std::set
<RTLIL::Cell
*, RTLIL::sort_by_name_id
<RTLIL::Cell
>> current_set
;
43 std::set
<RTLIL::Cell
*, RTLIL::sort_by_name_id
<RTLIL::Cell
>> no_candidate_set
;
45 bool already_optimized
;
46 int limit_transitions
;
48 bool is_cell_merge_candidate(RTLIL::Cell
*cell
)
50 if (full_mode
|| cell
->type
== ID($_MUX_
))
53 if (cell
->type
.in(ID($mux
), ID($pmux
)))
54 if (cell
->getPort(ID::A
).size() < 2)
58 RTLIL::SigSpec new_signals
;
60 if (cell
->hasPort(ID::A
)) {
61 in_bits
+= GetSize(cell
->getPort(ID::A
));
62 new_signals
.append(assign_map(cell
->getPort(ID::A
)));
65 if (cell
->hasPort(ID::B
)) {
66 in_bits
+= GetSize(cell
->getPort(ID::B
));
67 new_signals
.append(assign_map(cell
->getPort(ID::B
)));
70 if (cell
->hasPort(ID::S
)) {
71 in_bits
+= GetSize(cell
->getPort(ID::S
));
72 new_signals
.append(assign_map(cell
->getPort(ID::S
)));
78 if (cell
->hasPort(ID::Y
))
79 new_signals
.append(assign_map(cell
->getPort(ID::Y
)));
81 new_signals
.sort_and_unify();
82 new_signals
.remove_const();
84 new_signals
.remove(assign_map(fsm_cell
->getPort(ID::CTRL_IN
)));
85 new_signals
.remove(assign_map(fsm_cell
->getPort(ID::CTRL_OUT
)));
87 if (new_signals
.size() > 3)
93 void create_current_set()
95 std::vector
<RTLIL::Cell
*> cell_list
;
97 for (auto c
: sig2driver
.find(assign_map(fsm_cell
->getPort(ID::CTRL_IN
))))
98 cell_list
.push_back(c
);
100 for (auto c
: sig2user
.find(assign_map(fsm_cell
->getPort(ID::CTRL_OUT
))))
101 cell_list
.push_back(c
);
104 for (auto c
: cell_list
)
106 if (merged_set
.count(c
) > 0 || current_set
.count(c
) > 0 || no_candidate_set
.count(c
) > 0)
108 for (auto &p
: c
->connections()) {
109 if (p
.first
!= ID::A
&& p
.first
!= ID::B
&& p
.first
!= ID::S
&& p
.first
!= ID::Y
)
112 if (!is_cell_merge_candidate(c
)) {
113 no_candidate_set
.insert(c
);
116 current_set
.insert(c
);
121 void optimze_as_needed()
123 if (already_optimized
)
126 int trans_num
= fsm_cell
->parameters
[ID::TRANS_NUM
].as_int();
127 if (trans_num
> limit_transitions
)
129 log(" grown transition table to %d entries -> optimize.\n", trans_num
);
130 FsmData::optimize_fsm(fsm_cell
, module
);
131 already_optimized
= true;
133 trans_num
= fsm_cell
->parameters
[ID::TRANS_NUM
].as_int();
134 log(" transition table size after optimizaton: %d\n", trans_num
);
135 limit_transitions
= 16 * trans_num
;
139 void merge_cell_into_fsm(RTLIL::Cell
*cell
)
143 log(" merging %s cell %s.\n", cell
->type
.c_str(), cell
->name
.c_str());
144 merged_set
.insert(cell
);
145 already_optimized
= false;
147 RTLIL::SigSpec input_sig
, output_sig
;
149 for (auto &p
: cell
->connections())
150 if (ct
.cell_output(cell
->type
, p
.first
))
151 output_sig
.append(assign_map(p
.second
));
153 input_sig
.append(assign_map(p
.second
));
154 input_sig
.sort_and_unify();
155 input_sig
.remove_const();
157 std::vector
<RTLIL::Const
> truth_tab
;
159 for (int i
= 0; i
< (1 << input_sig
.size()); i
++) {
160 RTLIL::Const
in_val(i
, input_sig
.size());
161 RTLIL::SigSpec A
, B
, S
;
162 if (cell
->hasPort(ID::A
))
163 A
= assign_map(cell
->getPort(ID::A
));
164 if (cell
->hasPort(ID::B
))
165 B
= assign_map(cell
->getPort(ID::B
));
166 if (cell
->hasPort(ID::S
))
167 S
= assign_map(cell
->getPort(ID::S
));
168 A
.replace(input_sig
, RTLIL::SigSpec(in_val
));
169 B
.replace(input_sig
, RTLIL::SigSpec(in_val
));
170 S
.replace(input_sig
, RTLIL::SigSpec(in_val
));
171 log_assert(A
.is_fully_const());
172 log_assert(B
.is_fully_const());
173 log_assert(S
.is_fully_const());
174 truth_tab
.push_back(ct
.eval(cell
, A
.as_const(), B
.as_const(), S
.as_const()));
178 fsm_data
.copy_from_cell(fsm_cell
);
180 fsm_data
.num_inputs
+= input_sig
.size();
181 RTLIL::SigSpec new_ctrl_in
= fsm_cell
->getPort(ID::CTRL_IN
);
182 new_ctrl_in
.append(input_sig
);
183 fsm_cell
->setPort(ID::CTRL_IN
, new_ctrl_in
);
185 fsm_data
.num_outputs
+= output_sig
.size();
186 RTLIL::SigSpec new_ctrl_out
= fsm_cell
->getPort(ID::CTRL_OUT
);
187 new_ctrl_out
.append(output_sig
);
188 fsm_cell
->setPort(ID::CTRL_OUT
, new_ctrl_out
);
190 if (GetSize(input_sig
) > 10)
191 log_warning("Cell %s.%s (%s) has %d input bits, merging into FSM %s.%s might be problematic.\n",
192 log_id(cell
->module
), log_id(cell
), log_id(cell
->type
),
193 GetSize(input_sig
), log_id(fsm_cell
->module
), log_id(fsm_cell
));
195 if (GetSize(fsm_data
.transition_table
) > 10000)
196 log_warning("Transition table for FSM %s.%s already has %d rows, merging more cells "
197 "into this FSM might be problematic.\n", log_id(fsm_cell
->module
), log_id(fsm_cell
),
198 GetSize(fsm_data
.transition_table
));
200 std::vector
<FsmData::transition_t
> new_transition_table
;
201 for (auto &tr
: fsm_data
.transition_table
) {
202 for (int i
= 0; i
< (1 << input_sig
.size()); i
++) {
203 FsmData::transition_t new_tr
= tr
;
204 RTLIL::Const
in_val(i
, input_sig
.size());
205 RTLIL::Const out_val
= truth_tab
[i
];
206 RTLIL::SigSpec ctrl_in
= new_tr
.ctrl_in
;
207 RTLIL::SigSpec ctrl_out
= new_tr
.ctrl_out
;
208 ctrl_in
.append(in_val
);
209 ctrl_out
.append(out_val
);
210 new_tr
.ctrl_in
= ctrl_in
.as_const();
211 new_tr
.ctrl_out
= ctrl_out
.as_const();
212 new_transition_table
.push_back(new_tr
);
215 fsm_data
.transition_table
.swap(new_transition_table
);
216 new_transition_table
.clear();
218 fsm_data
.copy_to_cell(fsm_cell
);
221 FsmExpand(RTLIL::Cell
*cell
, RTLIL::Design
*design
, RTLIL::Module
*mod
, bool full
)
227 assign_map
.set(module
);
228 ct
.setup_internals();
231 for (auto &cell_it
: module
->cells_
) {
232 RTLIL::Cell
*c
= cell_it
.second
;
233 if (ct
.cell_known(c
->type
) && design
->selected(mod
, c
))
234 for (auto &p
: c
->connections()) {
235 if (ct
.cell_output(c
->type
, p
.first
))
236 sig2driver
.insert(assign_map(p
.second
), c
);
238 sig2user
.insert(assign_map(p
.second
), c
);
246 log("Expanding FSM `%s' from module `%s':\n", fsm_cell
->name
.c_str(), module
->name
.c_str());
248 already_optimized
= false;
249 limit_transitions
= 16 * fsm_cell
->parameters
[ID::TRANS_NUM
].as_int();
251 for (create_current_set(); current_set
.size() > 0; create_current_set()) {
252 for (auto c
: current_set
)
253 merge_cell_into_fsm(c
);
256 for (auto c
: merged_set
)
259 if (merged_set
.size() > 0 && !already_optimized
)
260 FsmData::optimize_fsm(fsm_cell
, module
);
262 log(" merged %d cells into FSM.\n", GetSize(merged_set
));
266 struct FsmExpandPass
: public Pass
{
267 FsmExpandPass() : Pass("fsm_expand", "expand FSM cells by merging logic into it") { }
270 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
272 log(" fsm_expand [-full] [selection]\n");
274 log("The fsm_extract pass is conservative about the cells that belong to a finite\n");
275 log("state machine. This pass can be used to merge additional auxiliary gates into\n");
276 log("the finite state machine.\n");
278 log("By default, fsm_expand is still a bit conservative regarding merging larger\n");
279 log("word-wide cells. Call with -full to consider all cells for merging.\n");
282 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) override
284 bool full_mode
= false;
286 log_header(design
, "Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n");
289 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
290 if (args
[argidx
] == "-full") {
296 extra_args(args
, argidx
, design
);
298 for (auto mod
: design
->selected_modules()) {
299 std::vector
<RTLIL::Cell
*> fsm_cells
;
300 for (auto cell
: mod
->selected_cells())
301 if (cell
->type
== ID($fsm
))
302 fsm_cells
.push_back(cell
);
303 for (auto c
: fsm_cells
) {
304 FsmExpand
fsm_expand(c
, design
, mod
, full_mode
);
305 fsm_expand
.execute();
311 PRIVATE_NAMESPACE_END