diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index b4ba2b03f..313af7d5c 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -204,6 +204,11 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) { + f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str()); + dump_const(f, ait->second); + f << stringf("\n"); + } f << stringf("%s case ", indent.c_str()); for (size_t i = 0; i < (*it)->compare.size(); i++) { if (i > 0) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 827af5d85..6288502a5 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -364,20 +364,22 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } } -void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false) +void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false, bool as_comment = false) { if (noattr) return; + if (attr2comment) + as_comment = true; for (auto it = attributes.begin(); it != attributes.end(); ++it) { - f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); + f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str()); f << stringf(" = "); if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) f << stringf(" 0 "); else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) f << stringf(" 1 "); else - dump_const(f, it->second, -1, 0, false, attr2comment); - f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); + dump_const(f, it->second, -1, 0, false, as_comment); + f << stringf(" %s%c", as_comment ? "*/" : "*)", term); } } @@ -1492,6 +1494,7 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw return; } + dump_attributes(f, indent, sw->attributes); f << stringf("%s" "casez (", indent.c_str()); dump_sigspec(f, sw->signal); f << stringf(")\n"); @@ -1511,7 +1514,9 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw dump_sigspec(f, (*it)->compare[i]); } } - f << stringf(":\n"); + f << stringf(":"); + dump_attributes(f, indent, (*it)->attributes, ' ', /*modattr=*/false, /*as_comment=*/true); + f << stringf("\n"); dump_case_body(f, indent + " ", *it); } @@ -1662,7 +1667,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } } - dump_attributes(f, indent, module->attributes, '\n', true); + dump_attributes(f, indent, module->attributes, '\n', /*attr2comment=*/true); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); bool keep_running = true; for (int port_id = 1; keep_running; port_id++) { diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 079fc11e5..571ddd988 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -504,6 +504,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; + current_case->attributes["\\src"] = stringf("%s:%d", child->filename.c_str(), child->linenum); last_generated_case = current_case; addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); for (auto node : child->children) { diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 44c99906a..b4b9693da 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -282,14 +282,14 @@ proc_stmt: } case_body sync_list TOK_END EOL; switch_stmt: - attr_list TOK_SWITCH sigspec EOL { + TOK_SWITCH sigspec EOL { RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; - rule->signal = *$3; + rule->signal = *$2; rule->attributes = attrbuf; switch_stack.back()->push_back(rule); attrbuf.clear(); - delete $3; - } switch_body TOK_END EOL; + delete $2; + } attr_list switch_body TOK_END EOL; attr_list: /* empty */ | @@ -298,9 +298,11 @@ attr_list: switch_body: switch_body TOK_CASE { RTLIL::CaseRule *rule = new RTLIL::CaseRule; + rule->attributes = attrbuf; switch_stack.back()->back()->cases.push_back(rule); switch_stack.push_back(&rule->switches); case_stack.push_back(rule); + attrbuf.clear(); } compare_list EOL case_body { switch_stack.pop_back(); case_stack.pop_back(); @@ -319,12 +321,15 @@ compare_list: /* empty */; case_body: + case_body attr_stmt | case_body switch_stmt | case_body assign_stmt | /* empty */; assign_stmt: TOK_ASSIGN sigspec sigspec EOL { + if (attrbuf.size() != 0) + rtlil_frontend_ilang_yyerror("dangling attribute"); case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); delete $2; delete $3; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f4fcf5dcf..82cbfaf28 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1327,7 +1327,7 @@ public: #endif }; -struct RTLIL::CaseRule +struct RTLIL::CaseRule : public RTLIL::AttrObject { std::vector compare; std::vector actions; diff --git a/kernel/yosys.cc b/kernel/yosys.cc index f95c0127b..a42a7c0b8 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -1273,6 +1273,10 @@ struct ScriptCmdPass : public Pass { log("If only one label is specified (without ':') then only the block\n"); log("marked with that label (until the next label) is executed.\n"); log("\n"); + log("In \"-scriptwire\" mode, the commands on the selected wire(s) will be executed\n"); + log("in the scope of (and thus, relative to) the wires' owning module(s). This\n"); + log("'-module' mode can be exited by using the 'cd' command.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 038ab7c7c..5a47988ec 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -51,14 +51,14 @@ struct BugpointPass : public Pass { log(" only consider crashes that place this string in the log file.\n"); log("\n"); log(" -fast\n"); - log(" run `clean -purge` after each minimization step. converges faster, but\n"); - log(" produces larger testcases, and may fail to produce any testcase at all if\n"); - log(" the crash is related to dangling wires.\n"); + log(" run `proc_clean; clean -purge` after each minimization step. converges\n"); + log(" faster, but produces larger testcases, and may fail to produce any\n"); + log(" testcase at all if the crash is related to dangling wires.\n"); log("\n"); log(" -clean\n"); - log(" run `clean -purge` before checking testcase and after finishing. produces\n"); - log(" smaller and more useful testcases, but may fail to produce any testcase\n"); - log(" at all if the crash is related to dangling wires.\n"); + log(" run `proc_clean; clean -purge` before checking testcase and after\n"); + log(" finishing. produces smaller and more useful testcases, but may fail to\n"); + log(" produce any testcase at all if the crash is related to dangling wires.\n"); log("\n"); log(" -modules\n"); log(" try to remove modules.\n"); @@ -72,6 +72,12 @@ struct BugpointPass : public Pass { log(" -connections\n"); log(" try to reconnect ports to 'x.\n"); log("\n"); + log(" -assigns\n"); + log(" try to remove process assigns from cases.\n"); + log("\n"); + log(" -updates\n"); + log(" try to remove process updates from syncs.\n"); + log("\n"); } bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script) @@ -110,6 +116,7 @@ struct BugpointPass : public Pass { RTLIL::Design *design_copy = new RTLIL::Design; for (auto &it : design->modules_) design_copy->add(it.second->clone()); + Pass::call(design_copy, "proc_clean -quiet"); Pass::call(design_copy, "clean -purge"); if (do_delete) @@ -117,7 +124,7 @@ struct BugpointPass : public Pass { return design_copy; } - RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections) + RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections, bool assigns, bool updates) { RTLIL::Design *design_copy = new RTLIL::Design; for (auto &it : design->modules_) @@ -225,6 +232,59 @@ struct BugpointPass : public Pass { } } } + if (assigns) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_blackbox_attribute()) + continue; + + for (auto &pr : mod->processes) + { + vector cases = {&pr.second->root_case}; + while (!cases.empty()) + { + RTLIL::CaseRule *cs = cases[0]; + cases.erase(cases.begin()); + for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) + { + if (index++ == seed) + { + log("Trying to remove assign %s %s in %s.%s.\n", log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str()); + cs->actions.erase(it); + return design_copy; + } + } + for (auto &sw : cs->switches) + cases.insert(cases.end(), sw->cases.begin(), sw->cases.end()); + } + } + } + } + if (updates) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_blackbox_attribute()) + continue; + + for (auto &pr : mod->processes) + { + for (auto &sy : pr.second->syncs) + { + for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) + { + if (index++ == seed) + { + log("Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str()); + sy->actions.erase(it); + return design_copy; + } + } + } + } + } + } return NULL; } @@ -232,7 +292,7 @@ struct BugpointPass : public Pass { { string yosys_cmd = "yosys", script, grep; bool fast = false, clean = false; - bool modules = false, ports = false, cells = false, connections = false, has_part = false; + bool modules = false, ports = false, cells = false, connections = false, assigns = false, updates = false, has_part = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -277,6 +337,16 @@ struct BugpointPass : public Pass { has_part = true; continue; } + if (args[argidx] == "-assigns") { + assigns = true; + has_part = true; + continue; + } + if (args[argidx] == "-updates") { + updates = true; + has_part = true; + continue; + } break; } extra_args(args, argidx, design); @@ -290,6 +360,8 @@ struct BugpointPass : public Pass { ports = true; cells = true; connections = true; + assigns = true; + updates = true; } if (!design->full_selection()) @@ -305,7 +377,7 @@ struct BugpointPass : public Pass { bool found_something = false, stage2 = false; while (true) { - if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections)) + if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections, assigns, updates)) { simplified = clean_design(simplified, fast, /*do_delete=*/true); diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index 397fe46a1..4b56979f8 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -1,5 +1,6 @@ OBJS += passes/proc/proc.o +OBJS += passes/proc/proc_prune.o OBJS += passes/proc/proc_clean.o OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_init.o @@ -7,4 +8,3 @@ OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o - diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index ef7cb0f71..a5b4a3112 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -37,6 +37,7 @@ struct ProcPass : public Pass { log("\n"); log(" proc_clean\n"); log(" proc_rmdead\n"); + log(" proc_prune\n"); log(" proc_init\n"); log(" proc_arst\n"); log(" proc_mux\n"); @@ -83,6 +84,7 @@ struct ProcPass : public Pass { Pass::call(design, "proc_clean"); if (!ifxmode) Pass::call(design, "proc_rmdead"); + Pass::call(design, "proc_prune"); Pass::call(design, "proc_init"); if (global_arst.empty()) Pass::call(design, "proc_arst"); diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index b69eba3f9..d069f152a 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -172,7 +172,7 @@ restart_proc_arst: sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; } for (auto &action : sync->actions) { - RTLIL::SigSpec rspec = action.second; + 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) diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 52141a8ec..97f4c6573 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -143,7 +143,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m YOSYS_NAMESPACE_END PRIVATE_NAMESPACE_BEGIN -void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) +void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool quiet) { int count = 0; bool did_something = true; @@ -160,7 +160,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) did_something = false; proc_clean_case(&proc->root_case, did_something, count, -1); } - if (count > 0) + if (count > 0 && !quiet) log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str()); total_count += count; } @@ -171,7 +171,10 @@ struct ProcCleanPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" proc_clean [selection]\n"); + log(" proc_clean [options] [selection]\n"); + log("\n"); + log(" -quiet\n"); + log(" do not print any messages.\n"); log("\n"); log("This pass removes empty parts of processes and ultimately removes a process\n"); log("if it contains only empty structures.\n"); @@ -180,9 +183,20 @@ struct ProcCleanPass : public Pass { void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { int total_count = 0; - log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + bool quiet = false; - extra_args(args, 1, design); + if (find(args.begin(), args.end(), "-quiet") == args.end()) + log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-quiet") { + quiet = true; + continue; + } + } + extra_args(args, argidx, design); for (auto mod : design->modules()) { std::vector delme; @@ -191,10 +205,11 @@ struct ProcCleanPass : public Pass { for (auto &proc_it : mod->processes) { if (!design->selected(mod, proc_it.second)) continue; - proc_clean(mod, proc_it.second, total_count); + proc_clean(mod, proc_it.second, total_count, quiet); if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 && proc_it.second->root_case.actions.size() == 0) { - log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); + if (!quiet) + log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); delme.push_back(proc_it.first); } } @@ -204,7 +219,8 @@ struct ProcCleanPass : public Pass { } } - log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); + if (!quiet) + log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); } } ProcCleanPass; diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index e2dc07e53..462a384b7 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -26,21 +26,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) -{ - log_assert(rule.compare.size() == 0); - - while (1) { - RTLIL::SigSpec tmp = sig; - for (auto &it : rule.actions) - tmp.replace(it.first, it.second); - if (tmp == sig) - break; - sig = tmp; - } -} - -void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc) { bool found_init = false; @@ -53,9 +39,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) for (auto &action : sync->actions) { RTLIL::SigSpec lhs = action.first; - RTLIL::SigSpec rhs = action.second; - - proc_get_const(rhs, proc->root_case); + RTLIL::SigSpec rhs = sigmap(action.second); if (!rhs.is_fully_const()) log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); @@ -120,10 +104,12 @@ struct ProcInitPass : public Pass { extra_args(args, 1, design); for (auto mod : design->modules()) - if (design->selected(mod)) + if (design->selected(mod)) { + SigMap sigmap(mod); for (auto &proc_it : mod->processes) if (design->selected(mod, proc_it.second)) - proc_init(mod, proc_it.second); + proc_init(mod, sigmap, proc_it.second); + } } } ProcInitPass; diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index aac0b121c..d029282fd 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -144,7 +144,13 @@ struct SnippetSwCache } }; -RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SwitchRule *sw, bool ifxmode) +void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs) +{ + cell->attributes = sw->attributes; + cell->add_strpool_attribute("\\src", cs->get_strpool_attribute("\\src")); +} + +RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode) { std::stringstream sstr; sstr << "$procmux$" << (autoidx++); @@ -173,7 +179,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s { // create compare cell RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); - eq_cell->attributes = sw->attributes; + apply_attrs(eq_cell, sw, cs); eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0); @@ -199,7 +205,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s // reduce cmp vector to one logic signal RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or"); - any_cell->attributes = sw->attributes; + apply_attrs(any_cell, sw, cs); any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width); @@ -212,7 +218,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(ctrl_wire); } -RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) +RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode) { log_assert(when_signal.size() == else_signal.size()); @@ -224,7 +230,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return when_signal; // compare results - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode); if (ctrl_sig.size() == 0) return when_signal; log_assert(ctrl_sig.size() == 1); @@ -234,7 +240,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s // create the multiplexer itself RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux"); - mux_cell->attributes = sw->attributes; + apply_attrs(mux_cell, sw, cs); mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size()); mux_cell->setPort("\\A", else_signal); @@ -246,7 +252,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(result_wire); } -void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) +void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode) { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); @@ -254,7 +260,7 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve if (when_signal == last_mux_cell->getPort("\\A")) return; - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode); log_assert(ctrl_sig.size() == 1); last_mux_cell->type = "$pmux"; @@ -395,9 +401,9 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d RTLIL::CaseRule *cs2 = sw->cases[case_idx]; RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) - append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); + append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, cs2, ifxmode); else - result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); + result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, cs2, ifxmode); } } diff --git a/passes/proc/proc_prune.cc b/passes/proc/proc_prune.cc new file mode 100644 index 000000000..9e00b0a8a --- /dev/null +++ b/passes/proc/proc_prune.cc @@ -0,0 +1,158 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2019 whitequark + * + * 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 +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct PruneWorker +{ + RTLIL::Module *module; + SigMap sigmap; + + int removed_count = 0, promoted_count = 0; + + PruneWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {} + + pool do_switch(RTLIL::SwitchRule *sw, pool assigned, pool &affected) + { + pool all_assigned; + bool full_case = sw->get_bool_attribute("\\full_case"); + bool first = true; + for (auto it : sw->cases) { + if (it->compare.empty()) + full_case = true; + pool case_assigned = do_case(it, assigned, affected); + if (first) { + first = false; + all_assigned = case_assigned; + } else { + for (auto &bit : all_assigned) + if (!case_assigned[bit]) + all_assigned.erase(bit); + } + } + if (full_case) + assigned.insert(all_assigned.begin(), all_assigned.end()); + return assigned; + } + + pool do_case(RTLIL::CaseRule *cs, pool assigned, pool &affected, + bool root = false) + { + for (auto it = cs->switches.rbegin(); it != cs->switches.rend(); ++it) { + pool sw_assigned = do_switch((*it), assigned, affected); + assigned.insert(sw_assigned.begin(), sw_assigned.end()); + } + pool remove; + for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ++it) { + RTLIL::SigSpec lhs = sigmap(it->first); + bool redundant = true; + for (auto &bit : lhs) { + if (bit.wire && !assigned[bit]) { + redundant = false; + break; + } + } + if (redundant) { + removed_count++; + remove.insert(*it); + } else { + if (root) { + bool promotable = true; + for (auto &bit : lhs) { + if (bit.wire && affected[bit]) { + promotable = false; + break; + } + } + if (promotable) { + promoted_count++; + module->connect(*it); + remove.insert(*it); + } + } + for (auto &bit : lhs) + if (bit.wire) + assigned.insert(bit); + for (auto &bit : lhs) + if (bit.wire) + affected.insert(bit); + } + } + for (auto it = cs->actions.begin(); it != cs->actions.end(); ) { + if (remove[*it]) { + it = cs->actions.erase(it); + } else it++; + } + return assigned; + } + + void do_process(RTLIL::Process *pr) + { + pool affected; + do_case(&pr->root_case, {}, affected, /*root=*/true); + } +}; + +struct ProcPrunePass : public Pass { + ProcPrunePass() : Pass("proc_prune", "remove redundant assignments") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_prune [selection]\n"); + log("\n"); + log("This pass identifies assignments in processes that are always overwritten by\n"); + log("a later assignment to the same signal and removes them.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + int total_removed_count = 0, total_promoted_count = 0; + log_header(design, "Executing PROC_PRUNE pass (remove redundant assignments in processes).\n"); + + extra_args(args, 1, design); + + for (auto mod : design->modules()) { + if (!design->selected(mod)) + continue; + PruneWorker worker(mod); + for (auto &proc_it : mod->processes) { + if (!design->selected(mod, proc_it.second)) + continue; + worker.do_process(proc_it.second); + } + total_removed_count += worker.removed_count; + total_promoted_count += worker.promoted_count; + } + + log("Removed %d redundant assignment%s.\n", + total_removed_count, total_removed_count == 1 ? "" : "s"); + log("Promoted %d assignment%s to connection%s.\n", + total_promoted_count, total_promoted_count == 1 ? "" : "s", total_promoted_count == 1 ? "" : "s"); + } +} ProcPrunePass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index 639cba2c2..09c9ba3af 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -48,6 +48,8 @@ struct SynthIntelPass : public ScriptPass { log(" -vqm \n"); log(" write the design to the specified Verilog Quartus Mapping File. Writing of an\n"); log(" output file is omitted if this parameter is not specified.\n"); + log(" Note that this backend has not been tested and is likely incompatible\n"); + log(" with recent versions of Quartus.\n"); log("\n"); log(" -vpr \n"); log(" write BLIF files for VPR flow experiments. The synthesized BLIF output file is not\n"); @@ -108,6 +110,7 @@ struct SynthIntelPass : public ScriptPass { } if (args[argidx] == "-vqm" && argidx + 1 < args.size()) { vout_file = args[++argidx]; + log_warning("The Quartus backend has not been tested recently and is likely incompatible with modern versions of Quartus.\n"); continue; } if (args[argidx] == "-vpr" && argidx + 1 < args.size()) { diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v index 35eea9858..d3f6a60cc 100644 --- a/techlibs/xilinx/cells_map.v +++ b/techlibs/xilinx/cells_map.v @@ -56,7 +56,6 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o localparam [DEPTH-1:0] INIT_R = brev(INIT); parameter _TECHMAP_CONSTMSK_L_ = 0; - parameter _TECHMAP_CONSTVAL_L_ = 0; wire CE; generate @@ -119,26 +118,33 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o else \$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(T6), .S0(L[5]), .S1(L[6]), .O(Q)); end - else if (DEPTH <= 129 && ~&_TECHMAP_CONSTMSK_L_) begin - // Handle cases where fixed-length depth is - // just 1 over a convenient value - \$__XILINX_SHREG_ #(.DEPTH(DEPTH+1), .INIT({INIT,1'b0}), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(L), .E(E), .Q(Q)); + // For fixed length, if just 1 over a convenient value, decompose + else if (DEPTH <= 129 && &_TECHMAP_CONSTMSK_L_) begin + wire T; + \$__XILINX_SHREG_ #(.DEPTH(DEPTH-1), .INIT(INIT[DEPTH-1:1]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(D), .L({32{1'b1}}), .E(E), .Q(T)); + \$__XILINX_SHREG_ #(.DEPTH(1), .INIT(INIT[0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(T), .L(L), .E(E), .Q(Q)); end + // For variable length, if just 1 over a convenient value, then bump up one more + else if (DEPTH < 129 && ~&_TECHMAP_CONSTMSK_L_) + \$__XILINX_SHREG_ #(.DEPTH(DEPTH+1), .INIT({INIT,1'b0}), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(L), .E(E), .Q(Q)); else begin - localparam lower_clog2 = $clog2((DEPTH+1)/2); - localparam lower_depth = 2 ** lower_clog2; - wire T0, T1, T2, T3; - if (&_TECHMAP_CONSTMSK_L_) begin - \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(lower_depth-1), .E(E), .Q(T0)); - \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T0), .L(DEPTH-lower_depth-1), .E(E), .Q(Q), .SO(T3)); - end - else begin - \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(L[lower_clog2-1:0]), .E(E), .Q(T0), .SO(T1)); - \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T1), .L(L[lower_clog2-1:0]), .E(E), .Q(T2), .SO(T3)); - assign Q = L[lower_clog2] ? T2 : T0; - end - if (DEPTH == 2 * lower_depth) - assign SO = T3; + localparam depth0 = 128; + localparam num_srl128 = DEPTH / depth0; + localparam depthN = DEPTH % depth0; + wire [num_srl128 + (depthN > 0 ? 1 : 0) - 1:0] T; + wire [num_srl128 + (depthN > 0 ? 1 : 0) :0] S; + assign S[0] = D; + genvar i; + for (i = 0; i < num_srl128; i++) + \$__XILINX_SHREG_ #(.DEPTH(depth0), .INIT(INIT[DEPTH-1-i*depth0-:depth0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(S[i]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[i]), .SO(S[i+1])); + + if (depthN > 0) + \$__XILINX_SHREG_ #(.DEPTH(depthN), .INIT(INIT[depthN-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(S[num_srl128]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[num_srl128])); + + if (&_TECHMAP_CONSTMSK_L_) + assign Q = T[num_srl128 + (depthN > 0 ? 1 : 0) - 1]; + else + assign Q = T[L[DEPTH-1:$clog2(depth0)]]; end endgenerate endmodule