| /* | |
| * 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/register.h" | |
| #include "kernel/celltypes.h" | |
| #include "kernel/sigtools.h" | |
| #include "kernel/rtlil.h" | |
| #include "kernel/log.h" | |
| #include <tuple> | |
| 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<RTLIL::IdString> ports; | |
| std::set<RTLIL::IdString> no_ports; | |
| CellTypes ct; | |
| SigMap sigmap; | |
| std::vector<RTLIL::SigBit> driven_bits; | |
| std::map<RTLIL::SigBit, int> driven_bits_map; | |
| std::set<RTLIL::SigSpec> driven_chunks; | |
| std::map<RTLIL::SigSpec, RTLIL::SigSpec> spliced_signals_cache; | |
| std::map<RTLIL::SigSpec, RTLIL::SigSpec> 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, "$slice"); | |
| cell->parameters["\\OFFSET"] = offset; | |
| cell->parameters["\\A_WIDTH"] = sig_a.size(); | |
| cell->parameters["\\Y_WIDTH"] = sig.size(); | |
| cell->setPort("\\A", sig_a); | |
| cell->setPort("\\Y", module->addWire(NEW_ID, sig.size())); | |
| new_sig = cell->getPort("\\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<RTLIL::SigSpec> chunks; | |
| for (auto &bit : sig.to_sigbit_vector()) | |
| { | |
| if (bit.wire == NULL) | |
| { | |
| 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, "$concat"); | |
| cell->parameters["\\A_WIDTH"] = new_sig.size(); | |
| cell->parameters["\\B_WIDTH"] = sig2.size(); | |
| cell->setPort("\\A", new_sig); | |
| cell->setPort("\\B", sig2); | |
| cell->setPort("\\Y", module->addWire(NEW_ID, new_sig.size() + sig2.size())); | |
| new_sig = cell->getPort("\\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", RTLIL::id2cstr(module->name)); | |
| driven_bits.push_back(RTLIL::State::Sm); | |
| driven_bits.push_back(RTLIL::State::Sm); | |
| for (auto &it : module->wires_) | |
| if (it.second->port_input) { | |
| RTLIL::SigSpec sig = sigmap(it.second); | |
| 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 &it : module->cells_) | |
| for (auto &conn : it.second->connections()) | |
| if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->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 &it : module->wires_) | |
| if (design->selected(module, it.second)) | |
| selected_bits.add(sigmap(it.second)); | |
| std::vector<Cell*> 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<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires; | |
| std::vector<Wire*> mod_wires = module->wires(); | |
| for (auto wire : mod_wires) | |
| if ((!no_outputs && wire->port_output) || (do_wires && wire->name[0] == '\\')) { | |
| 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<RTLIL::Wire*, RTLIL::SigSpec>(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<RTLIL::Wire*, RTLIL::SigSpec>(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<RTLIL::Wire*, RTLIL::SigSpec>(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() YS_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 <name>\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 <name>\n"); | |
| log(" do not rewire cell ports with the specified name. can be used multiple\n"); | |
| log(" times. can not be combined with -port <name>.\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<std::string> args, RTLIL::Design *design) YS_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<RTLIL::IdString> 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 &mod_it : design->modules_) | |
| { | |
| if (!design->selected(mod_it.second)) | |
| continue; | |
| if (mod_it.second->processes.size()) { | |
| log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str()); | |
| continue; | |
| } | |
| SpliceWorker worker(design, mod_it.second); | |
| 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 |