| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| */ |
| |
| #include "kernel/yosys.h" |
| #include "kernel/sigtools.h" |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| struct TribufConfig { |
| bool merge_mode; |
| bool logic_mode; |
| |
| TribufConfig() { |
| merge_mode = false; |
| logic_mode = false; |
| } |
| }; |
| |
| struct TribufWorker { |
| Module *module; |
| SigMap sigmap; |
| const TribufConfig &config; |
| |
| TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config) |
| { |
| } |
| |
| static bool is_all_z(SigSpec sig) |
| { |
| for (auto bit : sig) |
| if (bit != State::Sz) |
| return false; |
| return true; |
| } |
| |
| void run() |
| { |
| dict<SigSpec, vector<Cell*>> tribuf_cells; |
| pool<SigBit> output_bits; |
| |
| if (config.logic_mode) |
| for (auto wire : module->wires()) |
| if (wire->port_output) |
| for (auto bit : sigmap(wire)) |
| output_bits.insert(bit); |
| |
| for (auto cell : module->selected_cells()) |
| { |
| if (cell->type == ID($tribuf)) |
| tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); |
| |
| if (cell->type == ID($_TBUF_)) |
| tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); |
| |
| if (cell->type.in(ID($mux), ID($_MUX_))) |
| { |
| IdString en_port = cell->type == ID($mux) ? ID(EN) : ID(E); |
| IdString tri_type = cell->type == ID($mux) ? ID($tribuf) : ID($_TBUF_); |
| |
| if (is_all_z(cell->getPort(ID::A)) && is_all_z(cell->getPort(ID::B))) { |
| module->remove(cell); |
| continue; |
| } |
| |
| if (is_all_z(cell->getPort(ID::A))) { |
| cell->setPort(ID::A, cell->getPort(ID::B)); |
| cell->setPort(en_port, cell->getPort(ID(S))); |
| cell->unsetPort(ID::B); |
| cell->unsetPort(ID(S)); |
| cell->type = tri_type; |
| tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); |
| continue; |
| } |
| |
| if (is_all_z(cell->getPort(ID::B))) { |
| cell->setPort(en_port, module->Not(NEW_ID, cell->getPort(ID(S)))); |
| cell->unsetPort(ID::B); |
| cell->unsetPort(ID(S)); |
| cell->type = tri_type; |
| tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); |
| continue; |
| } |
| } |
| } |
| |
| if (config.merge_mode || config.logic_mode) |
| { |
| for (auto &it : tribuf_cells) |
| { |
| bool no_tribuf = false; |
| |
| if (config.logic_mode) { |
| no_tribuf = true; |
| for (auto bit : it.first) |
| if (output_bits.count(bit)) |
| no_tribuf = false; |
| } |
| |
| if (GetSize(it.second) <= 1 && !no_tribuf) |
| continue; |
| |
| SigSpec pmux_b, pmux_s; |
| for (auto cell : it.second) { |
| if (cell->type == ID($tribuf)) |
| pmux_s.append(cell->getPort(ID(EN))); |
| else |
| pmux_s.append(cell->getPort(ID(E))); |
| pmux_b.append(cell->getPort(ID::A)); |
| module->remove(cell); |
| } |
| |
| SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b; |
| |
| if (no_tribuf) |
| module->connect(it.first, muxout); |
| else |
| module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first); |
| } |
| } |
| } |
| }; |
| |
| struct TribufPass : public Pass { |
| TribufPass() : Pass("tribuf", "infer tri-state buffers") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" tribuf [options] [selection]\n"); |
| log("\n"); |
| log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n"); |
| log("\n"); |
| log(" -merge\n"); |
| log(" merge multiple tri-state buffers driving the same net\n"); |
| log(" into a single buffer.\n"); |
| log("\n"); |
| log(" -logic\n"); |
| log(" convert tri-state buffers that do not drive output ports\n"); |
| log(" to non-tristate logic. this option implies -merge.\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| TribufConfig config; |
| |
| log_header(design, "Executing TRIBUF pass.\n"); |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) { |
| if (args[argidx] == "-merge") { |
| config.merge_mode = true; |
| continue; |
| } |
| if (args[argidx] == "-logic") { |
| config.logic_mode = true; |
| continue; |
| } |
| break; |
| } |
| extra_args(args, argidx, design); |
| |
| for (auto module : design->selected_modules()) { |
| TribufWorker worker(module, config); |
| worker.run(); |
| } |
| } |
| } TribufPass; |
| |
| PRIVATE_NAMESPACE_END |