| /* | |
| * 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" | |
| #define MODE_ZERO 0 | |
| #define MODE_ONE 1 | |
| #define MODE_UNDEF 2 | |
| #define MODE_RANDOM 3 | |
| #define MODE_ANYSEQ 4 | |
| #define MODE_ANYCONST 5 | |
| USING_YOSYS_NAMESPACE | |
| PRIVATE_NAMESPACE_BEGIN | |
| static RTLIL::Wire * add_wire(RTLIL::Module *module, std::string name, int width, bool flag_input, bool flag_output) | |
| { | |
| RTLIL::Wire *wire = NULL; | |
| name = RTLIL::escape_id(name); | |
| if (module->count_id(name) != 0) | |
| { | |
| log("Module %s already has such an object %s.\n", module->name.c_str(), name.c_str()); | |
| name += "$"; | |
| return add_wire(module, name, width, flag_input, flag_output); | |
| } | |
| else | |
| { | |
| wire = module->addWire(name, width); | |
| wire->port_input = flag_input; | |
| wire->port_output = flag_output; | |
| if (flag_input || flag_output) { | |
| wire->port_id = module->wires_.size(); | |
| module->fixup_ports(); | |
| } | |
| log("Added wire %s to module %s.\n", name.c_str(), module->name.c_str()); | |
| } | |
| return wire; | |
| } | |
| struct SetundefWorker | |
| { | |
| int next_bit_mode; | |
| uint32_t next_bit_state; | |
| vector<SigSpec*> siglist; | |
| RTLIL::State next_bit() | |
| { | |
| if (next_bit_mode == MODE_ZERO) | |
| return RTLIL::State::S0; | |
| if (next_bit_mode == MODE_ONE) | |
| return RTLIL::State::S1; | |
| if (next_bit_mode == MODE_UNDEF) | |
| return RTLIL::State::Sx; | |
| if (next_bit_mode == MODE_RANDOM) | |
| { | |
| // xorshift32 | |
| next_bit_state ^= next_bit_state << 13; | |
| next_bit_state ^= next_bit_state >> 17; | |
| next_bit_state ^= next_bit_state << 5; | |
| log_assert(next_bit_state != 0); | |
| return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1; | |
| } | |
| log_abort(); | |
| } | |
| void operator()(RTLIL::SigSpec &sig) | |
| { | |
| if (next_bit_mode == MODE_ANYSEQ || next_bit_mode == MODE_ANYCONST) { | |
| siglist.push_back(&sig); | |
| return; | |
| } | |
| for (auto &bit : sig) | |
| if (bit.wire == NULL && bit.data > RTLIL::State::S1) | |
| bit = next_bit(); | |
| } | |
| }; | |
| struct SetundefPass : public Pass { | |
| SetundefPass() : Pass("setundef", "replace undef values with defined constants") { } | |
| void help() YS_OVERRIDE | |
| { | |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | |
| log("\n"); | |
| log(" setundef [options] [selection]\n"); | |
| log("\n"); | |
| log("This command replaces undef (x) constants with defined (0/1) constants.\n"); | |
| log("\n"); | |
| log(" -undriven\n"); | |
| log(" also set undriven nets to constant values\n"); | |
| log("\n"); | |
| log(" -expose\n"); | |
| log(" also expose undriven nets as inputs (use with -undriven)\n"); | |
| log("\n"); | |
| log(" -zero\n"); | |
| log(" replace with bits cleared (0)\n"); | |
| log("\n"); | |
| log(" -one\n"); | |
| log(" replace with bits set (1)\n"); | |
| log("\n"); | |
| log(" -undef\n"); | |
| log(" replace with undef (x) bits, may be used with -undriven\n"); | |
| log("\n"); | |
| log(" -anyseq\n"); | |
| log(" replace with $anyseq drivers (for formal)\n"); | |
| log("\n"); | |
| log(" -anyconst\n"); | |
| log(" replace with $anyconst drivers (for formal)\n"); | |
| log("\n"); | |
| log(" -random <seed>\n"); | |
| log(" replace with random bits using the specified integer as seed\n"); | |
| log(" value for the random number generator.\n"); | |
| log("\n"); | |
| log(" -init\n"); | |
| log(" also create/update init values for flip-flops\n"); | |
| log("\n"); | |
| log(" -params\n"); | |
| log(" replace undef in cell parameters\n"); | |
| log("\n"); | |
| } | |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | |
| { | |
| bool got_value = false; | |
| bool undriven_mode = false; | |
| bool expose_mode = false; | |
| bool init_mode = false; | |
| bool params_mode = false; | |
| SetundefWorker worker; | |
| log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); | |
| size_t argidx; | |
| for (argidx = 1; argidx < args.size(); argidx++) | |
| { | |
| if (args[argidx] == "-undriven") { | |
| undriven_mode = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-expose") { | |
| expose_mode = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-zero") { | |
| got_value = true; | |
| worker.next_bit_mode = MODE_ZERO; | |
| worker.next_bit_state = 0; | |
| continue; | |
| } | |
| if (args[argidx] == "-one") { | |
| got_value = true; | |
| worker.next_bit_mode = MODE_ONE; | |
| worker.next_bit_state = 0; | |
| continue; | |
| } | |
| if (args[argidx] == "-anyseq") { | |
| got_value = true; | |
| worker.next_bit_mode = MODE_ANYSEQ; | |
| worker.next_bit_state = 0; | |
| continue; | |
| } | |
| if (args[argidx] == "-anyconst") { | |
| got_value = true; | |
| worker.next_bit_mode = MODE_ANYCONST; | |
| worker.next_bit_state = 0; | |
| continue; | |
| } | |
| if (args[argidx] == "-undef") { | |
| got_value = true; | |
| worker.next_bit_mode = MODE_UNDEF; | |
| worker.next_bit_state = 0; | |
| continue; | |
| } | |
| if (args[argidx] == "-init") { | |
| init_mode = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-params") { | |
| params_mode = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { | |
| got_value = true; | |
| worker.next_bit_mode = MODE_RANDOM; | |
| worker.next_bit_state = atoi(args[++argidx].c_str()) + 1; | |
| for (int i = 0; i < 10; i++) | |
| worker.next_bit(); | |
| continue; | |
| } | |
| break; | |
| } | |
| extra_args(args, argidx, design); | |
| if (!got_value && expose_mode) { | |
| log("Using default as -undef with -expose.\n"); | |
| got_value = true; | |
| worker.next_bit_mode = MODE_UNDEF; | |
| worker.next_bit_state = 0; | |
| } | |
| if (expose_mode && !undriven_mode) | |
| log_cmd_error("Option -expose must be used with option -undriven.\n"); | |
| if (!got_value) | |
| log_cmd_error("One of the options -zero, -one, -anyseq, -anyconst, or -random <seed> must be specified.\n"); | |
| if (init_mode && (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST)) | |
| log_cmd_error("The options -init and -anyseq / -anyconst are exclusive.\n"); | |
| for (auto module : design->selected_modules()) | |
| { | |
| if (params_mode) | |
| { | |
| for (auto *cell : module->selected_cells()) { | |
| for (auto ¶meter : cell->parameters) { | |
| for (auto &bit : parameter.second.bits) { | |
| if (bit > RTLIL::State::S1) | |
| bit = worker.next_bit(); | |
| } | |
| } | |
| } | |
| } | |
| if (undriven_mode) | |
| { | |
| if (!module->processes.empty()) | |
| log_error("The 'setundef' command can't operate in -undriven mode on modules with processes. Run 'proc' first.\n"); | |
| if (expose_mode) | |
| { | |
| SigMap sigmap(module); | |
| dict<SigBit, bool> wire_drivers; | |
| pool<SigBit> used_wires; | |
| SigPool undriven_signals; | |
| for (auto cell : module->cells()) | |
| for (auto &conn : cell->connections()) { | |
| SigSpec sig = sigmap(conn.second); | |
| if (cell->input(conn.first)) | |
| for (auto bit : sig) | |
| if (bit.wire) | |
| used_wires.insert(bit); | |
| if (cell->output(conn.first)) | |
| for (int i = 0; i < GetSize(sig); i++) | |
| if (sig[i].wire) | |
| wire_drivers[sig[i]] = true; | |
| } | |
| for (auto wire : module->wires()) { | |
| if (wire->port_input) { | |
| SigSpec sig = sigmap(wire); | |
| for (int i = 0; i < GetSize(sig); i++) | |
| wire_drivers[sig[i]] = true; | |
| } | |
| if (wire->port_output) { | |
| SigSpec sig = sigmap(wire); | |
| for (auto bit : sig) | |
| if (bit.wire) | |
| used_wires.insert(bit); | |
| } | |
| } | |
| pool<RTLIL::Wire*> undriven_wires; | |
| for (auto bit : used_wires) | |
| if (!wire_drivers.count(bit)) | |
| undriven_wires.insert(bit.wire); | |
| for (auto &it : undriven_wires) | |
| undriven_signals.add(sigmap(it)); | |
| for (auto &it : undriven_wires) | |
| if (it->port_input) | |
| undriven_signals.del(sigmap(it)); | |
| CellTypes ct(design); | |
| 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)) | |
| undriven_signals.del(sigmap(conn.second)); | |
| RTLIL::SigSpec sig = undriven_signals.export_all(); | |
| for (auto &c : sig.chunks()) { | |
| RTLIL::Wire * wire; | |
| if (c.wire->width == c.width) { | |
| wire = c.wire; | |
| wire->port_input = true; | |
| } else { | |
| string name = c.wire->name.str() + "$[" + std::to_string(c.width + c.offset) + ":" + std::to_string(c.offset) + "]"; | |
| wire = add_wire(module, name, c.width, true, false); | |
| module->connect(RTLIL::SigSig(c, wire)); | |
| } | |
| log("Exposing undriven wire %s as input.\n", wire->name.c_str()); | |
| } | |
| module->fixup_ports(); | |
| } | |
| else | |
| { | |
| SigMap sigmap(module); | |
| SigPool undriven_signals; | |
| for (auto &it : module->wires_) | |
| undriven_signals.add(sigmap(it.second)); | |
| for (auto &it : module->wires_) | |
| if (it.second->port_input) | |
| undriven_signals.del(sigmap(it.second)); | |
| CellTypes ct(design); | |
| 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)) | |
| undriven_signals.del(sigmap(conn.second)); | |
| RTLIL::SigSpec sig = undriven_signals.export_all(); | |
| for (auto &c : sig.chunks()) { | |
| RTLIL::SigSpec bits; | |
| if (worker.next_bit_mode == MODE_ANYSEQ) | |
| bits = module->Anyseq(NEW_ID, c.width); | |
| else if (worker.next_bit_mode == MODE_ANYCONST) | |
| bits = module->Anyconst(NEW_ID, c.width); | |
| else | |
| for (int i = 0; i < c.width; i++) | |
| bits.append(worker.next_bit()); | |
| module->connect(RTLIL::SigSig(c, bits)); | |
| } | |
| } | |
| } | |
| if (init_mode) | |
| { | |
| SigMap sigmap(module); | |
| pool<SigBit> ffbits; | |
| pool<Wire*> initwires; | |
| pool<IdString> fftypes; | |
| fftypes.insert("$dff"); | |
| fftypes.insert("$dffe"); | |
| fftypes.insert("$dffsr"); | |
| fftypes.insert("$adff"); | |
| std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'}; | |
| for (auto c1 : list_np) | |
| fftypes.insert(stringf("$_DFF_%c_", c1)); | |
| for (auto c1 : list_np) | |
| for (auto c2 : list_np) | |
| fftypes.insert(stringf("$_DFFE_%c%c_", c1, c2)); | |
| for (auto c1 : list_np) | |
| for (auto c2 : list_np) | |
| for (auto c3 : list_01) | |
| fftypes.insert(stringf("$_DFF_%c%c%c_", c1, c2, c3)); | |
| for (auto c1 : list_np) | |
| for (auto c2 : list_np) | |
| for (auto c3 : list_np) | |
| fftypes.insert(stringf("$_DFFSR_%c%c%c_", c1, c2, c3)); | |
| for (auto cell : module->cells()) | |
| { | |
| if (!fftypes.count(cell->type)) | |
| continue; | |
| for (auto bit : sigmap(cell->getPort("\\Q"))) | |
| ffbits.insert(bit); | |
| } | |
| auto process_initwires = [&]() | |
| { | |
| dict<Wire*, int> wire_weights; | |
| for (auto wire : initwires) | |
| { | |
| int weight = 0; | |
| for (auto bit : sigmap(wire)) | |
| weight += ffbits.count(bit) ? +1 : -1; | |
| wire_weights[wire] = weight; | |
| } | |
| initwires.sort([&](Wire *a, Wire *b) { return wire_weights.at(a) > wire_weights.at(b); }); | |
| for (auto wire : initwires) | |
| { | |
| Const &initval = wire->attributes["\\init"]; | |
| initval.bits.resize(GetSize(wire), State::Sx); | |
| for (int i = 0; i < GetSize(wire); i++) { | |
| SigBit bit = sigmap(SigBit(wire, i)); | |
| if (initval[i] == State::Sx && ffbits.count(bit)) { | |
| initval[i] = worker.next_bit(); | |
| ffbits.erase(bit); | |
| } | |
| } | |
| if (initval.is_fully_undef()) | |
| wire->attributes.erase("\\init"); | |
| } | |
| initwires.clear(); | |
| }; | |
| for (int wire_types = 0; wire_types < 2; wire_types++) | |
| { | |
| // prioritize wires that already have an init attribute | |
| if (!ffbits.empty()) | |
| { | |
| for (auto wire : module->wires()) | |
| { | |
| if (wire->name[0] == (wire_types ? '\\' : '$')) | |
| continue; | |
| if (!wire->attributes.count("\\init")) | |
| continue; | |
| Const &initval = wire->attributes["\\init"]; | |
| initval.bits.resize(GetSize(wire), State::Sx); | |
| if (initval.is_fully_undef()) { | |
| wire->attributes.erase("\\init"); | |
| continue; | |
| } | |
| for (int i = 0; i < GetSize(wire); i++) | |
| if (initval[i] != State::Sx) | |
| ffbits.erase(sigmap(SigBit(wire, i))); | |
| initwires.insert(wire); | |
| } | |
| process_initwires(); | |
| } | |
| // next consider wires that completely contain bits to be initialized | |
| if (!ffbits.empty()) | |
| { | |
| for (auto wire : module->wires()) | |
| { | |
| if (wire->name[0] == (wire_types ? '\\' : '$')) | |
| continue; | |
| for (auto bit : sigmap(wire)) | |
| if (!ffbits.count(bit)) | |
| goto next_wire; | |
| initwires.insert(wire); | |
| next_wire: | |
| continue; | |
| } | |
| process_initwires(); | |
| } | |
| // finally use whatever wire we can find. | |
| if (!ffbits.empty()) | |
| { | |
| for (auto wire : module->wires()) | |
| { | |
| if (wire->name[0] == (wire_types ? '\\' : '$')) | |
| continue; | |
| for (auto bit : sigmap(wire)) | |
| if (ffbits.count(bit)) | |
| initwires.insert(wire); | |
| } | |
| process_initwires(); | |
| } | |
| } | |
| log_assert(ffbits.empty()); | |
| } | |
| module->rewrite_sigspecs(worker); | |
| if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST) | |
| { | |
| vector<SigSpec*> siglist; | |
| siglist.swap(worker.siglist); | |
| for (auto sigptr : siglist) | |
| { | |
| SigSpec &sig = *sigptr; | |
| int cursor = 0; | |
| while (cursor < GetSize(sig)) | |
| { | |
| int width = 0; | |
| while (cursor+width < GetSize(sig) && sig[cursor+width] == State::Sx) | |
| width++; | |
| if (width > 0) { | |
| if (worker.next_bit_mode == MODE_ANYSEQ) | |
| sig.replace(cursor, module->Anyseq(NEW_ID, width)); | |
| else | |
| sig.replace(cursor, module->Anyconst(NEW_ID, width)); | |
| cursor += width; | |
| } else { | |
| cursor++; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } SetundefPass; | |
| PRIVATE_NAMESPACE_END |