yosys-master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
opt_muxtree.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 <stdlib.h>
25 #include <stdio.h>
26 #include <set>
27 
30 
31 using RTLIL::id2cstr;
32 
34 {
39 
40  struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
41  bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
42  bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
43  };
44 
45 
46  struct bitinfo_t {
47  int num;
50  std::vector<int> mux_users;
51  std::vector<int> mux_drivers;
52  };
53 
54  std::map<bitDef_t, int> bit2num;
55  std::vector<bitinfo_t> bit2info;
56 
57  struct portinfo_t {
58  std::vector<int> ctrl_sigs;
59  std::vector<int> input_sigs;
60  std::vector<int> input_muxes;
62  bool enabled;
63  };
64 
65  struct muxinfo_t {
67  std::vector<portinfo_t> ports;
68  };
69 
70  std::vector<muxinfo_t> mux2info;
71 
73  design(design), module(module), assign_map(module), removed_count(0)
74  {
75  log("Running muxtree optimizier on module %s..\n", module->name.c_str());
76 
77  log(" Creating internal representation of mux trees.\n");
78 
79  // Populate bit2info[]:
80  // .seen_non_mux
81  // .mux_users
82  // .mux_drivers
83  // Populate mux2info[].ports[]:
84  // .ctrl_sigs
85  // .input_sigs
86  // .const_activated
87  for (auto cell : module->cells())
88  {
89  if (cell->type == "$mux" || cell->type == "$pmux")
90  {
91  RTLIL::SigSpec sig_a = cell->getPort("\\A");
92  RTLIL::SigSpec sig_b = cell->getPort("\\B");
93  RTLIL::SigSpec sig_s = cell->getPort("\\S");
94  RTLIL::SigSpec sig_y = cell->getPort("\\Y");
95 
96  muxinfo_t muxinfo;
97  muxinfo.cell = cell;
98 
99  for (int i = 0; i < sig_s.size(); i++) {
100  RTLIL::SigSpec sig = sig_b.extract(i*sig_a.size(), sig_a.size());
101  RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1));
102  portinfo_t portinfo;
103  for (int idx : sig2bits(sig)) {
104  add_to_list(bit2info[idx].mux_users, mux2info.size());
105  add_to_list(portinfo.input_sigs, idx);
106  }
107  for (int idx : sig2bits(ctrl_sig))
108  add_to_list(portinfo.ctrl_sigs, idx);
109  portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool();
110  portinfo.enabled = false;
111  muxinfo.ports.push_back(portinfo);
112  }
113 
114  portinfo_t portinfo;
115  for (int idx : sig2bits(sig_a)) {
116  add_to_list(bit2info[idx].mux_users, mux2info.size());
117  add_to_list(portinfo.input_sigs, idx);
118  }
119  portinfo.const_activated = false;
120  portinfo.enabled = false;
121  muxinfo.ports.push_back(portinfo);
122 
123  for (int idx : sig2bits(sig_y))
125 
126  for (int idx : sig2bits(sig_s))
127  bit2info[idx].seen_non_mux = true;
128 
129  mux2info.push_back(muxinfo);
130  }
131  else
132  {
133  for (auto &it : cell->connections()) {
134  for (int idx : sig2bits(it.second))
135  bit2info[idx].seen_non_mux = true;
136  }
137  }
138  }
139  for (auto wire : module->wires()) {
140  if (wire->port_output)
141  for (int idx : sig2bits(RTLIL::SigSpec(wire)))
142  bit2info[idx].seen_non_mux = true;
143  }
144 
145  if (mux2info.size() == 0) {
146  log(" No muxes found in this module.\n");
147  return;
148  }
149 
150  // Populate mux2info[].ports[]:
151  // .input_muxes
152  for (size_t i = 0; i < bit2info.size(); i++)
153  for (int j : bit2info[i].mux_users)
154  for (auto &p : mux2info[j].ports) {
155  if (is_in_list(p.input_sigs, i))
156  for (int k : bit2info[i].mux_drivers)
157  add_to_list(p.input_muxes, k);
158  }
159 
160  log(" Evaluating internal representation of mux trees.\n");
161 
162  std::set<int> root_muxes;
163  for (auto &bi : bit2info) {
164  if (!bi.seen_non_mux)
165  continue;
166  for (int mux_idx : bi.mux_drivers)
167  root_muxes.insert(mux_idx);
168  }
169  for (int mux_idx : root_muxes)
170  eval_root_mux(mux_idx);
171 
172  log(" Analyzing evaluation results.\n");
173 
174  for (auto &mi : mux2info)
175  {
176  std::vector<int> live_ports;
177  for (int port_idx = 0; port_idx < GetSize(mi.ports); port_idx++) {
178  portinfo_t &pi = mi.ports[port_idx];
179  if (pi.enabled) {
180  live_ports.push_back(port_idx);
181  } else {
182  log(" dead port %d/%d on %s %s.\n", port_idx+1, GetSize(mi.ports),
183  mi.cell->type.c_str(), mi.cell->name.c_str());
184  removed_count++;
185  }
186  }
187 
188  if (live_ports.size() == mi.ports.size())
189  continue;
190 
191  if (live_ports.size() == 0) {
192  module->remove(mi.cell);
193  continue;
194  }
195 
196  RTLIL::SigSpec sig_a = mi.cell->getPort("\\A");
197  RTLIL::SigSpec sig_b = mi.cell->getPort("\\B");
198  RTLIL::SigSpec sig_s = mi.cell->getPort("\\S");
199  RTLIL::SigSpec sig_y = mi.cell->getPort("\\Y");
200 
201  RTLIL::SigSpec sig_ports = sig_b;
202  sig_ports.append(sig_a);
203 
204  if (live_ports.size() == 1)
205  {
206  RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.size(), sig_a.size());
207  module->connect(RTLIL::SigSig(sig_y, sig_in));
208  module->remove(mi.cell);
209  }
210  else
211  {
212  RTLIL::SigSpec new_sig_a, new_sig_b, new_sig_s;
213 
214  for (size_t i = 0; i < live_ports.size(); i++) {
215  RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*sig_a.size(), sig_a.size());
216  if (i == live_ports.size()-1) {
217  new_sig_a = sig_in;
218  } else {
219  new_sig_b.append(sig_in);
220  new_sig_s.append(sig_s.extract(live_ports[i], 1));
221  }
222  }
223 
224  mi.cell->setPort("\\A", new_sig_a);
225  mi.cell->setPort("\\B", new_sig_b);
226  mi.cell->setPort("\\S", new_sig_s);
227  if (new_sig_s.size() == 1) {
228  mi.cell->type = "$mux";
229  mi.cell->parameters.erase("\\S_WIDTH");
230  } else {
231  mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size());
232  }
233  }
234  }
235  }
236 
237  bool list_is_subset(const std::vector<int> &sub, const std::vector<int> &super)
238  {
239  for (int v : sub)
240  if (!is_in_list(super, v))
241  return false;
242  return true;
243  }
244 
245  bool is_in_list(const std::vector<int> &list, int value)
246  {
247  for (int v : list)
248  if (v == value)
249  return true;
250  return false;
251  }
252 
253  void add_to_list(std::vector<int> &list, int value)
254  {
255  if (!is_in_list(list, value))
256  list.push_back(value);
257  }
258 
259  std::vector<int> sig2bits(RTLIL::SigSpec sig)
260  {
261  std::vector<int> results;
262  assign_map.apply(sig);
263  for (auto &bit : sig)
264  if (bit.wire != NULL) {
265  if (bit2num.count(bit) == 0) {
266  bitinfo_t info;
267  info.num = bit2info.size();
268  info.bit = bit;
269  info.seen_non_mux = false;
270  bit2info.push_back(info);
271  bit2num[info.bit] = info.num;
272  }
273  results.push_back(bit2num[bit]);
274  }
275  return results;
276  }
277 
278  struct knowledge_t
279  {
280  // database of known inactive signals
281  // the 2nd integer is a reference counter used to manage the
282  // list. when it is non-zero the signal in known to be inactive
283  std::map<int, int> known_inactive;
284 
285  // database of known active signals
286  // the 2nd dimension is the list of or-ed signals. so we know that
287  // for each i there is a j so that known_active[i][j] points to an
288  // inactive control signal.
289  std::vector<std::vector<int>> known_active;
290 
291  // this is just used to keep track of visited muxes in order to prohibit
292  // endless recursion in mux loops
293  std::set<int> visited_muxes;
294  };
295 
296  void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx)
297  {
298  muxinfo_t &muxinfo = mux2info[mux_idx];
299  muxinfo.ports[port_idx].enabled = true;
300 
301  for (size_t i = 0; i < muxinfo.ports.size(); i++) {
302  if (int(i) == port_idx)
303  continue;
304  for (int b : muxinfo.ports[i].ctrl_sigs)
305  knowledge.known_inactive[b]++;
306  }
307 
308  if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated)
309  knowledge.known_active.push_back(muxinfo.ports[port_idx].ctrl_sigs);
310 
311  std::vector<int> parent_muxes;
312  for (int m : muxinfo.ports[port_idx].input_muxes) {
313  if (knowledge.visited_muxes.count(m) > 0)
314  continue;
315  knowledge.visited_muxes.insert(m);
316  parent_muxes.push_back(m);
317  }
318  for (int m : parent_muxes)
319  eval_mux(knowledge, m);
320  for (int m : parent_muxes)
321  knowledge.visited_muxes.erase(m);
322 
323  if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated)
324  knowledge.known_active.pop_back();
325 
326  for (size_t i = 0; i < muxinfo.ports.size(); i++) {
327  if (int(i) == port_idx)
328  continue;
329  for (int b : muxinfo.ports[i].ctrl_sigs)
330  knowledge.known_inactive[b]--;
331  }
332  }
333 
334  void eval_mux(knowledge_t &knowledge, int mux_idx)
335  {
336  muxinfo_t &muxinfo = mux2info[mux_idx];
337 
338  // if there is a constant activated port we just use it
339  for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++)
340  {
341  portinfo_t &portinfo = muxinfo.ports[port_idx];
342  if (portinfo.const_activated) {
343  eval_mux_port(knowledge, mux_idx, port_idx);
344  return;
345  }
346  }
347 
348  // compare ports with known_active signals. if we find a match, only this
349  // port can be active. do not include the last port (its the default port
350  // that has no control signals).
351  for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++)
352  {
353  portinfo_t &portinfo = muxinfo.ports[port_idx];
354  for (size_t i = 0; i < knowledge.known_active.size(); i++) {
355  if (list_is_subset(knowledge.known_active[i], portinfo.ctrl_sigs)) {
356  eval_mux_port(knowledge, mux_idx, port_idx);
357  return;
358  }
359  }
360  }
361 
362  // compare ports with known_inactive and known_active signals. If all control
363  // signals of the port are know_inactive or if the control signals of all other
364  // ports are known_active this port can't be activated. this loop includes the
365  // default port but no known_inactive match is performed on the default port.
366  for (size_t port_idx = 0; port_idx < muxinfo.ports.size(); port_idx++)
367  {
368  portinfo_t &portinfo = muxinfo.ports[port_idx];
369 
370  if (port_idx < muxinfo.ports.size()-1) {
371  bool found_non_known_inactive = false;
372  for (int i : portinfo.ctrl_sigs)
373  if (knowledge.known_inactive[i] == 0)
374  found_non_known_inactive = true;
375  if (!found_non_known_inactive)
376  continue;
377  }
378 
379  bool port_active = true;
380  std::vector<int> other_ctrl_sig;
381  for (size_t i = 0; i < muxinfo.ports.size()-1; i++) {
382  if (i == port_idx)
383  continue;
384  other_ctrl_sig.insert(other_ctrl_sig.end(),
385  muxinfo.ports[i].ctrl_sigs.begin(), muxinfo.ports[i].ctrl_sigs.end());
386  }
387  for (size_t i = 0; i < knowledge.known_active.size(); i++) {
388  if (list_is_subset(knowledge.known_active[i], other_ctrl_sig))
389  port_active = false;
390  }
391  if (port_active)
392  eval_mux_port(knowledge, mux_idx, port_idx);
393  }
394  }
395 
396  void eval_root_mux(int mux_idx)
397  {
398  knowledge_t knowledge;
399  knowledge.visited_muxes.insert(mux_idx);
400  eval_mux(knowledge, mux_idx);
401  }
402 };
403 
404 struct OptMuxtreePass : public Pass {
405  OptMuxtreePass() : Pass("opt_muxtree", "eliminate dead trees in multiplexer trees") { }
406  virtual void help()
407  {
408  // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
409  log("\n");
410  log(" opt_muxtree [selection]\n");
411  log("\n");
412  log("This pass analyzes the control signals for the multiplexer trees in the design\n");
413  log("and identifies inputs that can never be active. It then removes this dead\n");
414  log("branches from the multiplexer trees.\n");
415  log("\n");
416  log("This pass only operates on completely selected modules without processes.\n");
417  log("\n");
418  }
419  virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
420  {
421  log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
422  extra_args(args, 1, design);
423 
424  int total_count = 0;
425  for (auto mod : design->modules()) {
426  if (!design->selected_whole_module(mod)) {
427  if (design->selected(mod))
428  log("Skipping module %s as it is only partially selected.\n", log_id(mod));
429  continue;
430  }
431  if (mod->processes.size() > 0) {
432  log("Skipping module %s as it contains processes.\n", log_id(mod));
433  } else {
434  OptMuxtreeWorker worker(design, mod);
435  total_count += worker.removed_count;
436  }
437  }
438  if (total_count)
439  design->scratchpad_set_bool("opt.did_something", true);
440  log("Removed %d multiplexer ports.\n", total_count);
441  }
443 
const char * c_str() const
Definition: rtlil.h:178
bool selected(T1 *module) const
Definition: rtlil.h:551
std::vector< int > input_sigs
Definition: opt_muxtree.cc:59
std::vector< int > input_muxes
Definition: opt_muxtree.cc:60
RTLIL::Design * design
Definition: opt_muxtree.cc:35
void log_header(const char *format,...)
Definition: log.cc:188
std::vector< bitinfo_t > bit2info
Definition: opt_muxtree.cc:55
RTLIL::Module * module
Definition: opt_muxtree.cc:36
bool as_bool() const
Definition: rtlil.cc:2818
static std::string idx(std::string str)
Definition: test_autotb.cc:57
RTLIL::ObjRange< RTLIL::Wire * > wires()
Definition: rtlil.h:640
bitDef_t(const RTLIL::SigBit &bit)
Definition: opt_muxtree.cc:42
int size() const
Definition: rtlil.h:1019
virtual void execute(std::vector< std::string > args, RTLIL::Design *design)
Definition: opt_muxtree.cc:419
bool list_is_subset(const std::vector< int > &sub, const std::vector< int > &super)
Definition: opt_muxtree.cc:237
std::vector< muxinfo_t > mux2info
Definition: opt_muxtree.cc:70
std::vector< int > sig2bits(RTLIL::SigSpec sig)
Definition: opt_muxtree.cc:259
void add_to_list(std::vector< int > &list, int value)
Definition: opt_muxtree.cc:253
void apply(RTLIL::SigBit &bit) const
Definition: sigtools.h:383
virtual void help()
Definition: opt_muxtree.cc:406
OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module)
Definition: opt_muxtree.cc:72
void connect(const RTLIL::SigSig &conn)
Definition: rtlil.cc:1278
std::vector< std::vector< int > > known_active
Definition: opt_muxtree.cc:289
#define PRIVATE_NAMESPACE_BEGIN
Definition: yosys.h:97
int GetSize(RTLIL::Wire *wire)
Definition: yosys.cc:334
bool is_fully_const() const
Definition: rtlil.cc:2763
void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx)
Definition: opt_muxtree.cc:296
RTLIL::IdString name
Definition: rtlil.h:599
bool selected_whole_module(RTLIL::IdString mod_name) const
Definition: rtlil.cc:388
#define PRIVATE_NAMESPACE_END
Definition: yosys.h:98
Definition: register.h:27
static const char * id2cstr(const RTLIL::IdString &str)
Definition: rtlil.h:267
std::map< bitDef_t, int > bit2num
Definition: opt_muxtree.cc:54
#define USING_YOSYS_NAMESPACE
Definition: yosys.h:102
void eval_root_mux(int mux_idx)
Definition: opt_muxtree.cc:396
RTLIL::ObjRange< RTLIL::Cell * > cells()
Definition: rtlil.h:641
RTLIL::ObjRange< RTLIL::Module * > modules()
Definition: rtlil.cc:249
#define NULL
std::vector< int > ctrl_sigs
Definition: opt_muxtree.cc:58
void remove(const std::set< RTLIL::Wire * > &wires)
Definition: rtlil.cc:1158
SigSet< RTLIL::Cell * > mux_drivers
Definition: opt_rmdff.cc:30
OptMuxtreePass OptMuxtreePass
std::vector< int > mux_users
Definition: opt_muxtree.cc:50
void log(const char *format,...)
Definition: log.cc:180
std::vector< portinfo_t > ports
Definition: opt_muxtree.cc:67
RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other=NULL) const
Definition: rtlil.cc:2414
std::vector< int > mux_drivers
Definition: opt_muxtree.cc:51
void eval_mux(knowledge_t &knowledge, int mux_idx)
Definition: opt_muxtree.cc:334
void scratchpad_set_bool(std::string varname, bool value)
Definition: rtlil.cc:296
void append(const RTLIL::SigSpec &signal)
Definition: rtlil.cc:2523
bool is_in_list(const std::vector< int > &list, int value)
Definition: opt_muxtree.cc:245
void extra_args(std::vector< std::string > args, size_t argidx, RTLIL::Design *design, bool select=true)
Definition: register.cc:128
std::map< int, int > known_inactive
Definition: opt_muxtree.cc:283
std::pair< SigSpec, SigSpec > SigSig
Definition: rtlil.h:71
const char * log_id(RTLIL::IdString str)
Definition: log.cc:283