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 "opt_status.h"
21 #include "kernel/register.h"
22 #include "kernel/sigtools.h"
23 #include "kernel/log.h"
24 #include "kernel/celltypes.h"
32 static CellTypes ct
, ct_reg
;
33 static int count_rm_cells
, count_rm_wires
;
35 static void rmunused_module_cells(RTLIL::Module
*module
, bool verbose
)
37 SigMap
assign_map(module
);
38 std::set
<RTLIL::Cell
*, RTLIL::sort_by_name
<RTLIL::Cell
>> queue
, unused
;
40 SigSet
<RTLIL::Cell
*> wire2driver
;
41 for (auto &it
: module
->cells
) {
42 RTLIL::Cell
*cell
= it
.second
;
43 for (auto &it2
: cell
->connections
) {
44 if (!ct
.cell_input(cell
->type
, it2
.first
)) {
45 RTLIL::SigSpec sig
= it2
.second
;
46 assign_map
.apply(sig
);
47 wire2driver
.insert(sig
, cell
);
50 if (cell
->type
== "$memwr" || cell
->attributes
.count("\\keep"))
55 for (auto &it
: module
->wires
) {
56 RTLIL::Wire
*wire
= it
.second
;
57 if (wire
->port_output
) {
58 std::set
<RTLIL::Cell
*> cell_list
;
59 RTLIL::SigSpec sig
= RTLIL::SigSpec(wire
);
60 assign_map
.apply(sig
);
61 wire2driver
.find(sig
, cell_list
);
62 for (auto cell
: cell_list
)
67 while (queue
.size() > 0)
69 std::set
<RTLIL::Cell
*, RTLIL::sort_by_name
<RTLIL::Cell
>> new_queue
;
70 for (auto cell
: queue
)
72 for (auto cell
: queue
) {
73 for (auto &it
: cell
->connections
) {
74 if (!ct
.cell_output(cell
->type
, it
.first
)) {
75 std::set
<RTLIL::Cell
*> cell_list
;
76 RTLIL::SigSpec sig
= it
.second
;
77 assign_map
.apply(sig
);
78 wire2driver
.find(sig
, cell_list
);
79 for (auto cell
: cell_list
) {
80 if (unused
.count(cell
) > 0)
81 new_queue
.insert(cell
);
86 queue
.swap(new_queue
);
89 for (auto cell
: unused
) {
91 log(" removing unused `%s' cell `%s'.\n", cell
->type
.c_str(), cell
->name
.c_str());
92 OPT_DID_SOMETHING
= true;
93 module
->cells
.erase(cell
->name
);
99 static bool compare_signals(RTLIL::SigSpec
&s1
, RTLIL::SigSpec
&s2
, SigPool
®s
, SigPool
&conns
)
101 assert(s1
.width
== 1);
102 assert(s2
.width
== 1);
103 assert(s1
.chunks
.size() == 1);
104 assert(s2
.chunks
.size() == 1);
106 RTLIL::Wire
*w1
= s1
.chunks
[0].wire
;
107 RTLIL::Wire
*w2
= s2
.chunks
[0].wire
;
109 if (w1
== NULL
|| w2
== NULL
)
112 if (w1
->port_input
!= w2
->port_input
)
113 return w2
->port_input
;
115 if (w1
->name
[0] == '\\' && w2
->name
[0] == '\\') {
116 if (regs
.check_any(s1
) != regs
.check_any(s2
))
117 return regs
.check_any(s2
);
118 if (conns
.check_any(s1
) != conns
.check_any(s2
))
119 return conns
.check_any(s2
);
122 if (w1
->port_output
!= w2
->port_output
)
123 return w2
->port_output
;
125 if (w1
->name
[0] != w2
->name
[0])
126 return w2
->name
[0] == '\\';
128 if (w1
->attributes
.size() != w2
->attributes
.size())
129 return w2
->attributes
.size() > w1
->attributes
.size();
131 return w2
->name
< w1
->name
;
134 static bool check_public_name(RTLIL::IdString id
)
138 if (id
.substr(0, 2) == "\\_" && (id
[id
.size()-1] == '_' || id
.find("_[") != std::string::npos
))
140 if (id
.find(".$") != std::string::npos
)
145 static void rmunused_module_signals(RTLIL::Module
*module
, bool purge_mode
, bool verbose
)
147 SigPool register_signals
;
148 SigPool connected_signals
;
151 for (auto &it
: module
->cells
) {
152 RTLIL::Cell
*cell
= it
.second
;
153 if (ct_reg
.cell_known(cell
->type
))
154 for (auto &it2
: cell
->connections
)
155 if (ct_reg
.cell_output(cell
->type
, it2
.first
))
156 register_signals
.add(it2
.second
);
157 for (auto &it2
: cell
->connections
)
158 connected_signals
.add(it2
.second
);
161 SigMap
assign_map(module
);
162 for (auto &it
: module
->wires
) {
163 RTLIL::Wire
*wire
= it
.second
;
164 for (int i
= 0; i
< wire
->width
; i
++) {
165 RTLIL::SigSpec s1
= RTLIL::SigSpec(wire
, 1, i
), s2
= assign_map(s1
);
166 if (!compare_signals(s1
, s2
, register_signals
, connected_signals
))
171 module
->connections
.clear();
173 SigPool used_signals
;
174 SigPool used_signals_nodrivers
;
175 for (auto &it
: module
->cells
) {
176 RTLIL::Cell
*cell
= it
.second
;
177 for (auto &it2
: cell
->connections
) {
178 assign_map
.apply(it2
.second
);
179 used_signals
.add(it2
.second
);
180 if (!ct
.cell_output(cell
->type
, it2
.first
))
181 used_signals_nodrivers
.add(it2
.second
);
184 for (auto &it
: module
->wires
) {
185 RTLIL::Wire
*wire
= it
.second
;
186 if (wire
->port_id
> 0) {
187 RTLIL::SigSpec sig
= RTLIL::SigSpec(wire
);
188 assign_map
.apply(sig
);
189 used_signals
.add(sig
);
190 if (!wire
->port_input
)
191 used_signals_nodrivers
.add(sig
);
195 std::vector
<RTLIL::Wire
*> del_wires
;
196 for (auto &it
: module
->wires
) {
197 RTLIL::Wire
*wire
= it
.second
;
198 if ((!purge_mode
&& check_public_name(wire
->name
)) || wire
->port_id
!= 0) {
199 RTLIL::SigSpec s1
= RTLIL::SigSpec(wire
), s2
= s1
;
200 assign_map
.apply(s2
);
201 if (!used_signals
.check_any(s2
) && wire
->port_id
== 0) {
202 del_wires
.push_back(wire
);
206 assert(s1
.chunks
.size() == s2
.chunks
.size());
207 RTLIL::SigSig new_conn
;
208 for (size_t i
= 0; i
< s1
.chunks
.size(); i
++)
209 if (s1
.chunks
[i
] != s2
.chunks
[i
]) {
210 new_conn
.first
.append(s1
.chunks
[i
]);
211 new_conn
.second
.append(s2
.chunks
[i
]);
213 if (new_conn
.first
.width
> 0) {
214 new_conn
.first
.optimize();
215 new_conn
.second
.optimize();
216 used_signals
.add(new_conn
.first
);
217 used_signals
.add(new_conn
.second
);
218 module
->connections
.push_back(new_conn
);
222 if (!used_signals
.check_any(RTLIL::SigSpec(wire
)))
223 del_wires
.push_back(wire
);
225 RTLIL::SigSpec sig
= assign_map(RTLIL::SigSpec(wire
));
226 if (!used_signals_nodrivers
.check_any(sig
)) {
227 std::string unused_bits
;
229 for (size_t i
= 0; i
< sig
.chunks
.size(); i
++) {
230 if (sig
.chunks
[i
].wire
== NULL
)
232 if (!used_signals_nodrivers
.check_any(sig
)) {
233 if (!unused_bits
.empty())
235 unused_bits
+= stringf("%zd", i
);
238 if (unused_bits
.empty() || wire
->port_id
!= 0)
239 wire
->attributes
.erase("\\unused_bits");
241 wire
->attributes
["\\unused_bits"] = RTLIL::Const(unused_bits
);
243 wire
->attributes
.erase("\\unused_bits");
247 int del_wires_count
= 0;
248 for (auto wire
: del_wires
)
249 if (!used_signals
.check_any(RTLIL::SigSpec(wire
))) {
250 if (check_public_name(wire
->name
) && verbose
) {
251 log(" removing unused non-port wire %s.\n", wire
->name
.c_str());
254 module
->wires
.erase(wire
->name
);
259 if (del_wires_count
> 0)
260 log(" removed %d unused temporary wires.\n", del_wires_count
);
263 static void rmunused_module(RTLIL::Module
*module
, bool purge_mode
, bool verbose
)
266 log("Finding unused cells or wires in module %s..\n", module
->name
.c_str());
268 rmunused_module_cells(module
, verbose
);
269 rmunused_module_signals(module
, purge_mode
, verbose
);
272 struct OptCleanPass
: public Pass
{
273 OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
276 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
278 log(" opt_clean [options] [selection]\n");
280 log("This pass identifies wires and cells that are unused and removes them. Other\n");
281 log("passes often remove cells but leave the wires in the design or reconnect the\n");
282 log("wires but leave the old cells in the design. This pass can be used to clean up\n");
283 log("after the passes that do the actual work.\n");
285 log("This pass only operates on completely selected modules without processes.\n");
288 log(" also remove internal nets if they have a public name\n");
291 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
293 bool purge_mode
= false;
295 log_header("Executing OPT_CLEAN pass (remove unused cells and wires).\n");
299 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
300 if (args
[argidx
] == "-purge") {
306 extra_args(args
, argidx
, design
);
308 ct
.setup_internals();
309 ct
.setup_internals_mem();
311 ct
.setup_stdcells_mem();
313 ct_reg
.setup_internals_mem();
314 ct_reg
.setup_stdcells_mem();
316 for (auto &mod_it
: design
->modules
) {
317 if (!design
->selected_whole_module(mod_it
.first
)) {
318 if (design
->selected(mod_it
.second
))
319 log("Skipping module %s as it is only partially selected.\n", id2cstr(mod_it
.second
->name
));
322 if (mod_it
.second
->processes
.size() > 0) {
323 log("Skipping module %s as it contains processes.\n", mod_it
.second
->name
.c_str());
325 rmunused_module(mod_it
.second
, purge_mode
, true);
335 struct CleanPass
: public Pass
{
336 CleanPass() : Pass("clean", "remove unused cells and wires") { }
339 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
341 log(" clean [options] [selection]\n");
343 log("This is identical to 'opt_clean', but less verbose.\n");
345 log("When commands are seperated using the ';;' token, this command will be executed\n");
346 log("between the commands.\n");
348 log("When commands are seperated using the ';;;' token, this command will be executed\n");
349 log("in -purge mode between the commands.\n");
352 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
354 bool purge_mode
= false;
357 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
358 if (args
[argidx
] == "-purge") {
364 if (argidx
< args
.size())
365 extra_args(args
, argidx
, design
);
367 ct
.setup_internals();
368 ct
.setup_internals_mem();
370 ct
.setup_stdcells_mem();
372 ct_reg
.setup_internals_mem();
373 ct_reg
.setup_stdcells_mem();
378 for (auto &mod_it
: design
->modules
) {
379 if (design
->selected_whole_module(mod_it
.first
) && mod_it
.second
->processes
.size() == 0)
381 OPT_DID_SOMETHING
= false;
382 rmunused_module(mod_it
.second
, purge_mode
, false);
383 } while (OPT_DID_SOMETHING
);
386 if (count_rm_cells
> 0 || count_rm_wires
> 0)
387 log("Removed %d unused cells and %d unused wires.\n", count_rm_cells
, count_rm_wires
);