yosys-master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
opt_share.cc
Go to the documentation of this file.
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 "kernel/register.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/log.h"
23 #include "kernel/celltypes.h"
24 #include "libs/sha1/sha1.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <set>
28 
29 #define USE_CELL_HASH_CACHE
30 
33 
35 {
40 
43 #ifdef USE_CELL_HASH_CACHE
44  std::map<const RTLIL::Cell*, std::string> cell_hash_cache;
45 #endif
46 
47 #ifdef USE_CELL_HASH_CACHE
48  std::string int_to_hash_string(unsigned int v)
49  {
50  if (v == 0)
51  return "0";
52  std::string str = "";
53  while (v > 0) {
54  str += 'a' + (v & 15);
55  v = v >> 4;
56  }
57  return str;
58  }
59 
61  {
62  if (cell_hash_cache.count(cell) > 0)
63  return cell_hash_cache[cell];
64 
65  std::string hash_string = cell->type.str() + "\n";
66 
67  for (auto &it : cell->parameters)
68  hash_string += "P " + it.first.str() + "=" + it.second.as_string() + "\n";
69 
70  const std::map<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections();
71  std::map<RTLIL::IdString, RTLIL::SigSpec> alt_conn;
72 
73  if (cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || cell->type == "$add" || cell->type == "$mul" ||
74  cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") {
75  alt_conn = *conn;
76  if (assign_map(alt_conn.at("\\A")) < assign_map(alt_conn.at("\\B"))) {
77  alt_conn["\\A"] = conn->at("\\B");
78  alt_conn["\\B"] = conn->at("\\A");
79  }
80  conn = &alt_conn;
81  } else
82  if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor") {
83  alt_conn = *conn;
84  assign_map.apply(alt_conn.at("\\A"));
85  alt_conn.at("\\A").sort();
86  conn = &alt_conn;
87  } else
88  if (cell->type == "$reduce_and" || cell->type == "$reduce_or" || cell->type == "$reduce_bool") {
89  alt_conn = *conn;
90  assign_map.apply(alt_conn.at("\\A"));
91  alt_conn.at("\\A").sort_and_unify();
92  conn = &alt_conn;
93  }
94 
95  for (auto &it : *conn) {
96  if (ct.cell_output(cell->type, it.first))
97  continue;
98  RTLIL::SigSpec sig = it.second;
99  assign_map.apply(sig);
100  hash_string += "C " + it.first.str() + "=";
101  for (auto &chunk : sig.chunks()) {
102  if (chunk.wire)
103  hash_string += "{" + chunk.wire->name.str() + " " +
104  int_to_hash_string(chunk.offset) + " " +
105  int_to_hash_string(chunk.width) + "}";
106  else
107  hash_string += RTLIL::Const(chunk.data).as_string();
108  }
109  hash_string += "\n";
110  }
111 
112  cell_hash_cache[cell] = sha1(hash_string);
113  return cell_hash_cache[cell];
114  }
115 #endif
116 
117  bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2, bool &lt)
118  {
119 #ifdef USE_CELL_HASH_CACHE
120  std::string hash1 = hash_cell_parameters_and_connections(cell1);
121  std::string hash2 = hash_cell_parameters_and_connections(cell2);
122 
123  if (hash1 != hash2) {
124  lt = hash1 < hash2;
125  return true;
126  }
127 #endif
128 
129  if (cell1->parameters != cell2->parameters) {
130  lt = cell1->parameters < cell2->parameters;
131  return true;
132  }
133 
134  std::map<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections();
135  std::map<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections();
136 
137  for (auto &it : conn1) {
138  if (ct.cell_output(cell1->type, it.first))
139  it.second = RTLIL::SigSpec();
140  else
141  assign_map.apply(it.second);
142  }
143 
144  for (auto &it : conn2) {
145  if (ct.cell_output(cell2->type, it.first))
146  it.second = RTLIL::SigSpec();
147  else
148  assign_map.apply(it.second);
149  }
150 
151  if (cell1->type == "$and" || cell1->type == "$or" || cell1->type == "$xor" || cell1->type == "$xnor" || cell1->type == "$add" || cell1->type == "$mul" ||
152  cell1->type == "$logic_and" || cell1->type == "$logic_or" || cell1->type == "$_AND_" || cell1->type == "$_OR_" || cell1->type == "$_XOR_") {
153  if (conn1.at("\\A") < conn1.at("\\B")) {
154  RTLIL::SigSpec tmp = conn1["\\A"];
155  conn1["\\A"] = conn1["\\B"];
156  conn1["\\B"] = tmp;
157  }
158  if (conn2.at("\\A") < conn2.at("\\B")) {
159  RTLIL::SigSpec tmp = conn2["\\A"];
160  conn2["\\A"] = conn2["\\B"];
161  conn2["\\B"] = tmp;
162  }
163  } else
164  if (cell1->type == "$reduce_xor" || cell1->type == "$reduce_xnor") {
165  conn1["\\A"].sort();
166  conn2["\\A"].sort();
167  } else
168  if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") {
169  conn1["\\A"].sort_and_unify();
170  conn2["\\A"].sort_and_unify();
171  }
172 
173  if (conn1 != conn2) {
174  lt = conn1 < conn2;
175  return true;
176  }
177 
178  if (cell1->type.substr(0, 1) == "$" && conn1.count("\\Q") != 0) {
179  std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort("\\Q")).to_sigbit_vector();
180  std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort("\\Q")).to_sigbit_vector();
181  for (size_t i = 0; i < q1.size(); i++)
182  if ((q1.at(i).wire == NULL || q2.at(i).wire == NULL) && q1.at(i) != q2.at(i)) {
183  lt = q1.at(i) < q2.at(i);
184  return true;
185  }
186  }
187 
188  return false;
189  }
190 
191  bool compare_cells(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2)
192  {
193  if (cell1->type != cell2->type)
194  return cell1->type < cell2->type;
195 
196  if (!ct.cell_known(cell1->type))
197  return cell1 < cell2;
198 
199  if (cell1->has_keep_attr() || cell2->has_keep_attr())
200  return cell1 < cell2;
201 
202  bool lt;
203  if (compare_cell_parameters_and_connections(cell1, cell2, lt))
204  return lt;
205 
206  return false;
207  }
208 
209  struct CompareCells {
211  CompareCells(OptShareWorker *that) : that(that) {}
212  bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const {
213  return that->compare_cells(cell1, cell2);
214  }
215  };
216 
218  design(design), module(module), assign_map(module)
219  {
220  total_count = 0;
223  ct.setup_stdcells();
225 
226  if (mode_nomux) {
227  ct.cell_types.erase("$mux");
228  ct.cell_types.erase("$pmux");
229  }
230 
231  log("Finding identical cells in module `%s'.\n", module->name.c_str());
232  assign_map.set(module);
233 
234  dff_init_map.set(module);
235  for (auto &it : module->wires_)
236  if (it.second->attributes.count("\\init") != 0)
237  dff_init_map.add(it.second, it.second->attributes.at("\\init"));
238 
239  bool did_something = true;
240  while (did_something)
241  {
242 #ifdef USE_CELL_HASH_CACHE
243  cell_hash_cache.clear();
244 #endif
245  std::vector<RTLIL::Cell*> cells;
246  cells.reserve(module->cells_.size());
247  for (auto &it : module->cells_) {
248  if (ct.cell_known(it.second->type) && design->selected(module, it.second))
249  cells.push_back(it.second);
250  }
251 
252  did_something = false;
253  std::map<RTLIL::Cell*, RTLIL::Cell*, CompareCells> sharemap(CompareCells(this));
254  for (auto cell : cells)
255  {
256  if (sharemap.count(cell) > 0) {
257  did_something = true;
258  log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
259  for (auto &it : cell->connections()) {
260  if (ct.cell_output(cell->type, it.first)) {
261  RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first);
262  log(" Redirecting output %s: %s = %s\n", it.first.c_str(),
263  log_signal(it.second), log_signal(other_sig));
264  module->connect(RTLIL::SigSig(it.second, other_sig));
265  assign_map.add(it.second, other_sig);
266  }
267  }
268  log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
269  module->remove(cell);
270  total_count++;
271  } else {
272  sharemap[cell] = cell;
273  }
274  }
275  }
276  }
277 };
278 
279 struct OptSharePass : public Pass {
280  OptSharePass() : Pass("opt_share", "consolidate identical cells") { }
281  virtual void help()
282  {
283  // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
284  log("\n");
285  log(" opt_share [-nomux] [selection]\n");
286  log("\n");
287  log("This pass identifies cells with identical type and input signals. Such cells\n");
288  log("are then merged to one cell.\n");
289  log("\n");
290  log(" -nomux\n");
291  log(" Do not merge MUX cells.\n");
292  log("\n");
293  }
294  virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
295  {
296  log_header("Executing OPT_SHARE pass (detect identical cells).\n");
297 
298  bool mode_nomux = false;
299 
300  size_t argidx;
301  for (argidx = 1; argidx < args.size(); argidx++) {
302  std::string arg = args[argidx];
303  if (arg == "-nomux") {
304  mode_nomux = true;
305  continue;
306  }
307  break;
308  }
309  extra_args(args, argidx, design);
310 
311  int total_count = 0;
312  for (auto &mod_it : design->modules_) {
313  if (!design->selected(mod_it.second))
314  continue;
315  OptShareWorker worker(design, mod_it.second, mode_nomux);
316  total_count += worker.total_count;
317  }
318 
319  if (total_count)
320  design->scratchpad_set_bool("opt.did_something", true);
321  log("Removed a total of %d cells.\n", total_count);
322  }
323 } OptSharePass;
324 
const char * c_str() const
Definition: rtlil.h:178
bool selected(T1 *module) const
Definition: rtlil.h:551
std::string str() const
Definition: rtlil.h:182
void sort()
Definition: rtlil.cc:2284
void setup_stdcells()
Definition: celltypes.h:132
bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const
Definition: opt_share.cc:212
void setup_internals_mem()
Definition: celltypes.h:115
void log_header(const char *format,...)
Definition: log.cc:188
std::map< RTLIL::IdString, RTLIL::Wire * > wires_
Definition: rtlil.h:595
const char * log_signal(const RTLIL::SigSpec &sig, bool autoint)
Definition: log.cc:269
RTLIL::Module * module
Definition: opt_share.cc:37
RTLIL::IdString type
Definition: rtlil.h:854
std::map< RTLIL::IdString, RTLIL::Const > parameters
Definition: rtlil.h:856
void apply(RTLIL::SigBit &bit) const
Definition: sigtools.h:383
std::string int_to_hash_string(unsigned int v)
Definition: opt_share.cc:48
USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN bool did_something
Definition: opt_const.cc:32
std::map< RTLIL::IdString, CellType > cell_types
Definition: celltypes.h:36
void set(RTLIL::Module *module)
Definition: sigtools.h:273
bool cell_known(RTLIL::IdString type)
Definition: celltypes.h:188
void connect(const RTLIL::SigSig &conn)
Definition: rtlil.cc:1278
std::string as_string() const
Definition: rtlil.cc:116
#define PRIVATE_NAMESPACE_BEGIN
Definition: yosys.h:97
std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell)
Definition: opt_share.cc:60
bool cell_output(RTLIL::IdString type, RTLIL::IdString port)
Definition: celltypes.h:193
const RTLIL::SigSpec & getPort(RTLIL::IdString portname) const
Definition: rtlil.cc:1809
SigMap assign_map
Definition: opt_share.cc:38
OptSharePass OptSharePass
RTLIL::IdString name
Definition: rtlil.h:599
CompareCells(OptShareWorker *that)
Definition: opt_share.cc:211
OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux)
Definition: opt_share.cc:217
SigMap dff_init_map
Definition: opt_share.cc:39
bool compare_cells(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2)
Definition: opt_share.cc:191
#define PRIVATE_NAMESPACE_END
Definition: yosys.h:98
Definition: register.h:27
std::string substr(size_t pos=0, size_t len=std::string::npos) const
Definition: rtlil.h:208
#define USING_YOSYS_NAMESPACE
Definition: yosys.h:102
std::map< RTLIL::IdString, RTLIL::Module * > modules_
Definition: rtlil.h:507
#define NULL
std::map< RTLIL::IdString, RTLIL::Cell * > cells_
Definition: rtlil.h:596
void remove(const std::set< RTLIL::Wire * > &wires)
Definition: rtlil.cc:1158
CellTypes ct
Definition: opt_share.cc:41
void log(const char *format,...)
Definition: log.cc:180
std::string sha1(const std::string &string)
Definition: sha1.cpp:270
void setup_internals()
Definition: celltypes.h:83
void scratchpad_set_bool(std::string varname, bool value)
Definition: rtlil.cc:296
void setup_stdcells_mem()
Definition: celltypes.h:149
virtual void help()
Definition: opt_share.cc:281
virtual void execute(std::vector< std::string > args, RTLIL::Design *design)
Definition: opt_share.cc:294
void add(RTLIL::SigSpec from, RTLIL::SigSpec to)
Definition: sigtools.h:347
bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2, bool &lt)
Definition: opt_share.cc:117
std::map< const RTLIL::Cell *, std::string > cell_hash_cache
Definition: opt_share.cc:44
void extra_args(std::vector< std::string > args, size_t argidx, RTLIL::Design *design, bool select=true)
Definition: register.cc:128
std::pair< SigSpec, SigSpec > SigSig
Definition: rtlil.h:71
const std::map< RTLIL::IdString, RTLIL::SigSpec > & connections() const
Definition: rtlil.cc:1814
bool has_keep_attr() const
Definition: rtlil.h:875
RTLIL::Design * design
Definition: opt_share.cc:36
const std::vector< RTLIL::SigChunk > & chunks() const
Definition: rtlil.h:1016