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
35 RTLIL::Module
*module
;
37 void opt_unreachable_states()
41 std::set
<int> unreachable_states
;
42 std::vector
<FsmData::transition_t
> new_transition_table
;
43 std::vector
<RTLIL::Const
> new_state_table
;
44 std::map
<int, int> old_to_new_state
;
46 for (int i
= 0; i
< GetSize(fsm_data
.state_table
); i
++)
47 if (i
!= fsm_data
.reset_state
)
48 unreachable_states
.insert(i
);
50 for (auto &trans
: fsm_data
.transition_table
)
51 unreachable_states
.erase(trans
.state_out
);
53 if (unreachable_states
.empty())
56 for (int i
= 0; i
< GetSize(fsm_data
.state_table
); i
++) {
57 if (unreachable_states
.count(i
)) {
58 log(" Removing unreachable state %s.\n", log_signal(fsm_data
.state_table
[i
]));
61 old_to_new_state
[i
] = GetSize(new_state_table
);
62 new_state_table
.push_back(fsm_data
.state_table
[i
]);
65 for (auto trans
: fsm_data
.transition_table
) {
66 if (unreachable_states
.count(trans
.state_in
))
68 trans
.state_in
= old_to_new_state
.at(trans
.state_in
);
69 trans
.state_out
= old_to_new_state
.at(trans
.state_out
);
70 new_transition_table
.push_back(trans
);
73 new_transition_table
.swap(fsm_data
.transition_table
);
74 new_state_table
.swap(fsm_data
.state_table
);
75 if (fsm_data
.reset_state
!= -1)
76 fsm_data
.reset_state
= old_to_new_state
.at(fsm_data
.reset_state
);
80 bool signal_is_unused(RTLIL::SigSpec sig
)
82 RTLIL::SigBit bit
= sig
.as_bit();
84 if (bit
.wire
== NULL
|| bit
.wire
->attributes
.count("\\unused_bits") == 0)
87 char *str
= strdup(bit
.wire
->attributes
["\\unused_bits"].decode_string().c_str());
88 for (char *tok
= strtok(str
, " "); tok
!= NULL
; tok
= strtok(NULL
, " ")) {
89 if (tok
[0] && bit
.offset
== atoi(tok
)) {
99 void opt_const_and_unused_inputs()
101 RTLIL::SigSpec ctrl_in
= cell
->getPort("\\CTRL_IN");
102 std::vector
<bool> ctrl_in_used(ctrl_in
.size());
104 std::vector
<FsmData::transition_t
> new_transition_table
;
105 for (auto &tr
: fsm_data
.transition_table
) {
106 for (int i
= 0; i
< ctrl_in
.size(); i
++) {
107 RTLIL::SigSpec ctrl_bit
= ctrl_in
.extract(i
, 1);
108 if (ctrl_bit
.is_fully_const()) {
109 if (tr
.ctrl_in
.bits
[i
] <= RTLIL::State::S1
&& RTLIL::SigSpec(tr
.ctrl_in
.bits
[i
]) != ctrl_bit
)
110 goto delete_this_transition
;
113 if (tr
.ctrl_in
.bits
[i
] <= RTLIL::State::S1
)
114 ctrl_in_used
[i
] = true;
116 new_transition_table
.push_back(tr
);
117 delete_this_transition
:;
120 for (int i
= int(ctrl_in_used
.size())-1; i
>= 0; i
--) {
121 if (!ctrl_in_used
[i
]) {
122 log(" Removing unused input signal %s.\n", log_signal(cell
->getPort("\\CTRL_IN").extract(i
, 1)));
123 for (auto &tr
: new_transition_table
) {
124 RTLIL::SigSpec
tmp(tr
.ctrl_in
);
126 tr
.ctrl_in
= tmp
.as_const();
128 RTLIL::SigSpec new_ctrl_in
= cell
->getPort("\\CTRL_IN");
129 new_ctrl_in
.remove(i
, 1);
130 cell
->setPort("\\CTRL_IN", new_ctrl_in
);
131 fsm_data
.num_inputs
--;
135 fsm_data
.transition_table
.swap(new_transition_table
);
136 new_transition_table
.clear();
139 void opt_unused_outputs()
141 for (int i
= 0; i
< fsm_data
.num_outputs
; i
++) {
142 RTLIL::SigSpec sig
= cell
->getPort("\\CTRL_OUT").extract(i
, 1);
143 if (signal_is_unused(sig
)) {
144 log(" Removing unused output signal %s.\n", log_signal(sig
));
145 RTLIL::SigSpec new_ctrl_out
= cell
->getPort("\\CTRL_OUT");
146 new_ctrl_out
.remove(i
, 1);
147 cell
->setPort("\\CTRL_OUT", new_ctrl_out
);
148 for (auto &tr
: fsm_data
.transition_table
) {
149 RTLIL::SigSpec
tmp(tr
.ctrl_out
);
151 tr
.ctrl_out
= tmp
.as_const();
153 fsm_data
.num_outputs
--;
159 void opt_alias_inputs()
161 RTLIL::SigSpec
&ctrl_in
= cell
->connections_
["\\CTRL_IN"];
163 for (int i
= 0; i
< ctrl_in
.size(); i
++)
164 for (int j
= i
+1; j
< ctrl_in
.size(); j
++)
165 if (ctrl_in
.extract(i
, 1) == ctrl_in
.extract(j
, 1))
167 log(" Optimize handling of signal %s that is connected to inputs %d and %d.\n", log_signal(ctrl_in
.extract(i
, 1)), i
, j
);
168 std::vector
<FsmData::transition_t
> new_transition_table
;
170 for (auto tr
: fsm_data
.transition_table
)
172 RTLIL::State
&si
= tr
.ctrl_in
.bits
[i
];
173 RTLIL::State
&sj
= tr
.ctrl_in
.bits
[j
];
175 if (si
> RTLIL::State::S1
)
177 else if (sj
> RTLIL::State::S1
)
181 RTLIL::SigSpec
tmp(tr
.ctrl_in
);
183 tr
.ctrl_in
= tmp
.as_const();
184 new_transition_table
.push_back(tr
);
188 ctrl_in
.remove(j
--, 1);
189 fsm_data
.num_inputs
--;
191 fsm_data
.transition_table
.swap(new_transition_table
);
192 new_transition_table
.clear();
196 void opt_feedback_inputs()
198 RTLIL::SigSpec
&ctrl_in
= cell
->connections_
["\\CTRL_IN"];
199 RTLIL::SigSpec
&ctrl_out
= cell
->connections_
["\\CTRL_OUT"];
201 for (int j
= 0; j
< ctrl_out
.size(); j
++)
202 for (int i
= 0; i
< ctrl_in
.size(); i
++)
203 if (ctrl_in
.extract(i
, 1) == ctrl_out
.extract(j
, 1))
205 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
);
206 std::vector
<FsmData::transition_t
> new_transition_table
;
208 for (auto tr
: fsm_data
.transition_table
)
210 RTLIL::State
&si
= tr
.ctrl_in
.bits
[i
];
211 RTLIL::State
&sj
= tr
.ctrl_out
.bits
[j
];
213 if (si
> RTLIL::State::S1
|| si
== sj
) {
214 RTLIL::SigSpec
tmp(tr
.ctrl_in
);
216 tr
.ctrl_in
= tmp
.as_const();
217 new_transition_table
.push_back(tr
);
221 ctrl_in
.remove(i
--, 1);
222 fsm_data
.num_inputs
--;
224 fsm_data
.transition_table
.swap(new_transition_table
);
225 new_transition_table
.clear();
229 void opt_find_dont_care_worker(std::set
<RTLIL::Const
> &set
, int bit
, FsmData::transition_t
&tr
, bool &did_something
)
231 std::set
<RTLIL::Const
> new_set
;
233 for (auto &pattern
: set
)
235 if (pattern
.bits
[bit
] > RTLIL::State::S1
) {
236 new_set
.insert(pattern
);
240 RTLIL::Const other_pattern
= pattern
;
242 if (pattern
.bits
[bit
] == RTLIL::State::S1
)
243 other_pattern
.bits
[bit
] = RTLIL::State::S0
;
245 other_pattern
.bits
[bit
] = RTLIL::State::S1
;
247 if (set
.count(other_pattern
) > 0) {
248 log(" Merging pattern %s and %s from group (%d %d %s).\n", log_signal(pattern
), log_signal(other_pattern
),
249 tr
.state_in
, tr
.state_out
, log_signal(tr
.ctrl_out
));
250 other_pattern
.bits
[bit
] = RTLIL::State::Sa
;
251 new_set
.insert(other_pattern
);
252 did_something
= true;
256 new_set
.insert(pattern
);
262 void opt_find_dont_care()
264 typedef std::pair
<std::pair
<int, int>, RTLIL::Const
> group_t
;
265 std::map
<group_t
, std::set
<RTLIL::Const
>> transitions_by_group
;
267 for (auto &tr
: fsm_data
.transition_table
) {
268 group_t
group(std::pair
<int, int>(tr
.state_in
, tr
.state_out
), tr
.ctrl_out
);
269 transitions_by_group
[group
].insert(tr
.ctrl_in
);
272 fsm_data
.transition_table
.clear();
273 for (auto &it
: transitions_by_group
)
275 FsmData::transition_t tr
;
276 tr
.state_in
= it
.first
.first
.first
;
277 tr
.state_out
= it
.first
.first
.second
;
278 tr
.ctrl_out
= it
.first
.second
;
280 bool did_something
= true;
281 while (did_something
) {
282 did_something
= false;
283 for (int i
= 0; i
< fsm_data
.num_inputs
; i
++)
284 opt_find_dont_care_worker(it
.second
, i
, tr
, did_something
);
287 for (auto &ci
: it
.second
) {
289 fsm_data
.transition_table
.push_back(tr
);
294 FsmOpt(RTLIL::Cell
*cell
, RTLIL::Module
*module
)
296 log("Optimizing FSM `%s' from module `%s'.\n", cell
->name
.c_str(), module
->name
.c_str());
298 fsm_data
.copy_from_cell(cell
);
300 this->module
= module
;
302 opt_unreachable_states();
304 opt_unused_outputs();
307 opt_feedback_inputs();
308 opt_find_dont_care();
310 opt_const_and_unused_inputs();
312 fsm_data
.copy_to_cell(cell
);
316 PRIVATE_NAMESPACE_END
318 void YOSYS_NAMESPACE_PREFIX
FsmData::optimize_fsm(RTLIL::Cell
*cell
, RTLIL::Module
*module
)
320 FsmOpt
fsmopt(cell
, module
);
323 PRIVATE_NAMESPACE_BEGIN
325 struct FsmOptPass
: public Pass
{
326 FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { }
327 void help() YS_OVERRIDE
329 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
331 log(" fsm_opt [selection]\n");
333 log("This pass optimizes FSM cells. It detects which output signals are actually\n");
334 log("not used and removes them from the FSM. This pass is usually used in\n");
335 log("combination with the 'opt_clean' pass (see also 'help fsm').\n");
338 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
340 log_header(design
, "Executing FSM_OPT pass (simple optimizations of FSMs).\n");
341 extra_args(args
, 1, design
);
343 for (auto &mod_it
: design
->modules_
) {
344 if (design
->selected(mod_it
.second
))
345 for (auto &cell_it
: mod_it
.second
->cells_
)
346 if (cell_it
.second
->type
== "$fsm" && design
->selected(mod_it
.second
, cell_it
.second
))
347 FsmData::optimize_fsm(cell_it
.second
, mod_it
.second
);
352 PRIVATE_NAMESPACE_END