2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
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.
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.
20 #include "kernel/yosys.h"
21 #include "kernel/sigtools.h"
24 PRIVATE_NAMESPACE_BEGIN
41 const TribufConfig
&config
;
43 TribufWorker(Module
*module
, const TribufConfig
&config
) : module(module
), sigmap(module
), config(config
)
47 static bool is_all_z(SigSpec sig
)
57 dict
<SigSpec
, vector
<Cell
*>> tribuf_cells
;
58 pool
<SigBit
> output_bits
;
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
);
66 for (auto cell
: module
->selected_cells())
68 if (cell
->type
== ID($tribuf
))
69 tribuf_cells
[sigmap(cell
->getPort(ID::Y
))].push_back(cell
);
71 if (cell
->type
== ID($_TBUF_
))
72 tribuf_cells
[sigmap(cell
->getPort(ID::Y
))].push_back(cell
);
74 if (cell
->type
.in(ID($mux
), ID($_MUX_
)))
76 IdString en_port
= cell
->type
== ID($mux
) ? ID::EN
: ID::E
;
77 IdString tri_type
= cell
->type
== ID($mux
) ? ID($tribuf
) : ID($_TBUF_
);
79 if (is_all_z(cell
->getPort(ID::A
)) && is_all_z(cell
->getPort(ID::B
))) {
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);
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);
107 if (config
.merge_mode
|| config
.logic_mode
|| config
.formal_mode
)
109 for (auto &it
: tribuf_cells
)
111 bool no_tribuf
= false;
113 if (config
.logic_mode
&& !config
.formal_mode
) {
115 for (auto bit
: it
.first
)
116 if (output_bits
.count(bit
))
120 if (config
.formal_mode
)
123 if (GetSize(it
.second
) <= 1 && !no_tribuf
)
126 if (config
.formal_mode
&& GetSize(it
.second
) >= 2) {
127 for (auto cell
: it
.second
) {
130 for (auto other_cell
: it
.second
) {
131 if (other_cell
== cell
)
133 else if (other_cell
->type
== ID($tribuf
))
134 others_s
.append(other_cell
->getPort(ID::EN
));
136 others_s
.append(other_cell
->getPort(ID::E
));
139 auto cell_s
= cell
->type
== ID($tribuf
) ? cell
->getPort(ID::EN
) : cell
->getPort(ID::E
);
141 auto other_s
= module
->ReduceOr(NEW_ID
, others_s
);
143 auto conflict
= module
->And(NEW_ID
, cell_s
, other_s
);
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));
148 assert_cell
->set_src_attribute(cell
->get_src_attribute());
149 assert_cell
->set_bool_attribute(ID::keep
);
151 module
->design
->scratchpad_set_bool("tribuf.added_something", true);
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
));
160 pmux_s
.append(cell
->getPort(ID::E
));
161 pmux_b
.append(cell
->getPort(ID::A
));
162 module
->remove(cell
);
165 SigSpec muxout
= GetSize(pmux_s
) > 1 ? module
->Pmux(NEW_ID
, SigSpec(State::Sx
, GetSize(it
.first
)), pmux_b
, pmux_s
) : pmux_b
;
168 module
->connect(it
.first
, muxout
);
170 module
->addTribuf(NEW_ID
, muxout
, module
->ReduceOr(NEW_ID
, pmux_s
), it
.first
);
171 module
->design
->scratchpad_set_bool("tribuf.added_something", true);
178 struct TribufPass
: public Pass
{
179 TribufPass() : Pass("tribuf", "infer tri-state buffers") { }
182 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
184 log(" tribuf [options] [selection]\n");
186 log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n");
189 log(" merge multiple tri-state buffers driving the same net\n");
190 log(" into a single buffer.\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");
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");
202 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) override
206 log_header(design
, "Executing TRIBUF pass.\n");
209 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
210 if (args
[argidx
] == "-merge") {
211 config
.merge_mode
= true;
214 if (args
[argidx
] == "-logic") {
215 config
.logic_mode
= true;
218 if (args
[argidx
] == "-formal") {
219 config
.formal_mode
= true;
224 extra_args(args
, argidx
, design
);
226 for (auto module
: design
->selected_modules()) {
227 TribufWorker
worker(module
, config
);
233 PRIVATE_NAMESPACE_END