183d6757e1cf54cce91ec51a48d0b11a76aa3f9d
[yosys.git] / passes / opt / opt_clean.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
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.
9 *
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.
17 *
18 */
19
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"
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <stdio.h>
28 #include <set>
29
30 using RTLIL::id2cstr;
31
32 static CellTypes ct;
33 static int count_rm_cells, count_rm_wires;
34
35 static void rmunused_module_cells(RTLIL::Module *module, bool verbose)
36 {
37 SigMap assign_map(module);
38 std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> queue, unused;
39
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);
48 }
49 }
50 if (cell->type == "$memwr")
51 queue.insert(cell);
52 unused.insert(cell);
53 }
54
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)
63 queue.insert(cell);
64 }
65 }
66
67 while (queue.size() > 0)
68 {
69 std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> new_queue;
70 for (auto cell : queue)
71 unused.erase(cell);
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);
82 }
83 }
84 }
85 }
86 queue.swap(new_queue);
87 }
88
89 for (auto cell : unused) {
90 if (verbose)
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);
94 count_rm_cells++;
95 delete cell;
96 }
97 }
98
99 static bool compare_signals(RTLIL::SigSpec &s1, RTLIL::SigSpec &s2)
100 {
101 assert(s1.width == 1);
102 assert(s2.width == 1);
103 assert(s1.chunks.size() == 1);
104 assert(s2.chunks.size() == 1);
105
106 RTLIL::Wire *w1 = s1.chunks[0].wire;
107 RTLIL::Wire *w2 = s2.chunks[0].wire;
108
109 if (w1 == NULL || w2 == NULL)
110 return w2 == NULL;
111
112 if (w1->port_input != w2->port_input)
113 return w2->port_input;
114
115 if (w1->name[0] != w2->name[0])
116 return w2->name[0] == '\\';
117
118 if (w1->attributes.size() != w2->attributes.size())
119 return w2->attributes.size() > w1->attributes.size();
120
121 return w2->name < w1->name;
122 }
123
124 static bool check_public_name(RTLIL::IdString id)
125 {
126 if (id[0] == '$')
127 return false;
128 if (id.substr(0, 2) == "\\_" && (id[id.size()-1] == '_' || id.find("_[") != std::string::npos))
129 return false;
130 if (id.find(".$") != std::string::npos)
131 return false;
132 return true;
133 }
134
135 static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
136 {
137 SigMap assign_map(module);
138 for (auto &it : module->wires) {
139 RTLIL::Wire *wire = it.second;
140 for (int i = 0; i < wire->width; i++) {
141 RTLIL::SigSpec s1 = RTLIL::SigSpec(wire, 1, i), s2 = assign_map(s1);
142 if (!compare_signals(s1, s2))
143 assign_map.add(s1);
144 }
145 }
146
147 module->connections.clear();
148
149 SigPool used_signals;
150 SigPool used_signals_nodrivers;
151 for (auto &it : module->cells) {
152 RTLIL::Cell *cell = it.second;
153 for (auto &it2 : cell->connections) {
154 assign_map.apply(it2.second);
155 used_signals.add(it2.second);
156 if (!ct.cell_output(cell->type, it2.first))
157 used_signals_nodrivers.add(it2.second);
158 }
159 }
160 for (auto &it : module->wires) {
161 RTLIL::Wire *wire = it.second;
162 if (wire->port_id > 0) {
163 RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
164 assign_map.apply(sig);
165 used_signals.add(sig);
166 if (!wire->port_input)
167 used_signals_nodrivers.add(sig);
168 }
169 }
170
171 std::vector<RTLIL::Wire*> del_wires;
172 for (auto &it : module->wires) {
173 RTLIL::Wire *wire = it.second;
174 if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0) {
175 RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
176 assign_map.apply(s2);
177 if (!used_signals.check_any(s2) && wire->port_id == 0) {
178 del_wires.push_back(wire);
179 } else {
180 s1.expand();
181 s2.expand();
182 assert(s1.chunks.size() == s2.chunks.size());
183 RTLIL::SigSig new_conn;
184 for (size_t i = 0; i < s1.chunks.size(); i++)
185 if (s1.chunks[i] != s2.chunks[i]) {
186 new_conn.first.append(s1.chunks[i]);
187 new_conn.second.append(s2.chunks[i]);
188 }
189 if (new_conn.first.width > 0) {
190 new_conn.first.optimize();
191 new_conn.second.optimize();
192 used_signals.add(new_conn.first);
193 used_signals.add(new_conn.second);
194 module->connections.push_back(new_conn);
195 }
196 }
197 } else {
198 if (!used_signals.check_any(RTLIL::SigSpec(wire)))
199 del_wires.push_back(wire);
200 }
201 RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire));
202 if (!used_signals_nodrivers.check_any(sig)) {
203 std::string unused_bits;
204 sig.expand();
205 for (size_t i = 0; i < sig.chunks.size(); i++) {
206 if (sig.chunks[i].wire == NULL)
207 continue;
208 if (!used_signals_nodrivers.check_any(sig)) {
209 if (!unused_bits.empty())
210 unused_bits += " ";
211 unused_bits += stringf("%zd", i);
212 }
213 }
214 if (unused_bits.empty() || wire->port_id != 0)
215 wire->attributes.erase("\\unused_bits");
216 else
217 wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
218 } else {
219 wire->attributes.erase("\\unused_bits");
220 }
221 }
222
223 int del_wires_count = 0;
224 for (auto wire : del_wires)
225 if (!used_signals.check_any(RTLIL::SigSpec(wire))) {
226 if (check_public_name(wire->name) && verbose) {
227 log(" removing unused non-port wire %s.\n", wire->name.c_str());
228 del_wires_count++;
229 }
230 module->wires.erase(wire->name);
231 count_rm_wires++;
232 delete wire;
233 }
234
235 if (del_wires_count > 0)
236 log(" removed %d unused temporary wires.\n", del_wires_count);
237 }
238
239 static void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose)
240 {
241 if (verbose)
242 log("Finding unused cells or wires in module %s..\n", module->name.c_str());
243
244 rmunused_module_cells(module, verbose);
245 rmunused_module_signals(module, purge_mode, verbose);
246 }
247
248 struct OptCleanPass : public Pass {
249 OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
250 virtual void help()
251 {
252 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
253 log("\n");
254 log(" opt_clean [options] [selection]\n");
255 log("\n");
256 log("This pass identifies wires and cells that are unused and removes them. Other\n");
257 log("passes often remove cells but leave the wires in the design or reconnect the\n");
258 log("wires but leave the old cells in the design. This pass can be used to clean up\n");
259 log("after the passes that do the actual work.\n");
260 log("\n");
261 log("This pass only operates on completely selected modules without processes.\n");
262 log("\n");
263 log(" -purge\n");
264 log(" also remove internal nets if they have a public name\n");
265 log("\n");
266 }
267 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
268 {
269 bool purge_mode = false;
270
271 log_header("Executing OPT_CLEAN pass (remove unused cells and wires).\n");
272 log_push();
273
274 size_t argidx;
275 for (argidx = 1; argidx < args.size(); argidx++) {
276 if (args[argidx] == "-purge") {
277 purge_mode = true;
278 continue;
279 }
280 }
281 extra_args(args, argidx, design);
282
283 ct.setup_internals();
284 ct.setup_internals_mem();
285 ct.setup_stdcells();
286 ct.setup_stdcells_mem();
287
288 for (auto &mod_it : design->modules) {
289 if (!design->selected_whole_module(mod_it.first)) {
290 if (design->selected(mod_it.second))
291 log("Skipping module %s as it is only partially selected.\n", id2cstr(mod_it.second->name));
292 continue;
293 }
294 if (mod_it.second->processes.size() > 0) {
295 log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str());
296 } else {
297 rmunused_module(mod_it.second, purge_mode, true);
298 }
299 }
300
301 ct.clear();
302 log_pop();
303 }
304 } OptCleanPass;
305
306 struct CleanPass : public Pass {
307 CleanPass() : Pass("clean", "remove unused cells and wires") { }
308 virtual void help()
309 {
310 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
311 log("\n");
312 log(" clean [selection]\n");
313 log("\n");
314 log("This is identical to opt_clean, but less verbose.\n");
315 log("\n");
316 }
317 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
318 {
319 extra_args(args, 1, design);
320
321 ct.setup_internals();
322 ct.setup_internals_mem();
323 ct.setup_stdcells();
324 ct.setup_stdcells_mem();
325
326 count_rm_cells = 0;
327 count_rm_wires = 0;
328
329 for (auto &mod_it : design->modules) {
330 if (design->selected_whole_module(mod_it.first) && mod_it.second->processes.size() == 0)
331 do {
332 OPT_DID_SOMETHING = false;
333 rmunused_module(mod_it.second, false, false);
334 } while (OPT_DID_SOMETHING);
335 }
336
337 if (count_rm_cells > 0 || count_rm_wires > 0)
338 log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
339
340 ct.clear();
341 }
342 } CleanPass;
343