/* * 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 struct SpliceWorker { RTLIL::Design *design; RTLIL::Module *module; 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.width == 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).to_single_sigbit()) - 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.width != sig.width) { RTLIL::Cell *cell = new RTLIL::Cell; cell->name = NEW_ID; cell->type = "$slice"; cell->parameters["\\OFFSET"] = offset; cell->parameters["\\A_WIDTH"] = sig_a.width; cell->parameters["\\Y_WIDTH"] = sig.width; cell->connections["\\A"] = sig_a; cell->connections["\\Y"] = module->new_wire(sig.width, NEW_ID); new_sig = cell->connections["\\Y"]; module->add(cell); } new_sig.optimize(); sliced_signals_cache[sig] = new_sig; return new_sig; } RTLIL::SigSpec get_spliced_signal(RTLIL::SigSpec sig) { if (sig.width == 0) 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 == 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 = new RTLIL::Cell; cell->name = NEW_ID; cell->type = "$concat"; cell->parameters["\\A_WIDTH"] = new_sig.width; cell->parameters["\\B_WIDTH"] = sig2.width; cell->connections["\\A"] = new_sig; cell->connections["\\B"] = sig2; cell->connections["\\Y"] = module->new_wire(new_sig.width + sig2.width, NEW_ID); new_sig = cell->connections["\\Y"]; module->add(cell); } new_sig.optimize(); 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; for (auto &it : module->cells) { if (!design->selected(module, it.second)) continue; for (auto &conn : it.second->connections) if (ct.cell_input(it.second->type, conn.first)) { RTLIL::SigSpec sig = sigmap(conn.second); if (driven_chunks.count(sig) > 0) continue; conn.second = get_spliced_signal(sig); } } std::vector> rework_outputs; for (auto &it : module->wires) if (it.second->port_output) { if (!design->selected(module, it.second)) continue; RTLIL::SigSpec sig = sigmap(it.second); if (driven_chunks.count(sig) > 0) continue; RTLIL::SigSpec new_sig = get_spliced_signal(sig); if (new_sig != sig) rework_outputs.push_back(std::pair(it.second, new_sig)); } for (auto &it : rework_outputs) { module->wires.erase(it.first->name); RTLIL::Wire *new_port = new RTLIL::Wire(*it.first); it.first->name = NEW_ID; it.first->port_id = 0; it.first->port_input = false; it.first->port_output = false; module->add(it.first); module->add(new_port); module->connections.push_back(RTLIL::SigSig(new_port, it.second)); } } }; struct SplicePass : public Pass { SplicePass() : Pass("splice", "create explicit splicing cells") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" splice [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 dedidacted hardware is needed to splice signals.\n"); log("\n"); } virtual void execute(std::vector args, RTLIL::Design *design) { extra_args(args, 1, design); log_header("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.run(); } } } SplicePass;