tribuf: `-formal` option: convert all to logic and detect conflicts
[yosys.git] / passes / techmap / tribuf.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
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 bool formal_mode;
30
31 TribufConfig() {
32 merge_mode = false;
33 logic_mode = false;
34 formal_mode = false;
35 }
36 };
37
38 struct TribufWorker {
39 Module *module;
40 SigMap sigmap;
41 const TribufConfig &config;
42
43 TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config)
44 {
45 }
46
47 static bool is_all_z(SigSpec sig)
48 {
49 for (auto bit : sig)
50 if (bit != State::Sz)
51 return false;
52 return true;
53 }
54
55 void run()
56 {
57 dict<SigSpec, vector<Cell*>> tribuf_cells;
58 pool<SigBit> output_bits;
59
60 if (config.logic_mode || config.formal_mode)
61 for (auto wire : module->wires())
62 if (wire->port_output)
63 for (auto bit : sigmap(wire))
64 output_bits.insert(bit);
65
66 for (auto cell : module->selected_cells())
67 {
68 if (cell->type == ID($tribuf))
69 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
70
71 if (cell->type == ID($_TBUF_))
72 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
73
74 if (cell->type.in(ID($mux), ID($_MUX_)))
75 {
76 IdString en_port = cell->type == ID($mux) ? ID::EN : ID::E;
77 IdString tri_type = cell->type == ID($mux) ? ID($tribuf) : ID($_TBUF_);
78
79 if (is_all_z(cell->getPort(ID::A)) && is_all_z(cell->getPort(ID::B))) {
80 module->remove(cell);
81 continue;
82 }
83
84 if (is_all_z(cell->getPort(ID::A))) {
85 cell->setPort(ID::A, cell->getPort(ID::B));
86 cell->setPort(en_port, cell->getPort(ID::S));
87 cell->unsetPort(ID::B);
88 cell->unsetPort(ID::S);
89 cell->type = tri_type;
90 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
91 module->design->scratchpad_set_bool("tribuf.added_something", true);
92 continue;
93 }
94
95 if (is_all_z(cell->getPort(ID::B))) {
96 cell->setPort(en_port, module->Not(NEW_ID, cell->getPort(ID::S)));
97 cell->unsetPort(ID::B);
98 cell->unsetPort(ID::S);
99 cell->type = tri_type;
100 tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
101 module->design->scratchpad_set_bool("tribuf.added_something", true);
102 continue;
103 }
104 }
105 }
106
107 if (config.merge_mode || config.logic_mode || config.formal_mode)
108 {
109 for (auto &it : tribuf_cells)
110 {
111 bool no_tribuf = false;
112
113 if (config.logic_mode && !config.formal_mode) {
114 no_tribuf = true;
115 for (auto bit : it.first)
116 if (output_bits.count(bit))
117 no_tribuf = false;
118 }
119
120 if (config.formal_mode)
121 no_tribuf = true;
122
123 if (GetSize(it.second) <= 1 && !no_tribuf)
124 continue;
125
126 if (config.formal_mode && GetSize(it.second) >= 2) {
127 for (auto cell : it.second) {
128 SigSpec others_s;
129
130 for (auto other_cell : it.second) {
131 if (other_cell == cell)
132 continue;
133 else if (other_cell->type == ID($tribuf))
134 others_s.append(other_cell->getPort(ID::EN));
135 else
136 others_s.append(other_cell->getPort(ID::E));
137 }
138
139 auto cell_s = cell->type == ID($tribuf) ? cell->getPort(ID::EN) : cell->getPort(ID::E);
140
141 auto other_s = module->ReduceOr(NEW_ID, others_s);
142
143 auto conflict = module->And(NEW_ID, cell_s, other_s);
144
145 std::string name = stringf("$tribuf_conflict$%s", log_id(cell->name));
146 auto assert_cell = module->addAssert(name, module->Not(NEW_ID, conflict), SigSpec(true));
147
148 assert_cell->set_src_attribute(cell->get_src_attribute());
149 assert_cell->set_bool_attribute(ID::keep);
150
151 module->design->scratchpad_set_bool("tribuf.added_something", true);
152 }
153 }
154
155 SigSpec pmux_b, pmux_s;
156 for (auto cell : it.second) {
157 if (cell->type == ID($tribuf))
158 pmux_s.append(cell->getPort(ID::EN));
159 else
160 pmux_s.append(cell->getPort(ID::E));
161 pmux_b.append(cell->getPort(ID::A));
162 module->remove(cell);
163 }
164
165 SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b;
166
167 if (no_tribuf)
168 module->connect(it.first, muxout);
169 else {
170 module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
171 module->design->scratchpad_set_bool("tribuf.added_something", true);
172 }
173 }
174 }
175 }
176 };
177
178 struct TribufPass : public Pass {
179 TribufPass() : Pass("tribuf", "infer tri-state buffers") { }
180 void help() override
181 {
182 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
183 log("\n");
184 log(" tribuf [options] [selection]\n");
185 log("\n");
186 log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n");
187 log("\n");
188 log(" -merge\n");
189 log(" merge multiple tri-state buffers driving the same net\n");
190 log(" into a single buffer.\n");
191 log("\n");
192 log(" -logic\n");
193 log(" convert tri-state buffers that do not drive output ports\n");
194 log(" to non-tristate logic. this option implies -merge.\n");
195 log("\n");
196 log(" -formal\n");
197 log(" convert all tri-state buffers to non-tristate logic and\n");
198 log(" add a formal assertion that no two buffers are driving the\n");
199 log(" same net simultaneously. this option implies -merge.\n");
200 log("\n");
201 }
202 void execute(std::vector<std::string> args, RTLIL::Design *design) override
203 {
204 TribufConfig config;
205
206 log_header(design, "Executing TRIBUF pass.\n");
207
208 size_t argidx;
209 for (argidx = 1; argidx < args.size(); argidx++) {
210 if (args[argidx] == "-merge") {
211 config.merge_mode = true;
212 continue;
213 }
214 if (args[argidx] == "-logic") {
215 config.logic_mode = true;
216 continue;
217 }
218 if (args[argidx] == "-formal") {
219 config.formal_mode = true;
220 continue;
221 }
222 break;
223 }
224 extra_args(args, argidx, design);
225
226 for (auto module : design->selected_modules()) {
227 TribufWorker worker(module, config);
228 worker.run();
229 }
230 }
231 } TribufPass;
232
233 PRIVATE_NAMESPACE_END