Merge pull request #1866 from boqwxp/cleanup_test_autotb
[yosys.git] / passes / techmap / tribuf.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 "kernel/yosys.h"
21 #include "kernel/sigtools.h"
22
23 USING_YOSYS_NAMESPACE
24 PRIVATE_NAMESPACE_BEGIN
25
26 struct TribufConfig {
27 bool merge_mode;
28 bool logic_mode;
29
30 TribufConfig() {
31 merge_mode = false;
32 logic_mode = false;
33 }
34 };
35
36 struct TribufWorker {
37 Module *module;
38 SigMap sigmap;
39 const TribufConfig &config;
40
41 TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config)
42 {
43 }
44
45 static bool is_all_z(SigSpec sig)
46 {
47 for (auto bit : sig)
48 if (bit != State::Sz)
49 return false;
50 return true;
51 }
52
53 void run()
54 {
55 dict<SigSpec, vector<Cell*>> tribuf_cells;
56 pool<SigBit> output_bits;
57
58 if (config.logic_mode)
59 for (auto wire : module->wires())
60 if (wire->port_output)
61 for (auto bit : sigmap(wire))
62 output_bits.insert(bit);
63
64 for (auto cell : module->selected_cells())
65 {
66 if (cell->type == ID($tribuf))
67 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
68
69 if (cell->type == ID($_TBUF_))
70 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
71
72 if (cell->type.in(ID($mux), ID($_MUX_)))
73 {
74 IdString en_port = cell->type == ID($mux) ? ID::EN : ID::E;
75 IdString tri_type = cell->type == ID($mux) ? ID($tribuf) : ID($_TBUF_);
76
77 if (is_all_z(cell->getPort(ID::A)) && is_all_z(cell->getPort(ID::B))) {
78 module->remove(cell);
79 continue;
80 }
81
82 if (is_all_z(cell->getPort(ID::A))) {
83 cell->setPort(ID::A, cell->getPort(ID::B));
84 cell->setPort(en_port, cell->getPort(ID::S));
85 cell->unsetPort(ID::B);
86 cell->unsetPort(ID::S);
87 cell->type = tri_type;
88 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
89 module->design->scratchpad_set_bool("tribuf.added_something", true);
90 continue;
91 }
92
93 if (is_all_z(cell->getPort(ID::B))) {
94 cell->setPort(en_port, module->Not(NEW_ID, cell->getPort(ID::S)));
95 cell->unsetPort(ID::B);
96 cell->unsetPort(ID::S);
97 cell->type = tri_type;
98 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
99 module->design->scratchpad_set_bool("tribuf.added_something", true);
100 continue;
101 }
102 }
103 }
104
105 if (config.merge_mode || config.logic_mode)
106 {
107 for (auto &it : tribuf_cells)
108 {
109 bool no_tribuf = false;
110
111 if (config.logic_mode) {
112 no_tribuf = true;
113 for (auto bit : it.first)
114 if (output_bits.count(bit))
115 no_tribuf = false;
116 }
117
118 if (GetSize(it.second) <= 1 && !no_tribuf)
119 continue;
120
121 SigSpec pmux_b, pmux_s;
122 for (auto cell : it.second) {
123 if (cell->type == ID($tribuf))
124 pmux_s.append(cell->getPort(ID::EN));
125 else
126 pmux_s.append(cell->getPort(ID::E));
127 pmux_b.append(cell->getPort(ID::A));
128 module->remove(cell);
129 }
130
131 SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b;
132
133 if (no_tribuf)
134 module->connect(it.first, muxout);
135 else {
136 module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
137 module->design->scratchpad_set_bool("tribuf.added_something", true);
138 }
139 }
140 }
141 }
142 };
143
144 struct TribufPass : public Pass {
145 TribufPass() : Pass("tribuf", "infer tri-state buffers") { }
146 void help() YS_OVERRIDE
147 {
148 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
149 log("\n");
150 log(" tribuf [options] [selection]\n");
151 log("\n");
152 log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n");
153 log("\n");
154 log(" -merge\n");
155 log(" merge multiple tri-state buffers driving the same net\n");
156 log(" into a single buffer.\n");
157 log("\n");
158 log(" -logic\n");
159 log(" convert tri-state buffers that do not drive output ports\n");
160 log(" to non-tristate logic. this option implies -merge.\n");
161 log("\n");
162 }
163 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
164 {
165 TribufConfig config;
166
167 log_header(design, "Executing TRIBUF pass.\n");
168
169 size_t argidx;
170 for (argidx = 1; argidx < args.size(); argidx++) {
171 if (args[argidx] == "-merge") {
172 config.merge_mode = true;
173 continue;
174 }
175 if (args[argidx] == "-logic") {
176 config.logic_mode = true;
177 continue;
178 }
179 break;
180 }
181 extra_args(args, argidx, design);
182
183 for (auto module : design->selected_modules()) {
184 TribufWorker worker(module, config);
185 worker.run();
186 }
187 }
188 } TribufPass;
189
190 PRIVATE_NAMESPACE_END