| /* |
| * 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 AssertpmuxWorker |
| { |
| Module *module; |
| SigMap sigmap; |
| |
| bool flag_noinit; |
| bool flag_always; |
| |
| // get<0> ... mux cell |
| // get<1> ... mux port index |
| // get<2> ... mux bit index |
| dict<SigBit, pool<tuple<Cell*, int, int>>> sigbit_muxusers; |
| |
| dict<SigBit, SigBit> sigbit_actsignals; |
| dict<SigSpec, SigBit> sigspec_actsignals; |
| dict<tuple<Cell*, int>, SigBit> muxport_actsignal; |
| |
| AssertpmuxWorker(Module *module, bool flag_noinit, bool flag_always) : |
| module(module), sigmap(module), flag_noinit(flag_noinit), flag_always(flag_always) |
| { |
| for (auto wire : module->wires()) |
| { |
| if (wire->port_output) |
| for (auto bit : sigmap(wire)) |
| sigbit_actsignals[bit] = State::S1; |
| } |
| |
| for (auto cell : module->cells()) |
| { |
| if (cell->type.in("$mux", "$pmux")) |
| { |
| int width = cell->getParam("\\WIDTH").as_int(); |
| int numports = cell->type == "$mux" ? 2 : cell->getParam("\\S_WIDTH").as_int() + 1; |
| |
| SigSpec sig_a = sigmap(cell->getPort("\\A")); |
| SigSpec sig_b = sigmap(cell->getPort("\\B")); |
| SigSpec sig_s = sigmap(cell->getPort("\\S")); |
| |
| for (int i = 0; i < numports; i++) { |
| SigSpec bits = i == 0 ? sig_a : sig_b.extract(width*(i-1), width); |
| for (int k = 0; k < width; k++) { |
| tuple<Cell*, int, int> muxuser(cell, i, k); |
| sigbit_muxusers[bits[k]].insert(muxuser); |
| } |
| } |
| } |
| else |
| { |
| for (auto &conn : cell->connections()) { |
| if (!cell->known() || cell->input(conn.first)) |
| for (auto bit : sigmap(conn.second)) |
| sigbit_actsignals[bit] = State::S1; |
| } |
| } |
| } |
| } |
| |
| SigBit get_bit_activation(SigBit bit) |
| { |
| sigmap.apply(bit); |
| |
| if (sigbit_actsignals.count(bit) == 0) |
| { |
| SigSpec output; |
| |
| for (auto muxuser : sigbit_muxusers.at(bit)) |
| { |
| Cell *cell = std::get<0>(muxuser); |
| int portidx = std::get<1>(muxuser); |
| int bitidx = std::get<2>(muxuser); |
| |
| tuple<Cell*, int> muxport(cell, portidx); |
| |
| if (muxport_actsignal.count(muxport) == 0) { |
| if (portidx == 0) |
| muxport_actsignal[muxport] = module->LogicNot(NEW_ID, cell->getPort("\\S")); |
| else |
| muxport_actsignal[muxport] = cell->getPort("\\S")[portidx-1]; |
| } |
| |
| output.append(module->LogicAnd(NEW_ID, muxport_actsignal.at(muxport), get_bit_activation(cell->getPort("\\Y")[bitidx]))); |
| } |
| |
| output.sort_and_unify(); |
| |
| if (GetSize(output) == 0) |
| output = State::S0; |
| else if (GetSize(output) > 1) |
| output = module->ReduceOr(NEW_ID, output); |
| |
| sigbit_actsignals[bit] = output.as_bit(); |
| } |
| |
| return sigbit_actsignals.at(bit); |
| } |
| |
| SigBit get_activation(SigSpec sig) |
| { |
| sigmap.apply(sig); |
| sig.sort_and_unify(); |
| |
| if (sigspec_actsignals.count(sig) == 0) |
| { |
| SigSpec output; |
| |
| for (auto bit : sig) |
| output.append(get_bit_activation(bit)); |
| |
| output.sort_and_unify(); |
| |
| if (GetSize(output) == 0) |
| output = State::S0; |
| else if (GetSize(output) > 1) |
| output = module->ReduceOr(NEW_ID, output); |
| |
| sigspec_actsignals[sig] = output.as_bit(); |
| } |
| |
| return sigspec_actsignals.at(sig); |
| } |
| |
| void run(Cell *pmux) |
| { |
| log("Adding assert for $pmux cell %s.%s.\n", log_id(module), log_id(pmux)); |
| |
| int swidth = pmux->getParam("\\S_WIDTH").as_int(); |
| int cntbits = ceil_log2(swidth+1); |
| |
| SigSpec sel = pmux->getPort("\\S"); |
| SigSpec cnt(State::S0, cntbits); |
| |
| for (int i = 0; i < swidth; i++) |
| cnt = module->Add(NEW_ID, cnt, sel[i]); |
| |
| SigSpec assert_a = module->Le(NEW_ID, cnt, SigSpec(1, cntbits)); |
| SigSpec assert_en; |
| |
| if (flag_noinit) |
| assert_en.append(module->LogicNot(NEW_ID, module->Initstate(NEW_ID))); |
| |
| if (!flag_always) |
| assert_en.append(get_activation(pmux->getPort("\\Y"))); |
| |
| if (GetSize(assert_en) == 0) |
| assert_en = State::S1; |
| |
| if (GetSize(assert_en) == 2) |
| assert_en = module->LogicAnd(NEW_ID, assert_en[0], assert_en[1]); |
| |
| Cell *assert_cell = module->addAssert(NEW_ID, assert_a, assert_en); |
| |
| if (pmux->attributes.count("\\src") != 0) |
| assert_cell->attributes["\\src"] = pmux->attributes.at("\\src"); |
| } |
| }; |
| |
| struct AssertpmuxPass : public Pass { |
| AssertpmuxPass() : Pass("assertpmux", "adds asserts for parallel muxes") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" assertpmux [options] [selection]\n"); |
| log("\n"); |
| log("This command adds asserts to the design that assert that all parallel muxes\n"); |
| log("($pmux cells) have a maximum of one of their inputs enable at any time.\n"); |
| log("\n"); |
| log(" -noinit\n"); |
| log(" do not enforce the pmux condition during the init state\n"); |
| log("\n"); |
| log(" -always\n"); |
| log(" usually the $pmux condition is only checked when the $pmux output\n"); |
| log(" is used by the mux tree it drives. this option will deactivate this\n"); |
| log(" additional constraint and check the $pmux condition always.\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| bool flag_noinit = false; |
| bool flag_always = false; |
| |
| log_header(design, "Executing ASSERTPMUX pass (add asserts for $pmux cells).\n"); |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) |
| { |
| if (args[argidx] == "-noinit") { |
| flag_noinit = true; |
| continue; |
| } |
| if (args[argidx] == "-always") { |
| flag_always = true; |
| continue; |
| } |
| break; |
| } |
| extra_args(args, argidx, design); |
| |
| for (auto module : design->selected_modules()) |
| { |
| AssertpmuxWorker worker(module, flag_noinit, flag_always); |
| vector<Cell*> pmux_cells; |
| |
| for (auto cell : module->selected_cells()) |
| if (cell->type == "$pmux") |
| pmux_cells.push_back(cell); |
| |
| for (auto cell : pmux_cells) |
| worker.run(cell); |
| } |
| |
| } |
| } AssertpmuxPass; |
| |
| PRIVATE_NAMESPACE_END |