| /* | |
| * 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/sigtools.h" | |
| #include "kernel/log.h" | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| YOSYS_NAMESPACE_BEGIN | |
| extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth); | |
| YOSYS_NAMESPACE_END | |
| USING_YOSYS_NAMESPACE | |
| PRIVATE_NAMESPACE_BEGIN | |
| bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity) | |
| { | |
| if (signal.size() != 1) | |
| return false; | |
| if (signal == ref) | |
| return true; | |
| for (auto cell : mod->cells()) | |
| { | |
| if (cell->type == "$reduce_or" && cell->getPort("\\Y") == signal) | |
| return check_signal(mod, cell->getPort("\\A"), ref, polarity); | |
| if (cell->type == "$reduce_bool" && cell->getPort("\\Y") == signal) | |
| return check_signal(mod, cell->getPort("\\A"), ref, polarity); | |
| if (cell->type == "$logic_not" && cell->getPort("\\Y") == signal) { | |
| polarity = !polarity; | |
| return check_signal(mod, cell->getPort("\\A"), ref, polarity); | |
| } | |
| if (cell->type == "$not" && cell->getPort("\\Y") == signal) { | |
| polarity = !polarity; | |
| return check_signal(mod, cell->getPort("\\A"), ref, polarity); | |
| } | |
| if (cell->type.in("$eq", "$eqx") && cell->getPort("\\Y") == signal) { | |
| if (cell->getPort("\\A").is_fully_const()) { | |
| if (!cell->getPort("\\A").as_bool()) | |
| polarity = !polarity; | |
| return check_signal(mod, cell->getPort("\\B"), ref, polarity); | |
| } | |
| if (cell->getPort("\\B").is_fully_const()) { | |
| if (!cell->getPort("\\B").as_bool()) | |
| polarity = !polarity; | |
| return check_signal(mod, cell->getPort("\\A"), ref, polarity); | |
| } | |
| } | |
| if (cell->type.in("$ne", "$nex") && cell->getPort("\\Y") == signal) { | |
| if (cell->getPort("\\A").is_fully_const()) { | |
| if (cell->getPort("\\A").as_bool()) | |
| polarity = !polarity; | |
| return check_signal(mod, cell->getPort("\\B"), ref, polarity); | |
| } | |
| if (cell->getPort("\\B").is_fully_const()) { | |
| if (cell->getPort("\\B").as_bool()) | |
| polarity = !polarity; | |
| return check_signal(mod, cell->getPort("\\A"), ref, polarity); | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec &rval, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity, bool unknown) | |
| { | |
| for (auto &action : cs->actions) { | |
| if (unknown) | |
| rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.size()), &rval); | |
| else | |
| rspec.replace(action.first, action.second, &rval); | |
| } | |
| for (auto sw : cs->switches) { | |
| if (sw->signal.size() == 0) { | |
| for (auto cs2 : sw->cases) | |
| apply_const(mod, rspec, rval, cs2, const_sig, polarity, unknown); | |
| } | |
| bool this_polarity = polarity; | |
| if (check_signal(mod, sw->signal, const_sig, this_polarity)) { | |
| for (auto cs2 : sw->cases) { | |
| for (auto comp : cs2->compare) | |
| if (comp == RTLIL::SigSpec(this_polarity, 1)) | |
| goto matched_case; | |
| if (cs2->compare.size() == 0) { | |
| matched_case: | |
| apply_const(mod, rspec, rval, cs2, const_sig, polarity, false); | |
| break; | |
| } | |
| } | |
| } else { | |
| for (auto cs2 : sw->cases) | |
| apply_const(mod, rspec, rval, cs2, const_sig, polarity, true); | |
| } | |
| } | |
| } | |
| void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity) | |
| { | |
| for (auto sw : cs->switches) { | |
| bool this_polarity = polarity; | |
| if (check_signal(mod, sw->signal, const_sig, this_polarity)) { | |
| bool found_rem_path = false; | |
| for (size_t i = 0; i < sw->cases.size(); i++) { | |
| RTLIL::CaseRule *cs2 = sw->cases[i]; | |
| for (auto comp : cs2->compare) | |
| if (comp == RTLIL::SigSpec(this_polarity, 1)) | |
| goto matched_case; | |
| if (found_rem_path) { | |
| matched_case: | |
| sw->cases.erase(sw->cases.begin() + (i--)); | |
| delete cs2; | |
| continue; | |
| } | |
| found_rem_path = true; | |
| cs2->compare.clear(); | |
| } | |
| sw->signal = RTLIL::SigSpec(); | |
| } else { | |
| for (auto cs2 : sw->cases) | |
| eliminate_const(mod, cs2, const_sig, polarity); | |
| } | |
| } | |
| int dummy_count = 0; | |
| bool did_something = true; | |
| while (did_something) { | |
| did_something = false; | |
| proc_clean_case(cs, did_something, dummy_count, 1); | |
| } | |
| } | |
| void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map) | |
| { | |
| restart_proc_arst: | |
| if (proc->root_case.switches.size() != 1) | |
| return; | |
| RTLIL::SigSpec root_sig = proc->root_case.switches[0]->signal; | |
| for (auto &sync : proc->syncs) { | |
| if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) { | |
| bool polarity = sync->type == RTLIL::SyncType::STp; | |
| if (check_signal(mod, root_sig, sync->signal, polarity)) { | |
| if (proc->syncs.size() == 1) { | |
| log("Found VHDL-style edge-trigger %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); | |
| } else { | |
| log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); | |
| sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; | |
| } | |
| for (auto &action : sync->actions) { | |
| RTLIL::SigSpec rspec = assign_map(action.second); | |
| RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); | |
| for (int i = 0; i < GetSize(rspec); i++) | |
| if (rspec[i].wire == NULL) | |
| rval[i] = rspec[i]; | |
| RTLIL::SigSpec last_rval; | |
| for (int count = 0; rval != last_rval; count++) { | |
| last_rval = rval; | |
| apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false); | |
| assign_map.apply(rval); | |
| if (rval.is_fully_const()) | |
| break; | |
| if (count > 100) | |
| log_error("Async reset %s yields endless loop at value %s for signal %s.\n", | |
| log_signal(sync->signal), log_signal(rval), log_signal(action.first)); | |
| rspec = rval; | |
| } | |
| if (rval.has_marked_bits()) | |
| log_error("Async reset %s yields non-constant value %s for signal %s.\n", | |
| log_signal(sync->signal), log_signal(rval), log_signal(action.first)); | |
| action.second = rval; | |
| } | |
| eliminate_const(mod, &proc->root_case, root_sig, polarity); | |
| goto restart_proc_arst; | |
| } | |
| } | |
| } | |
| } | |
| struct ProcArstPass : public Pass { | |
| ProcArstPass() : Pass("proc_arst", "detect asynchronous resets") { } | |
| void help() YS_OVERRIDE | |
| { | |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | |
| log("\n"); | |
| log(" proc_arst [-global_arst [!]<netname>] [selection]\n"); | |
| log("\n"); | |
| log("This pass identifies asynchronous resets in the processes and converts them\n"); | |
| log("to a different internal representation that is suitable for generating\n"); | |
| log("flip-flop cells with asynchronous resets.\n"); | |
| log("\n"); | |
| log(" -global_arst [!]<netname>\n"); | |
| log(" In modules that have a net with the given name, use this net as async\n"); | |
| log(" reset for registers that have been assign initial values in their\n"); | |
| log(" declaration ('reg foobar = constant_value;'). Use the '!' modifier for\n"); | |
| log(" active low reset signals. Note: the frontend stores the default value\n"); | |
| log(" in the 'init' attribute on the net.\n"); | |
| log("\n"); | |
| } | |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | |
| { | |
| std::string global_arst; | |
| bool global_arst_neg = false; | |
| log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n"); | |
| size_t argidx; | |
| for (argidx = 1; argidx < args.size(); argidx++) | |
| { | |
| if (args[argidx] == "-global_arst" && argidx+1 < args.size()) { | |
| global_arst = args[++argidx]; | |
| if (!global_arst.empty() && global_arst[0] == '!') { | |
| global_arst_neg = true; | |
| global_arst = global_arst.substr(1); | |
| } | |
| global_arst = RTLIL::escape_id(global_arst); | |
| continue; | |
| } | |
| break; | |
| } | |
| extra_args(args, argidx, design); | |
| pool<Wire*> delete_initattr_wires; | |
| for (auto mod : design->modules()) | |
| if (design->selected(mod)) { | |
| SigMap assign_map(mod); | |
| for (auto &proc_it : mod->processes) { | |
| if (!design->selected(mod, proc_it.second)) | |
| continue; | |
| proc_arst(mod, proc_it.second, assign_map); | |
| if (global_arst.empty() || mod->wire(global_arst) == nullptr) | |
| continue; | |
| std::vector<RTLIL::SigSig> arst_actions; | |
| for (auto sync : proc_it.second->syncs) | |
| if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) | |
| for (auto &act : sync->actions) { | |
| RTLIL::SigSpec arst_sig, arst_val; | |
| for (auto &chunk : act.first.chunks()) | |
| if (chunk.wire && chunk.wire->attributes.count("\\init")) { | |
| RTLIL::SigSpec value = chunk.wire->attributes.at("\\init"); | |
| value.extend_u0(chunk.wire->width, false); | |
| arst_sig.append(chunk); | |
| arst_val.append(value.extract(chunk.offset, chunk.width)); | |
| delete_initattr_wires.insert(chunk.wire); | |
| } | |
| if (arst_sig.size()) { | |
| log("Added global reset to process %s: %s <- %s\n", | |
| proc_it.first.c_str(), log_signal(arst_sig), log_signal(arst_val)); | |
| arst_actions.push_back(RTLIL::SigSig(arst_sig, arst_val)); | |
| } | |
| } | |
| if (!arst_actions.empty()) { | |
| RTLIL::SyncRule *sync = new RTLIL::SyncRule; | |
| sync->type = global_arst_neg ? RTLIL::SyncType::ST0 : RTLIL::SyncType::ST1; | |
| sync->signal = mod->wire(global_arst); | |
| sync->actions = arst_actions; | |
| proc_it.second->syncs.push_back(sync); | |
| } | |
| } | |
| } | |
| for (auto wire : delete_initattr_wires) | |
| wire->attributes.erase("\\init"); | |
| } | |
| } ProcArstPass; | |
| PRIVATE_NAMESPACE_END |