/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf * * 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/register.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" #include "kernel/rtlil.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SpliceWorker { RTLIL::Design *design; RTLIL::Module *module; bool sel_by_cell; bool sel_by_wire; bool sel_any_bit; bool no_outputs; bool do_wires; std::set ports; std::set no_ports; CellTypes ct; SigMap sigmap; std::vector driven_bits; std::map driven_bits_map; std::set driven_chunks; std::map spliced_signals_cache; std::map sliced_signals_cache; SpliceWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), ct(design), sigmap(module) { } RTLIL::SigSpec get_sliced_signal(RTLIL::SigSpec sig) { if (sig.size() == 0 || sig.is_fully_const()) return sig; if (sliced_signals_cache.count(sig)) return sliced_signals_cache.at(sig); int offset = 0; int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1; while (driven_bits.at(p) != RTLIL::State::Sm) p--, offset++; RTLIL::SigSpec sig_a; for (p++; driven_bits.at(p) != RTLIL::State::Sm; p++) sig_a.append(driven_bits.at(p)); RTLIL::SigSpec new_sig = sig; if (sig_a.size() != sig.size()) { RTLIL::Cell *cell = module->addCell(NEW_ID, ID($slice)); cell->parameters[ID::OFFSET] = offset; cell->parameters[ID::A_WIDTH] = sig_a.size(); cell->parameters[ID::Y_WIDTH] = sig.size(); cell->setPort(ID::A, sig_a); cell->setPort(ID::Y, module->addWire(NEW_ID, sig.size())); new_sig = cell->getPort(ID::Y); } sliced_signals_cache[sig] = new_sig; return new_sig; } RTLIL::SigSpec get_spliced_signal(RTLIL::SigSpec sig) { if (sig.size() == 0 || sig.is_fully_const()) return sig; if (spliced_signals_cache.count(sig)) return spliced_signals_cache.at(sig); int last_bit = -1; std::vector chunks; for (auto &bit : sig.to_sigbit_vector()) { if (bit.wire == nullptr) { if (last_bit == 0) chunks.back().append(bit); else chunks.push_back(bit); last_bit = 0; continue; } if (driven_bits_map.count(bit)) { int this_bit = driven_bits_map.at(bit); if (last_bit+1 == this_bit) chunks.back().append(bit); else chunks.push_back(bit); last_bit = this_bit; continue; } log(" Failed to generate spliced signal %s.\n", log_signal(sig)); spliced_signals_cache[sig] = sig; return sig; } RTLIL::SigSpec new_sig = get_sliced_signal(chunks.front()); for (size_t i = 1; i < chunks.size(); i++) { RTLIL::SigSpec sig2 = get_sliced_signal(chunks[i]); RTLIL::Cell *cell = module->addCell(NEW_ID, ID($concat)); cell->parameters[ID::A_WIDTH] = new_sig.size(); cell->parameters[ID::B_WIDTH] = sig2.size(); cell->setPort(ID::A, new_sig); cell->setPort(ID::B, sig2); cell->setPort(ID::Y, module->addWire(NEW_ID, new_sig.size() + sig2.size())); new_sig = cell->getPort(ID::Y); } spliced_signals_cache[sig] = new_sig; log(" Created spliced signal: %s -> %s\n", log_signal(sig), log_signal(new_sig)); return new_sig; } void run() { log("Splicing signals in module %s:\n", log_id(module->name)); driven_bits.push_back(RTLIL::State::Sm); driven_bits.push_back(RTLIL::State::Sm); for (auto wire : module->wires()) if (wire->port_input) { RTLIL::SigSpec sig = sigmap(wire); driven_chunks.insert(sig); for (auto &bit : sig.to_sigbit_vector()) driven_bits.push_back(bit); driven_bits.push_back(RTLIL::State::Sm); } for (auto cell : module->cells()) for (auto &conn : cell->connections()) if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first)) { RTLIL::SigSpec sig = sigmap(conn.second); driven_chunks.insert(sig); for (auto &bit : sig.to_sigbit_vector()) driven_bits.push_back(bit); driven_bits.push_back(RTLIL::State::Sm); } driven_bits.push_back(RTLIL::State::Sm); for (size_t i = 0; i < driven_bits.size(); i++) driven_bits_map[driven_bits[i]] = i; SigPool selected_bits; if (!sel_by_cell) for (auto wire : module->selected_wires()) selected_bits.add(sigmap(wire)); std::vector mod_cells = module->cells(); for (auto cell : mod_cells) { if (!sel_by_wire && !design->selected(module, cell)) continue; for (auto &conn : cell->connections_) if (ct.cell_input(cell->type, conn.first)) { if (ports.size() > 0 && !ports.count(conn.first)) continue; if (no_ports.size() > 0 && no_ports.count(conn.first)) continue; RTLIL::SigSpec sig = sigmap(conn.second); if (!sel_by_cell) { if (!sel_any_bit && !selected_bits.check_all(sig)) continue; if (sel_any_bit && !selected_bits.check_any(sig)) continue; } if (driven_chunks.count(sig) > 0) continue; conn.second = get_spliced_signal(sig); } } std::vector> rework_wires; std::vector mod_wires = module->wires(); for (auto wire : mod_wires) if ((!no_outputs && wire->port_output) || (do_wires && wire->name.isPublic())) { if (!design->selected(module, wire)) continue; RTLIL::SigSpec sig = sigmap(wire); if (driven_chunks.count(sig) > 0) continue; RTLIL::SigSpec new_sig = get_spliced_signal(sig); if (new_sig != sig) rework_wires.push_back(std::pair(wire, new_sig)); } else if (!wire->port_input) { RTLIL::SigSpec sig = sigmap(wire); if (spliced_signals_cache.count(sig) && spliced_signals_cache.at(sig) != sig) rework_wires.push_back(std::pair(wire, spliced_signals_cache.at(sig))); else if (sliced_signals_cache.count(sig) && sliced_signals_cache.at(sig) != sig) rework_wires.push_back(std::pair(wire, sliced_signals_cache.at(sig))); } for (auto &it : rework_wires) { RTLIL::IdString orig_name = it.first->name; module->rename(it.first, NEW_ID); RTLIL::Wire *new_port = module->addWire(orig_name, it.first); it.first->port_id = 0; it.first->port_input = false; it.first->port_output = false; module->connect(RTLIL::SigSig(new_port, it.second)); } } }; struct SplicePass : public Pass { SplicePass() : Pass("splice", "create explicit splicing cells") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" splice [options] [selection]\n"); log("\n"); log("This command adds $slice and $concat cells to the design to make the splicing\n"); log("of multi-bit signals explicit. This for example is useful for coarse grain\n"); log("synthesis, where dedicated hardware is needed to splice signals.\n"); log("\n"); log(" -sel_by_cell\n"); log(" only select the cell ports to rewire by the cell. if the selection\n"); log(" contains a cell, than all cell inputs are rewired, if necessary.\n"); log("\n"); log(" -sel_by_wire\n"); log(" only select the cell ports to rewire by the wire. if the selection\n"); log(" contains a wire, than all cell ports driven by this wire are wired,\n"); log(" if necessary.\n"); log("\n"); log(" -sel_any_bit\n"); log(" it is sufficient if the driver of any bit of a cell port is selected.\n"); log(" by default all bits must be selected.\n"); log("\n"); log(" -wires\n"); log(" also add $slice and $concat cells to drive otherwise unused wires.\n"); log("\n"); log(" -no_outputs\n"); log(" do not rewire selected module outputs.\n"); log("\n"); log(" -port \n"); log(" only rewire cell ports with the specified name. can be used multiple\n"); log(" times. implies -no_output.\n"); log("\n"); log(" -no_port \n"); log(" do not rewire cell ports with the specified name. can be used multiple\n"); log(" times. can not be combined with -port .\n"); log("\n"); log("By default selected output wires and all cell ports of selected cells driven\n"); log("by selected wires are rewired.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { bool sel_by_cell = false; bool sel_by_wire = false; bool sel_any_bit = false; bool no_outputs = false; bool do_wires = false; std::set ports, no_ports; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-sel_by_cell") { sel_by_cell = true; continue; } if (args[argidx] == "-sel_by_wire") { sel_by_wire = true; continue; } if (args[argidx] == "-sel_any_bit") { sel_any_bit = true; continue; } if (args[argidx] == "-wires") { do_wires = true; continue; } if (args[argidx] == "-no_outputs") { no_outputs = true; continue; } if (args[argidx] == "-port" && argidx+1 < args.size()) { ports.insert(RTLIL::escape_id(args[++argidx])); no_outputs = true; continue; } if (args[argidx] == "-no_port" && argidx+1 < args.size()) { no_ports.insert(RTLIL::escape_id(args[++argidx])); continue; } break; } extra_args(args, argidx, design); if (sel_by_cell && sel_by_wire) log_cmd_error("The options -sel_by_cell and -sel_by_wire are exclusive!\n"); if (sel_by_cell && sel_any_bit) log_cmd_error("The options -sel_by_cell and -sel_any_bit are exclusive!\n"); if (!ports.empty() && !no_ports.empty()) log_cmd_error("The options -port and -no_port are exclusive!\n"); log_header(design, "Executing SPLICE pass (creating cells for signal splicing).\n"); for (auto module : design->selected_modules()) { if (module->processes.size()) { log("Skipping module %s as it contains processes.\n", module->name.c_str()); continue; } SpliceWorker worker(design, module); worker.sel_by_cell = sel_by_cell; worker.sel_by_wire = sel_by_wire; worker.sel_any_bit = sel_any_bit; worker.no_outputs = no_outputs; worker.do_wires = do_wires; worker.ports = ports; worker.no_ports = no_ports; worker.run(); } } } SplicePass; PRIVATE_NAMESPACE_END