From 1eb696c78612b6fd21d27ded571e9a49fd82751c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 11:22:03 +0100 Subject: [PATCH 1/9] rtlil: replace SigSig actions with new type SyncAction --- backends/cxxrtl/cxxrtl_backend.cc | 16 +- backends/rtlil/rtlil_backend.cc | 9 +- backends/verilog/verilog_backend.cc | 17 +- frontends/ast/genrtlil.cc | 35 ++-- frontends/rtlil/rtlil_frontend.cc | 5 +- kernel/rtlil.h | 30 ++- passes/cmds/bugpoint.cc | 4 +- passes/cmds/check.cc | 12 +- passes/cmds/clean_zerowidth.cc | 9 +- passes/cmds/show.cc | 9 +- passes/proc/proc_arst.cc | 13 +- passes/proc/proc_clean.cc | 4 +- passes/proc/proc_dff.cc | 21 +- passes/proc/proc_dlatch.cc | 14 +- passes/proc/proc_init.cc | 4 +- passes/proc/proc_mux.cc | 292 ++++++++++++++++------------ passes/proc/proc_prune.cc | 8 +- passes/proc/proc_rom.cc | 17 +- passes/techmap/simplemap.cc | 38 ++-- 19 files changed, 305 insertions(+), 252 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 48710aff8..e5d64d5c7 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -494,8 +494,8 @@ struct FlowGraph { void add_case_rule_defs_uses(Node *node, const RTLIL::CaseRule *case_) { for (auto &action : case_->actions) { - add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); - add_uses(node, action.second); + add_defs(node, action.lhs, /*is_ff=*/false, /*inlinable=*/false); + add_uses(node, action.rhs); } for (auto sub_switch : case_->switches) { add_uses(node, sub_switch->signal); @@ -512,10 +512,10 @@ struct FlowGraph { for (auto sync : process->syncs) { for (auto &action : sync->actions) { if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe) - add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false); + add_defs(node, action.lhs, /*is_ff=*/true, /*inlinable=*/false); else - add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); - add_uses(node, action.second); + add_defs(node, action.lhs, /*is_ff=*/false, /*inlinable=*/false); + add_uses(node, action.rhs); } for (auto &memwr : sync->mem_write_actions) { add_uses(node, memwr.address); @@ -1623,12 +1623,12 @@ struct CxxrtlWorker { collect_sigspec_rhs(port.second, for_debug, cells); } - void dump_assign(const RTLIL::SigSig &sigsig, bool for_debug = false) + void dump_assign(const RTLIL::SyncAction &action, bool for_debug = false) { f << indent; - dump_sigspec_lhs(sigsig.first, for_debug); + dump_sigspec_lhs(action.lhs, for_debug); f << " = "; - dump_sigspec_rhs(sigsig.second, for_debug); + dump_sigspec_rhs(action.rhs, for_debug); f << ";\n"; } diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index d607be837..ef7795b33 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -189,7 +189,7 @@ void RTLIL_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL:: void RTLIL_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs) { - for (const auto& [lhs, rhs] : cs->actions) { + for (const auto& [lhs, rhs, _] : cs->actions) { f << stringf("%s" "assign ", indent); dump_sigspec(f, lhs); f << stringf(" "); @@ -243,7 +243,7 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT case RTLIL::STi: f << stringf("init\n"); break; } - for (const auto& [lhs, rhs] : sy->actions) { + for (const auto& [lhs, rhs, _] : sy->actions) { f << stringf("%s update ", indent); dump_sigspec(f, lhs); f << stringf(" "); @@ -375,8 +375,11 @@ void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl for (auto* module : design->modules()) { if (design->selected_whole_module(module->name)) flag_m = true; - if (design->selected(module)) + if (design->selected(module)) { count_selected_mods++; + if (module->has_processes()) + log_warning("Module %s contains processes. Case action sources attributes will be lost.\n", log_id(module)); + } } if (count_selected_mods > 1) flag_m = true; diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index c747aa901..3f5c16e11 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2122,13 +2122,14 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { - if (it->first.size() == 0) + if (it->lhs.size() == 0) continue; f << stringf("%s ", indent); - dump_sigspec(f, it->first); + dump_sigspec(f, it->lhs); f << stringf(" = "); - dump_sigspec(f, it->second); + dump_sigspec(f, it->rhs); f << stringf(";\n"); + // TODO } } @@ -2259,7 +2260,7 @@ void case_body_find_regs(RTLIL::CaseRule *cs) case_body_find_regs(*it2); for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { - for (auto &c : it->first.chunks()) + for (auto &c : it->lhs.chunks()) if (c.wire != NULL) reg_wires.insert(c.wire->name); } @@ -2271,7 +2272,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo case_body_find_regs(&proc->root_case); for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) { - for (auto &c : it2->first.chunks()) + for (auto &c : it2->lhs.chunks()) if (c.wire != NULL) reg_wires.insert(c.wire->name); } @@ -2328,12 +2329,12 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo } for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) { - if (it->first.size() == 0) + if (it->lhs.size() == 0) continue; f << stringf("%s ", indent); - dump_sigspec(f, it->first); + dump_sigspec(f, it->lhs); f << stringf(" <= "); - dump_sigspec(f, it->second); + dump_sigspec(f, it->rhs); f << stringf(";\n"); } diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index c26750c98..858714b5b 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -382,7 +382,7 @@ struct AST_INTERNAL::ProcessGenerator if (found_anyedge_syncs) { if (found_global_syncs) always->input_error("Found non-synthesizable event list!\n"); - log("Note: Assuming pure combinatorial block at %s in\n", always->loc_string()); + log("Note: Assuming pure combinatorial block at %s in\n", always->location.to_string()); log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n"); log("use of @* instead of @(...) for better match of synthesis and simulation.\n"); } @@ -402,14 +402,14 @@ struct AST_INTERNAL::ProcessGenerator syncrule->signal = child->children[0]->genRTLIL(); if (GetSize(syncrule->signal) != 1) always->input_error("Found posedge/negedge event on a signal that is not 1 bit wide!\n"); - addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true); + addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, child.get(), true); proc->syncs.push_back(syncrule); } if (proc->syncs.empty()) { RTLIL::SyncRule *syncrule = new RTLIL::SyncRule; syncrule->type = found_global_syncs ? RTLIL::STg : RTLIL::STa; syncrule->signal = RTLIL::SigSpec(); - addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true); + addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, always.get(), true); proc->syncs.push_back(syncrule); } @@ -417,7 +417,7 @@ struct AST_INTERNAL::ProcessGenerator if ((flag_nolatches || always->get_bool_attribute(ID::nolatches) || current_module->get_bool_attribute(ID::nolatches)) && !found_clocked_sync) { subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from))); } else { - addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from); + addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from, always.get()); } // process the AST @@ -441,7 +441,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::SigSpec lhs = init_lvalue_c; RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width); remove_unwanted_lvalue_bits(lhs, rhs); - sync->actions.push_back(RTLIL::SigSig(lhs, rhs)); + sync->actions.push_back({lhs, rhs}); offset += lhs.size(); } } @@ -548,7 +548,7 @@ struct AST_INTERNAL::ProcessGenerator void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) - it->first.remove2(pattern, &it->second); + it->lhs.remove2(pattern, &it->rhs); for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) @@ -557,7 +557,7 @@ struct AST_INTERNAL::ProcessGenerator // add an assignment (aka "action") but split it up in chunks. this way huge assignments // are avoided and the generated $mux cells have a more "natural" size. - void addChunkActions(std::vector &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false) + void addChunkActions(std::vector &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, AstNode* ast, bool inSyncRule = false) { if (inSyncRule && initSyncSignals.size() > 0) { init_lvalue.append(lvalue.extract(initSyncSignals)); @@ -573,7 +573,7 @@ struct AST_INTERNAL::ProcessGenerator if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute(ID::nosync)) rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size()); remove_unwanted_lvalue_bits(lhs, rhs); - actions.push_back(RTLIL::SigSig(lhs, rhs)); + actions.push_back({lhs, rhs}); offset += lhs.size(); } } @@ -613,7 +613,7 @@ struct AST_INTERNAL::ProcessGenerator removeSignalFromCaseTree(lvalue, current_case); remove_unwanted_lvalue_bits(lvalue, rvalue); - current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue)); + current_case->actions.push_back({lvalue, rvalue}); } break; @@ -657,10 +657,11 @@ struct AST_INTERNAL::ProcessGenerator subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); RTLIL::CaseRule *backup_case = current_case; + // here current_case = new RTLIL::CaseRule; set_src_attr(current_case, child.get()); last_generated_case = current_case; - addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); + addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue, child.get()); for (auto& node : child->children) { if (node->type == AST_DEFAULT) default_case = current_case; @@ -687,13 +688,13 @@ struct AST_INTERNAL::ProcessGenerator last_generated_case->compare.clear(); #else default_case = new RTLIL::CaseRule; - addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue))); + addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue)), ast); sw->cases.push_back(default_case); #endif } else { if (default_case == nullptr) { default_case = new RTLIL::CaseRule; - addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); + addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue, ast); } sw->cases.push_back(default_case); } @@ -703,7 +704,7 @@ struct AST_INTERNAL::ProcessGenerator this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); removeSignalFromCaseTree(this_case_eq_lvalue, current_case); - addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp); + addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp, ast); } break; @@ -728,8 +729,8 @@ struct AST_INTERNAL::ProcessGenerator Wire *en = current_module->addWire(sstr.str() + "_EN", 1); set_src_attr(en, ast); - proc->root_case.actions.push_back(SigSig(en, false)); - current_case->actions.push_back(SigSig(en, true)); + proc->root_case.actions.push_back({en, SigSpec(false)}); + current_case->actions.push_back({en, SigSpec(true)}); RTLIL::SigSpec triggers; RTLIL::Const::Builder polarity_builder; @@ -826,8 +827,8 @@ struct AST_INTERNAL::ProcessGenerator Wire *en = current_module->addWire(cellname.str() + "_EN", 1); set_src_attr(en, ast); - proc->root_case.actions.push_back(SigSig(en, false)); - current_case->actions.push_back(SigSig(en, true)); + proc->root_case.actions.push_back({en, SigSpec(false)}); + current_case->actions.push_back({en, SigSpec(true)}); RTLIL::SigSpec triggers; RTLIL::Const::Builder polarity_builder; diff --git a/frontends/rtlil/rtlil_frontend.cc b/frontends/rtlil/rtlil_frontend.cc index 04d01fc93..d5cf84d9c 100644 --- a/frontends/rtlil/rtlil_frontend.cc +++ b/frontends/rtlil/rtlil_frontend.cc @@ -23,6 +23,7 @@ #include "kernel/register.h" #include "kernel/log.h" +#include "kernel/rtlil.h" #include "kernel/utils.h" #include #include @@ -623,7 +624,7 @@ struct RTLILFrontendWorker { "The assign statement is reordered to come before all switch statements."); RTLIL::SigSpec s1 = parse_sigspec(); RTLIL::SigSpec s2 = parse_sigspec(); - current_case->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2))); + current_case->actions.push_back({std::move(s1), std::move(s2)}); expect_eol(); } else return; @@ -714,7 +715,7 @@ struct RTLILFrontendWorker { if (try_parse_keyword("update")) { RTLIL::SigSpec s1 = parse_sigspec(); RTLIL::SigSpec s2 = parse_sigspec(); - rule->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2))); + rule->actions.push_back({std::move(s1), std::move(s2)}); expect_eol(); continue; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 394c6f25d..16e80ce60 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -116,6 +116,7 @@ namespace RTLIL struct CaseRule; struct SwitchRule; struct MemWriteAction; + struct SyncAction; struct SyncRule; struct Process; struct Binding; @@ -1113,6 +1114,13 @@ struct RTLIL::AttrObject std::string get_src_attribute() const { return get_string_attribute(ID::src); } + void transfer_attribute(const AttrObject* from, const IdString& attr) { + if (from->has_attribute(attr)) + attributes[attr] = from->attributes.at(attr); + } + void transfer_src_attribute(const AttrObject* from) { + transfer_attribute(from, ID::src); + } void set_hdlname_attribute(const vector &hierarchy); vector get_hdlname_attribute() const; @@ -2194,7 +2202,7 @@ public: struct RTLIL::CaseRule : public RTLIL::AttrObject { std::vector compare; - std::vector actions; + std::vector actions; std::vector switches; ~CaseRule(); @@ -2229,11 +2237,17 @@ struct RTLIL::MemWriteAction : RTLIL::AttrObject RTLIL::Const priority_mask; }; +struct RTLIL::SyncAction +{ + RTLIL::SigSpec lhs; + RTLIL::SigSpec rhs; +}; + struct RTLIL::SyncRule { RTLIL::SyncType type; RTLIL::SigSpec signal; - std::vector actions; + std::vector actions; std::vector mem_write_actions; template void rewrite_sigspecs(T &functor); @@ -2363,8 +2377,8 @@ void RTLIL::CaseRule::rewrite_sigspecs(T &functor) { for (auto &it : compare) functor(it); for (auto &it : actions) { - functor(it.first); - functor(it.second); + functor(it.lhs); + functor(it.rhs); } for (auto it : switches) it->rewrite_sigspecs(functor); @@ -2375,7 +2389,7 @@ void RTLIL::CaseRule::rewrite_sigspecs2(T &functor) { for (auto &it : compare) functor(it); for (auto &it : actions) { - functor(it.first, it.second); + functor(it.lhs, it.rhs); } for (auto it : switches) it->rewrite_sigspecs2(functor); @@ -2402,8 +2416,8 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor) { functor(signal); for (auto &it : actions) { - functor(it.first); - functor(it.second); + functor(it.lhs); + functor(it.rhs); } for (auto &it : mem_write_actions) { functor(it.address); @@ -2417,7 +2431,7 @@ void RTLIL::SyncRule::rewrite_sigspecs2(T &functor) { functor(signal); for (auto &it : actions) { - functor(it.first, it.second); + functor(it.lhs, it.rhs); } for (auto &it : mem_write_actions) { functor(it.address); diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 897dd8459..f6aa79fc8 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -361,7 +361,7 @@ struct BugpointPass : public Pass { { if (index++ == seed) { - log_header(design, "Trying to remove assign %s %s in %s.%s.\n", log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first)); + log_header(design, "Trying to remove assign %s %s in %s.%s.\n", log_signal(it->lhs), log_signal(it->rhs), log_id(mod), log_id(pr.first)); cs->actions.erase(it); return design_copy; } @@ -387,7 +387,7 @@ struct BugpointPass : public Pass { { if (index++ == seed) { - log_header(design, "Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first)); + log_header(design, "Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal(it->lhs), log_signal(it->rhs), log_id(mod), log_id(pr.first)); sy->actions.erase(it); return design_copy; } diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index b7a5feb57..4cfc2915e 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -130,12 +130,12 @@ struct CheckPass : public Pass { std::vector all_cases = {&proc_it.second->root_case}; for (size_t i = 0; i < all_cases.size(); i++) { for (auto action : all_cases[i]->actions) { - for (auto bit : sigmap(action.first)) + for (auto bit : sigmap(action.lhs)) wire_drivers[bit].push_back( stringf("action %s <= %s (case rule) in process %s", - log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); + log_signal(action.lhs), log_signal(action.rhs), log_id(proc_it.first))); - for (auto bit : sigmap(action.second)) + for (auto bit : sigmap(action.rhs)) if (bit.wire) used_wires.insert(bit); } for (auto switch_ : all_cases[i]->switches) { @@ -151,11 +151,11 @@ struct CheckPass : public Pass { for (auto bit : sigmap(sync->signal)) if (bit.wire) used_wires.insert(bit); for (auto action : sync->actions) { - for (auto bit : sigmap(action.first)) + for (auto bit : sigmap(action.lhs)) wire_drivers[bit].push_back( stringf("action %s <= %s (sync rule) in process %s", - log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); - for (auto bit : sigmap(action.second)) + log_signal(action.lhs), log_signal(action.rhs), log_id(proc_it.first))); + for (auto bit : sigmap(action.rhs)) if (bit.wire) used_wires.insert(bit); } for (auto memwr : sync->mem_write_actions) { diff --git a/passes/cmds/clean_zerowidth.cc b/passes/cmds/clean_zerowidth.cc index d48d3a958..3dc6ed195 100644 --- a/passes/cmds/clean_zerowidth.cc +++ b/passes/cmds/clean_zerowidth.cc @@ -17,6 +17,7 @@ * */ +#include "kernel/rtlil.h" #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/mem.h" @@ -40,9 +41,9 @@ struct CleanZeroWidthPass : public Pass { void clean_case(RTLIL::CaseRule *cs) { - std::vector new_actions; + std::vector new_actions; for (auto &action : cs->actions) - if (GetSize(action.first) != 0) + if (GetSize(action.lhs) != 0) new_actions.push_back(action); std::swap(new_actions, cs->actions); for (auto sw : cs->switches) @@ -167,9 +168,9 @@ struct CleanZeroWidthPass : public Pass { new_memwr_actions.push_back(memwr); } std::swap(new_memwr_actions, sync->mem_write_actions); - std::vector new_actions; + std::vector new_actions; for (auto &action : sync->actions) - if (GetSize(action.first) != 0) + if (GetSize(action.lhs) != 0) new_actions.push_back(action); std::swap(new_actions, sync->actions); } diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index d0d9c0f85..5bebe9ae6 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -17,6 +17,7 @@ * */ +#include "kernel/rtlil.h" #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/log_help.h" @@ -370,12 +371,12 @@ struct ShowWorker signals.insert(it); } - void collect_proc_signals(std::vector &obj, std::set &input_signals, std::set &output_signals) + void collect_proc_signals(std::vector &obj, std::set &input_signals, std::set &output_signals) { for (auto &it : obj) { - output_signals.insert(it.first); - if (!it.second.is_fully_const()) - input_signals.insert(it.second); + output_signals.insert(it.lhs); + if (!it.rhs.is_fully_const()) + input_signals.insert(it.rhs); } } diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 92d8d0569..56938ff7f 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -18,6 +18,7 @@ */ #include "kernel/register.h" +#include "kernel/rtlil.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include @@ -89,9 +90,9 @@ void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec { for (auto &action : cs->actions) { if (unknown) - rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.size()), &rval); + rspec.replace(action.lhs, RTLIL::SigSpec(RTLIL::State::Sm, action.rhs.size()), &rval); else - rspec.replace(action.first, action.second, &rval); + rspec.replace(action.lhs, action.rhs, &rval); } for (auto sw : cs->switches) { @@ -209,7 +210,7 @@ void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map) arst_syncs.push_back(sync); edge_syncs.erase(it); for (auto &action : sync->actions) { - action.second = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, action.second, action.first); + action.rhs = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, action.rhs, action.lhs); } for (auto &memwr : sync->mem_write_actions) { RTLIL::SigSpec en = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.enable, memwr.enable); @@ -294,12 +295,12 @@ struct ProcArstPass : public Pass { proc_arst(mod, proc, assign_map); if (global_arst.empty() || mod->wire(global_arst) == nullptr) continue; - std::vector arst_actions; + std::vector arst_actions; for (auto sync : proc->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()) + for (auto &chunk : act.lhs.chunks()) if (chunk.wire && chunk.wire->attributes.count(ID::init)) { RTLIL::SigSpec value = chunk.wire->attributes.at(ID::init); value.extend_u0(chunk.wire->width, false); @@ -310,7 +311,7 @@ struct ProcArstPass : public Pass { if (arst_sig.size()) { log("Added global reset to process %s: %s <- %s\n", proc->name.c_str(), log_signal(arst_sig), log_signal(arst_val)); - arst_actions.push_back(RTLIL::SigSig(arst_sig, arst_val)); + arst_actions.push_back({arst_sig, arst_val}); } } if (!arst_actions.empty()) { diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 19c2be4ca..c95c473bf 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -133,7 +133,7 @@ YOSYS_NAMESPACE_BEGIN void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth) { for (size_t i = 0; i < cs->actions.size(); i++) { - if (cs->actions[i].first.size() == 0) { + if (cs->actions[i].lhs.size() == 0) { did_something = true; cs->actions.erase(cs->actions.begin() + (i--)); } @@ -159,7 +159,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool bool did_something = true; for (size_t i = 0; i < proc->syncs.size(); i++) { for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++) - if (proc->syncs[i]->actions[j].first.size() == 0) + if (proc->syncs[i]->actions[j].lhs.size() == 0) proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--)); if (proc->syncs[i]->actions.size() == 0 && proc->syncs[i]->mem_write_actions.size() == 0) { delete proc->syncs[i]; diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 06c740a88..d764a20af 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -34,8 +34,8 @@ RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) for (auto sync : proc->syncs) for (auto &action : sync->actions) - if (action.first.size() > 0) { - lvalue = action.first; + if (action.lhs.size() > 0) { + lvalue = action.lhs; lvalue.sort_and_unify(); break; } @@ -43,7 +43,7 @@ RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) for (auto sync : proc->syncs) { RTLIL::SigSpec this_lvalue; for (auto &action : sync->actions) - this_lvalue.append(action.first); + this_lvalue.append(action.lhs); this_lvalue.sort_and_unify(); RTLIL::SigSpec common_sig = this_lvalue.extract(lvalue); if (common_sig.size() > 0) @@ -172,35 +172,35 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) for (auto sync : proc->syncs) for (auto &action : sync->actions) { - if (action.first.extract(sig).size() == 0) + if (action.lhs.extract(sig).size() == 0) continue; if (sync->type == RTLIL::SyncType::ST0 || sync->type == RTLIL::SyncType::ST1) { RTLIL::SigSpec rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); - sig.replace(action.first, action.second, &rstval); + sig.replace(action.lhs, action.rhs, &rstval); async_rules.emplace_back(rstval, sync); } else if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) { if (sync_edge != NULL && sync_edge != sync) log_error("Multiple edge sensitive events found for this signal!\n"); - sig.replace(action.first, action.second, &insig); + sig.replace(action.lhs, action.rhs, &insig); sync_edge = sync; } else if (sync->type == RTLIL::SyncType::STa) { if (sync_always != NULL && sync_always != sync) log_error("Multiple always events found for this signal!\n"); - sig.replace(action.first, action.second, &insig); + sig.replace(action.lhs, action.rhs, &insig); sync_always = sync; } else if (sync->type == RTLIL::SyncType::STg) { - sig.replace(action.first, action.second, &insig); + sig.replace(action.lhs, action.rhs, &insig); global_clock = true; } else { log_error("Event with any-edge sensitivity found for this signal!\n"); } - action.first.remove2(sig, &action.second); + action.lhs.remove2(sig, &action.rhs); } // If all async rules assign the same value, priority ordering between @@ -223,7 +223,8 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) // as ones coming from the module single_async_rule.type = RTLIL::SyncType::ST1; single_async_rule.signal = mod->ReduceOr(NEW_ID, triggers); - single_async_rule.actions.push_back(RTLIL::SigSig(sig, rstval)); + // TODO + single_async_rule.actions.push_back({sig, rstval}); // Replace existing rules with this new rule async_rules.clear(); diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index bda2d272f..6066a89da 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -364,17 +364,17 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) for (auto ss : sr->actions) { - db.sigmap.apply(ss.first); - db.sigmap.apply(ss.second); + db.sigmap.apply(ss.lhs); + db.sigmap.apply(ss.rhs); - if (!db.quickcheck(ss.second, ss.first)) { - nolatches_bits.first.append(ss.first); - nolatches_bits.second.append(ss.second); + if (!db.quickcheck(ss.rhs, ss.lhs)) { + nolatches_bits.first.append(ss.lhs); + nolatches_bits.second.append(ss.rhs); continue; } - for (int i = 0; i < GetSize(ss.first); i++) - latches_out_in[ss.first[i]] = ss.second[i]; + for (int i = 0; i < GetSize(ss.lhs); i++) + latches_out_in[ss.lhs[i]] = ss.rhs[i]; } sr->actions.clear(); } diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index 3d27eaa9a..7e9553d20 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -35,8 +35,8 @@ void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc) for (auto &action : sync->actions) { - RTLIL::SigSpec lhs = action.first; - RTLIL::SigSpec rhs = sigmap(action.second); + RTLIL::SigSpec lhs = action.lhs; + RTLIL::SigSpec rhs = sigmap(action.rhs); 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)); diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 68127d1bc..5bef77cd2 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -98,7 +98,7 @@ struct SigSnippets void insert(const RTLIL::CaseRule *cs) { for (auto &action : cs->actions) - insert(action.first); + insert(action.lhs); for (auto sw : cs->switches) for (auto cs2 : sw->cases) @@ -121,7 +121,7 @@ struct SnippetSwCache void insert(const RTLIL::CaseRule *cs, vector &sw_stack) { for (auto &action : cs->actions) - for (auto bit : action.first) { + for (auto bit : action.lhs) { int sn = snippets->bit2snippet.at(bit, -1); if (sn < 0) continue; @@ -150,130 +150,138 @@ void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::Ca cell->add_strpool_attribute(ID::src, cs->get_strpool_attribute(ID::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++); +struct MuxGenCtx { + RTLIL::Module *mod; + const RTLIL::SigSpec &signal; + const std::vector *compare; + RTLIL::Cell *last_mux_cell; + RTLIL::SwitchRule *sw; + RTLIL::CaseRule *cs; + bool ifxmode; - RTLIL::Wire *cmp_wire = mod->addWire(sstr.str() + "_CMP", 0); + RTLIL::SigSpec gen_cmp() { + std::stringstream sstr; + sstr << "$procmux$" << (autoidx++); - for (auto comp : compare) - { - RTLIL::SigSpec sig = signal; + RTLIL::Wire *cmp_wire = mod->addWire(sstr.str() + "_CMP", 0); - // get rid of don't-care bits - log_assert(sig.size() == comp.size()); - for (int i = 0; i < comp.size(); i++) - if (comp[i] == RTLIL::State::Sa) { - sig.remove(i); - comp.remove(i--); - } - if (comp.size() == 0) - return RTLIL::SigSpec(); - - if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode) + for (auto comp : *compare) { - mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig)); + RTLIL::SigSpec sig = signal; + + // get rid of don't-care bits + log_assert(sig.size() == comp.size()); + for (int i = 0; i < comp.size(); i++) + if (comp[i] == RTLIL::State::Sa) { + sig.remove(i); + comp.remove(i--); + } + if (comp.size() == 0) + return RTLIL::SigSpec(); + + if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode) + { + mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig)); + } + else + { + // create compare cell + RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str(), cmp_wire->width), ifxmode ? ID($eqx) : ID($eq)); + apply_attrs(eq_cell, sw, cs); + + eq_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0); + eq_cell->parameters[ID::B_SIGNED] = RTLIL::Const(0); + + eq_cell->parameters[ID::A_WIDTH] = RTLIL::Const(sig.size()); + eq_cell->parameters[ID::B_WIDTH] = RTLIL::Const(comp.size()); + eq_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1); + + eq_cell->setPort(ID::A, sig); + eq_cell->setPort(ID::B, comp); + eq_cell->setPort(ID::Y, RTLIL::SigSpec(cmp_wire, cmp_wire->width++)); + } + } + + RTLIL::Wire *ctrl_wire; + if (cmp_wire->width == 1) + { + ctrl_wire = cmp_wire; } else { - // create compare cell - RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str(), cmp_wire->width), ifxmode ? ID($eqx) : ID($eq)); - apply_attrs(eq_cell, sw, cs); + ctrl_wire = mod->addWire(sstr.str() + "_CTRL"); - eq_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0); - eq_cell->parameters[ID::B_SIGNED] = RTLIL::Const(0); + // reduce cmp vector to one logic signal + RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", ID($reduce_or)); + apply_attrs(any_cell, sw, cs); - eq_cell->parameters[ID::A_WIDTH] = RTLIL::Const(sig.size()); - eq_cell->parameters[ID::B_WIDTH] = RTLIL::Const(comp.size()); - eq_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1); + any_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0); + any_cell->parameters[ID::A_WIDTH] = RTLIL::Const(cmp_wire->width); + any_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1); - eq_cell->setPort(ID::A, sig); - eq_cell->setPort(ID::B, comp); - eq_cell->setPort(ID::Y, RTLIL::SigSpec(cmp_wire, cmp_wire->width++)); + any_cell->setPort(ID::A, cmp_wire); + any_cell->setPort(ID::Y, RTLIL::SigSpec(ctrl_wire)); } + + return RTLIL::SigSpec(ctrl_wire); } - RTLIL::Wire *ctrl_wire; - if (cmp_wire->width == 1) - { - ctrl_wire = cmp_wire; - } - else - { - ctrl_wire = mod->addWire(sstr.str() + "_CTRL"); + RTLIL::SigSpec gen_mux(RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal) { + log_assert(when_signal.size() == else_signal.size()); - // reduce cmp vector to one logic signal - RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", ID($reduce_or)); - apply_attrs(any_cell, sw, cs); + std::stringstream sstr; + sstr << "$procmux$" << (autoidx++); - any_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0); - any_cell->parameters[ID::A_WIDTH] = RTLIL::Const(cmp_wire->width); - any_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1); + // the trivial cases + if (compare->size() == 0 || when_signal == else_signal) + return when_signal; - any_cell->setPort(ID::A, cmp_wire); - any_cell->setPort(ID::Y, RTLIL::SigSpec(ctrl_wire)); + // compare results + RTLIL::SigSpec ctrl_sig = gen_cmp(); + if (ctrl_sig.size() == 0) + return when_signal; + log_assert(ctrl_sig.size() == 1); + + // prepare multiplexer output signal + RTLIL::Wire *result_wire = mod->addWire(sstr.str() + "_Y", when_signal.size()); + + // create the multiplexer itself + RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), ID($mux)); + apply_attrs(mux_cell, sw, cs); + + mux_cell->parameters[ID::WIDTH] = RTLIL::Const(when_signal.size()); + mux_cell->setPort(ID::A, else_signal); + mux_cell->setPort(ID::B, when_signal); + mux_cell->setPort(ID::S, ctrl_sig); + mux_cell->setPort(ID::Y, RTLIL::SigSpec(result_wire)); + + last_mux_cell = mux_cell; + return RTLIL::SigSpec(result_wire); } - return RTLIL::SigSpec(ctrl_wire); -} + void append_pmux(RTLIL::SigSpec when_signal) { + log_assert(last_mux_cell != NULL); + log_assert(when_signal.size() == last_mux_cell->getPort(ID::A).size()); -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()); + if (when_signal == last_mux_cell->getPort(ID::A)) + return; - std::stringstream sstr; - sstr << "$procmux$" << (autoidx++); + RTLIL::SigSpec ctrl_sig = gen_cmp(); + log_assert(ctrl_sig.size() == 1); + last_mux_cell->type = ID($pmux); - // the trivial cases - if (compare.size() == 0 || when_signal == else_signal) - return when_signal; + RTLIL::SigSpec new_s = last_mux_cell->getPort(ID::S); + new_s.append(ctrl_sig); + last_mux_cell->setPort(ID::S, new_s); - // compare results - 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); + RTLIL::SigSpec new_b = last_mux_cell->getPort(ID::B); + new_b.append(when_signal); + last_mux_cell->setPort(ID::B, new_b); - // prepare multiplexer output signal - RTLIL::Wire *result_wire = mod->addWire(sstr.str() + "_Y", when_signal.size()); + last_mux_cell->parameters[ID::S_WIDTH] = last_mux_cell->getPort(ID::S).size(); + } - // create the multiplexer itself - RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), ID($mux)); - apply_attrs(mux_cell, sw, cs); - - mux_cell->parameters[ID::WIDTH] = RTLIL::Const(when_signal.size()); - mux_cell->setPort(ID::A, else_signal); - mux_cell->setPort(ID::B, when_signal); - mux_cell->setPort(ID::S, ctrl_sig); - mux_cell->setPort(ID::Y, RTLIL::SigSpec(result_wire)); - - last_mux_cell = mux_cell; - 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, RTLIL::CaseRule *cs, bool ifxmode) -{ - log_assert(last_mux_cell != NULL); - log_assert(when_signal.size() == last_mux_cell->getPort(ID::A).size()); - - if (when_signal == last_mux_cell->getPort(ID::A)) - return; - - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode); - log_assert(ctrl_sig.size() == 1); - last_mux_cell->type = ID($pmux); - - RTLIL::SigSpec new_s = last_mux_cell->getPort(ID::S); - new_s.append(ctrl_sig); - last_mux_cell->setPort(ID::S, new_s); - - RTLIL::SigSpec new_b = last_mux_cell->getPort(ID::B); - new_b.append(when_signal); - last_mux_cell->setPort(ID::B, new_b); - - last_mux_cell->parameters[ID::S_WIDTH] = last_mux_cell->getPort(ID::S).size(); -} +}; const pool &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRule *sw) { @@ -290,7 +298,7 @@ const pool &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul pool case_bits; for (auto it : cs->actions) { - for (auto bit : it.first) + for (auto bit : it.lhs) case_bits.insert(bit); } @@ -317,20 +325,28 @@ const pool &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul return swcache.full_case_bits_cache.at(sw); } +struct MuxTreeContext { + RTLIL::Module* mod; + SnippetSwCache& swcache; + dict &swpara; + RTLIL::CaseRule *cs; + const RTLIL::SigSpec &sig; + RTLIL::SigSpec defval; + const bool ifxmode; +}; -RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict &swpara, - RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) +RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx) { - RTLIL::SigSpec result = defval; + RTLIL::SigSpec result = ctx.defval; - for (auto &action : cs->actions) { - sig.replace(action.first, action.second, &result); - action.first.remove2(sig, &action.second); + for (auto &action : ctx.cs->actions) { + ctx.sig.replace(action.lhs, action.rhs, &result); + action.lhs.remove2(ctx.sig, &action.rhs); } - for (auto sw : cs->switches) + for (auto sw : ctx.cs->switches) { - if (!swcache.check(sw)) + if (!ctx.swcache.check(sw)) continue; // detect groups of parallel cases @@ -338,7 +354,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d bool is_simple_parallel_case = true; if (!sw->get_bool_attribute(ID::parallel_case)) { - if (!swpara.count(sw)) { + if (!ctx.swpara.count(sw)) { pool case_values; for (size_t i = 0; i < sw->cases.size(); i++) { RTLIL::CaseRule *cs2 = sw->cases[i]; @@ -354,9 +370,9 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d if (0) not_simple_parallel_case: is_simple_parallel_case = false; - swpara[sw] = is_simple_parallel_case; + ctx.swpara[sw] = is_simple_parallel_case; } else { - is_simple_parallel_case = swpara.at(sw); + is_simple_parallel_case = ctx.swpara.at(sw); } } @@ -382,28 +398,40 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d for (auto pat : cs2->compare) if (!pat.is_fully_const()) extra_group_for_next_case = true; - else if (!ifxmode) + else if (!ctx.ifxmode) pool.take(pat); } } // mask default bits that are irrelevant because the output is driven by a full case - const pool &full_case_bits = get_full_case_bits(swcache, sw); - for (int i = 0; i < GetSize(sig); i++) - if (full_case_bits.count(sig[i])) + const pool &full_case_bits = get_full_case_bits(ctx.swcache, sw); + for (int i = 0; i < GetSize(ctx.sig); i++) + if (full_case_bits.count(ctx.sig[i])) result[i] = State::Sx; - // evaluate in reverse order to give the first entry the top priority RTLIL::SigSpec initial_val = result; - RTLIL::Cell *last_mux_cell = NULL; + MuxGenCtx mux_gen_ctx {ctx.mod, + sw->signal, + nullptr, + nullptr, + sw, + nullptr, + ctx.ifxmode + }; + // evaluate in reverse order to give the first entry the top priority for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; - 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, cs2, ifxmode); - else - result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, cs2, ifxmode); + MuxTreeContext new_ctx = ctx; + new_ctx.cs = sw->cases[case_idx]; + new_ctx.defval = initial_val; + RTLIL::SigSpec value = signal_to_mux_tree(new_ctx); + mux_gen_ctx.cs = new_ctx.cs; + mux_gen_ctx.compare = &new_ctx.cs->compare; + if (mux_gen_ctx.last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) { + mux_gen_ctx.append_pmux(value); + } else { + result = mux_gen_ctx.gen_mux(value, result); + } } } @@ -429,9 +457,17 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) swcache.current_snippet = idx; RTLIL::SigSpec sig = sigsnip.sigidx[idx]; - log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); + log_debug("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); - RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode); + RTLIL::SigSpec value = signal_to_mux_tree({ + mod, + swcache, + swpara, + &proc->root_case, + sig, + RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), + ifxmode + }); mod->connect(RTLIL::SigSig(sig, value)); } } diff --git a/passes/proc/proc_prune.cc b/passes/proc/proc_prune.cc index 08903d93f..9ad567d4d 100644 --- a/passes/proc/proc_prune.cc +++ b/passes/proc/proc_prune.cc @@ -66,8 +66,8 @@ struct PruneWorker assigned.insert(sw_assigned.begin(), sw_assigned.end()); } for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ) { - RTLIL::SigSpec lhs = sigmap(it->first); - RTLIL::SigSpec rhs = sigmap(it->second); + RTLIL::SigSpec lhs = sigmap(it->lhs); + RTLIL::SigSpec rhs = sigmap(it->rhs); SigSpec new_lhs, new_rhs; SigSpec conn_lhs, conn_rhs; for (int i = 0; i < GetSize(lhs); i++) { @@ -93,8 +93,8 @@ struct PruneWorker removed_count++; it = decltype(cs->actions)::reverse_iterator(cs->actions.erase(it.base() - 1)); } else { - it->first = new_lhs; - it->second = new_rhs; + it->lhs = new_lhs; + it->rhs = new_rhs; it++; } } diff --git a/passes/proc/proc_rom.cc b/passes/proc/proc_rom.cc index a7b485194..07aeacd36 100644 --- a/passes/proc/proc_rom.cc +++ b/passes/proc/proc_rom.cc @@ -58,7 +58,7 @@ struct RomWorker SigSpec lhs; dict lhs_lookup; for (auto &it: sw->cases[0]->actions) { - for (auto bit: it.first) { + for (auto bit: it.lhs) { if (!lhs_lookup.count(bit)) { lhs_lookup[bit] = GetSize(lhs); lhs.append(bit); @@ -87,17 +87,17 @@ struct RomWorker } Const val = Const(State::Sm, GetSize(lhs)); for (auto &it: cs->actions) { - if (!it.second.is_fully_const()) { + if (!it.rhs.is_fully_const()) { log_debug("rejecting switch: rhs not const\n"); return; } - for (int i = 0; i < GetSize(it.first); i++) { - auto it2 = lhs_lookup.find(it.first[i]); + for (int i = 0; i < GetSize(it.lhs); i++) { + auto it2 = lhs_lookup.find(it.lhs[i]); if (it2 == lhs_lookup.end()) { log_debug("rejecting switch: lhs not uniform\n"); return; } - val.set(it2->second, it.second[i].data); + val.set(it2->second, it.rhs[i].data); } } for (auto bit: val) { @@ -193,19 +193,20 @@ struct RomWorker delete cs; sw->cases.clear(); sw->signal = sw->signal.extract(0, swsigbits); + Const action_src = mem.has_attribute(ID::src) ? mem.attributes[ID::src] : Const(""); if (abits == GetSize(sw->signal)) { sw->signal = SigSpec(); RTLIL::CaseRule *cs = new RTLIL::CaseRule; - cs->actions.push_back(SigSig(lhs, rdata)); + cs->actions.push_back({lhs, rdata}); sw->cases.push_back(cs); } else { sw->signal = sw->signal.extract_end(abits); RTLIL::CaseRule *cs = new RTLIL::CaseRule; cs->compare.push_back(Const(State::S0, GetSize(sw->signal))); - cs->actions.push_back(SigSig(lhs, rdata)); + cs->actions.push_back({lhs, rdata}); sw->cases.push_back(cs); RTLIL::CaseRule *cs2 = new RTLIL::CaseRule; - cs2->actions.push_back(SigSig(lhs, default_val)); + cs2->actions.push_back({lhs, default_val}); sw->cases.push_back(cs2); } diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 938ed5355..5fdf9e9ba 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -27,14 +27,6 @@ USING_YOSYS_NAMESPACE YOSYS_NAMESPACE_BEGIN -static void transfer_attr (Cell* to, const Cell* from, const IdString& attr) { - if (from->has_attribute(attr)) - to->attributes[attr] = from->attributes.at(attr); -} -static void transfer_src (Cell* to, const Cell* from) { - transfer_attr(to, from, ID::src); -} - void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort(ID::A); @@ -44,7 +36,7 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::Y, sig_y[i]); } @@ -104,7 +96,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::B, sig_b[i]); gate->setPort(ID::Y, sig_y[i]); @@ -155,7 +147,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) } RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::B, sig_a[i+1]); gate->setPort(ID::Y, sig_t[i/2]); @@ -168,7 +160,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) if (cell->type == ID($reduce_xnor)) { RTLIL::SigSpec sig_t = module->addWire(NEW_ID); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a); gate->setPort(ID::Y, sig_t); last_output_cell = gate; @@ -196,7 +188,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell } RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_OR_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig[i]); gate->setPort(ID::B, sig[i+1]); gate->setPort(ID::Y, sig_t[i/2]); @@ -225,7 +217,7 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) } RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a); gate->setPort(ID::Y, sig_y); } @@ -254,7 +246,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) log_assert(!gate_type.empty()); RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a); gate->setPort(ID::B, sig_b); gate->setPort(ID::Y, sig_y); @@ -270,19 +262,19 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell) RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b))); RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed); - transfer_src(xor_cell, cell); + xor_cell->transfer_src_attribute(cell); simplemap_bitop(module, xor_cell); module->remove(xor_cell); RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID); RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out); - transfer_src(reduce_cell, cell); + reduce_cell->transfer_src_attribute(cell); simplemap_reduce(module, reduce_cell); module->remove(reduce_cell); if (!is_ne) { RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y); - transfer_src(not_cell, cell); + not_cell->transfer_src_attribute(cell); simplemap_lognot(module, not_cell); module->remove(not_cell); } @@ -296,7 +288,7 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::B, sig_b[i]); gate->setPort(ID::S, cell->getPort(ID::S)); @@ -313,7 +305,7 @@ void simplemap_bwmux(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::B, sig_b[i]); gate->setPort(ID::S, sig_s[i]); @@ -329,7 +321,7 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_TBUF_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::E, sig_e); gate->setPort(ID::Y, sig_y[i]); @@ -347,7 +339,7 @@ void simplemap_bmux(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(new_data); i += width) { for (int k = 0; k < width; k++) { RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, data[i*2+k]); gate->setPort(ID::B, data[i*2+width+k]); gate->setPort(ID::S, sel[idx]); @@ -370,7 +362,7 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell) SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2); for (int i = 0; i < GetSize(lut_data); i += 2) { RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); - transfer_src(gate, cell); + gate->transfer_src_attribute(cell); gate->setPort(ID::A, lut_data[i]); gate->setPort(ID::B, lut_data[i+1]); gate->setPort(ID::S, lut_ctrl[idx]); From b5e5554553346fc68dd5736fd8c26c519c5fafc4 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 11:22:33 +0100 Subject: [PATCH 2/9] verilog: fix case location --- frontends/verilog/verilog_parser.y | 1 + 1 file changed, 1 insertion(+) diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index ef8427679..1c1779ae8 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -3026,6 +3026,7 @@ case_item: extra->case_type_stack.pop_back(); SET_AST_NODE_LOC(extra->ast_stack.back(), @4, @4); extra->ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @2); extra->ast_stack.pop_back(); }; From c45a035ebf4c80dcfea6b58eabf52524e47cb3d4 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 11:22:48 +0100 Subject: [PATCH 3/9] gowin: lower LUT count sensitivity --- tests/arch/gowin/mux.ys | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/arch/gowin/mux.ys b/tests/arch/gowin/mux.ys index 2ca973520..e56d2be7b 100644 --- a/tests/arch/gowin/mux.ys +++ b/tests/arch/gowin/mux.ys @@ -18,7 +18,6 @@ proc equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux4 # Constrain all select calls below inside the top module -select -assert-count 4 t:LUT* select -assert-count 2 t:MUX2_LUT5 select -assert-count 1 t:MUX2_LUT6 select -assert-count 6 t:IBUF @@ -32,9 +31,6 @@ proc equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux8 # Constrain all select calls below inside the top module -select -assert-count 3 t:LUT1 -select -assert-count 2 t:LUT3 -select -assert-count 1 t:LUT4 select -assert-count 5 t:MUX2_LUT5 select -assert-count 2 t:MUX2_LUT6 select -assert-count 1 t:MUX2_LUT7 From 304757c881f7a90dc4fe4ea936a7c1531aa022c6 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 11:25:42 +0100 Subject: [PATCH 4/9] rtlil: add source tracking to CaseRule actions --- frontends/ast/genrtlil.cc | 14 +++++++------- frontends/rtlil/rtlil_frontend.cc | 4 ++-- kernel/rtlil.h | 1 + passes/proc/proc_arst.cc | 2 +- passes/proc/proc_dff.cc | 2 +- passes/proc/proc_rom.cc | 6 +++--- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 858714b5b..5c9f17710 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -441,7 +441,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::SigSpec lhs = init_lvalue_c; RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width); remove_unwanted_lvalue_bits(lhs, rhs); - sync->actions.push_back({lhs, rhs}); + sync->actions.push_back({lhs, rhs, Const("")}); offset += lhs.size(); } } @@ -573,7 +573,7 @@ struct AST_INTERNAL::ProcessGenerator if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute(ID::nosync)) rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size()); remove_unwanted_lvalue_bits(lhs, rhs); - actions.push_back({lhs, rhs}); + actions.push_back({lhs, rhs, ast ? ast->loc_string() : ""}); offset += lhs.size(); } } @@ -613,7 +613,7 @@ struct AST_INTERNAL::ProcessGenerator removeSignalFromCaseTree(lvalue, current_case); remove_unwanted_lvalue_bits(lvalue, rvalue); - current_case->actions.push_back({lvalue, rvalue}); + current_case->actions.push_back({lvalue, rvalue, ast->loc_string()}); } break; @@ -729,8 +729,8 @@ struct AST_INTERNAL::ProcessGenerator Wire *en = current_module->addWire(sstr.str() + "_EN", 1); set_src_attr(en, ast); - proc->root_case.actions.push_back({en, SigSpec(false)}); - current_case->actions.push_back({en, SigSpec(true)}); + proc->root_case.actions.push_back({en, SigSpec(false), ast->loc_string()}); + current_case->actions.push_back({en, SigSpec(true), ast->loc_string()}); RTLIL::SigSpec triggers; RTLIL::Const::Builder polarity_builder; @@ -827,8 +827,8 @@ struct AST_INTERNAL::ProcessGenerator Wire *en = current_module->addWire(cellname.str() + "_EN", 1); set_src_attr(en, ast); - proc->root_case.actions.push_back({en, SigSpec(false)}); - current_case->actions.push_back({en, SigSpec(true)}); + proc->root_case.actions.push_back({en, SigSpec(false), ast->loc_string()}); + current_case->actions.push_back({en, SigSpec(true), ast->loc_string()}); RTLIL::SigSpec triggers; RTLIL::Const::Builder polarity_builder; diff --git a/frontends/rtlil/rtlil_frontend.cc b/frontends/rtlil/rtlil_frontend.cc index d5cf84d9c..991f02017 100644 --- a/frontends/rtlil/rtlil_frontend.cc +++ b/frontends/rtlil/rtlil_frontend.cc @@ -624,7 +624,7 @@ struct RTLILFrontendWorker { "The assign statement is reordered to come before all switch statements."); RTLIL::SigSpec s1 = parse_sigspec(); RTLIL::SigSpec s2 = parse_sigspec(); - current_case->actions.push_back({std::move(s1), std::move(s2)}); + current_case->actions.push_back({std::move(s1), std::move(s2), Const("")}); expect_eol(); } else return; @@ -715,7 +715,7 @@ struct RTLILFrontendWorker { if (try_parse_keyword("update")) { RTLIL::SigSpec s1 = parse_sigspec(); RTLIL::SigSpec s2 = parse_sigspec(); - rule->actions.push_back({std::move(s1), std::move(s2)}); + rule->actions.push_back({std::move(s1), std::move(s2), Const("")}); expect_eol(); continue; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 16e80ce60..46477d6b2 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -2241,6 +2241,7 @@ struct RTLIL::SyncAction { RTLIL::SigSpec lhs; RTLIL::SigSpec rhs; + RTLIL::Const src; }; struct RTLIL::SyncRule diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 56938ff7f..105c894ac 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -311,7 +311,7 @@ struct ProcArstPass : public Pass { if (arst_sig.size()) { log("Added global reset to process %s: %s <- %s\n", proc->name.c_str(), log_signal(arst_sig), log_signal(arst_val)); - arst_actions.push_back({arst_sig, arst_val}); + arst_actions.push_back({arst_sig, arst_val, act.src}); } } if (!arst_actions.empty()) { diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index d764a20af..a4c7d927c 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -224,7 +224,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) single_async_rule.type = RTLIL::SyncType::ST1; single_async_rule.signal = mod->ReduceOr(NEW_ID, triggers); // TODO - single_async_rule.actions.push_back({sig, rstval}); + single_async_rule.actions.push_back({sig, rstval, Const("")}); // Replace existing rules with this new rule async_rules.clear(); diff --git a/passes/proc/proc_rom.cc b/passes/proc/proc_rom.cc index 07aeacd36..e6b67abd8 100644 --- a/passes/proc/proc_rom.cc +++ b/passes/proc/proc_rom.cc @@ -197,16 +197,16 @@ struct RomWorker if (abits == GetSize(sw->signal)) { sw->signal = SigSpec(); RTLIL::CaseRule *cs = new RTLIL::CaseRule; - cs->actions.push_back({lhs, rdata}); + cs->actions.push_back({lhs, rdata, std::move(action_src)}); sw->cases.push_back(cs); } else { sw->signal = sw->signal.extract_end(abits); RTLIL::CaseRule *cs = new RTLIL::CaseRule; cs->compare.push_back(Const(State::S0, GetSize(sw->signal))); - cs->actions.push_back({lhs, rdata}); + cs->actions.push_back({lhs, rdata, action_src}); sw->cases.push_back(cs); RTLIL::CaseRule *cs2 = new RTLIL::CaseRule; - cs2->actions.push_back({lhs, default_val}); + cs2->actions.push_back({lhs, default_val, std::move(action_src)}); sw->cases.push_back(cs2); } From d762c5f5e85b8a0be68aa7371753d84191b1b064 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 30 Oct 2025 15:41:46 +0100 Subject: [PATCH 5/9] proc_mux: emit fused action location src attributes on procmuxes --- frontends/ast/genrtlil.cc | 1 + passes/proc/proc_mux.cc | 64 +++++++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 5c9f17710..cf5e6dcb7 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -441,6 +441,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::SigSpec lhs = init_lvalue_c; RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width); remove_unwanted_lvalue_bits(lhs, rhs); + // TODO sync->actions.push_back({lhs, rhs, Const("")}); offset += lhs.size(); } diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 5bef77cd2..9eae29bbd 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -20,6 +20,7 @@ #include "kernel/register.h" #include "kernel/bitpattern.h" #include "kernel/log.h" +#include "kernel/rtlil.h" #include #include #include @@ -27,11 +28,31 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +using SnippetSourceMap = dict, const Const*>; +struct SnippetSourceMapBuilder { + SnippetSourceMap map; + void insert(int snippet, const RTLIL::CaseRule* cs, const RTLIL::SyncAction& action) { + map[std::make_pair(snippet, cs)] = &action.src; + } + +}; +struct SnippetSourceMapper { + SnippetSourceMap map; + void try_map_into(pool& sources, int snippet, const RTLIL::CaseRule* cs) const { + auto src_it = map.find(std::make_pair(snippet, cs)); + if (src_it != map.end()) { + sources.insert(src_it->second->decode_string()); + } + } + +}; + struct SigSnippets { idict sigidx; dict bit2snippet; pool snippets; + SnippetSourceMapBuilder source_builder; void insert(SigSpec sig) { @@ -97,8 +118,11 @@ struct SigSnippets void insert(const RTLIL::CaseRule *cs) { - for (auto &action : cs->actions) + for (auto &action : cs->actions) { insert(action.lhs); + int idx = sigidx(action.lhs); + source_builder.insert(idx, cs, action); + } for (auto sw : cs->switches) for (auto cs2 : sw->cases) @@ -146,8 +170,14 @@ struct SnippetSwCache void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs) { - cell->attributes = sw->attributes; - cell->add_strpool_attribute(ID::src, cs->get_strpool_attribute(ID::src)); + Const old_src; + if (cell->attributes.count(ID::src)) { + std::swap(old_src, cell->attributes[ID::src]); + } + cell->attributes = cs->attributes; + if (old_src.size()) { + std::swap(old_src, cell->attributes[ID::src]); + } } struct MuxGenCtx { @@ -158,6 +188,9 @@ struct MuxGenCtx { RTLIL::SwitchRule *sw; RTLIL::CaseRule *cs; bool ifxmode; + const SnippetSourceMapper& source_mapper; + int current_snippet; + pool& snippet_sources; RTLIL::SigSpec gen_cmp() { std::stringstream sstr; @@ -255,6 +288,8 @@ struct MuxGenCtx { mux_cell->setPort(ID::S, ctrl_sig); mux_cell->setPort(ID::Y, RTLIL::SigSpec(result_wire)); + source_mapper.try_map_into(snippet_sources, current_snippet, cs); + last_mux_cell = mux_cell; return RTLIL::SigSpec(result_wire); } @@ -279,8 +314,9 @@ struct MuxGenCtx { last_mux_cell->setPort(ID::B, new_b); last_mux_cell->parameters[ID::S_WIDTH] = last_mux_cell->getPort(ID::S).size(); - } + source_mapper.try_map_into(snippet_sources, current_snippet, cs); + } }; const pool &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRule *sw) @@ -328,6 +364,7 @@ const pool &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul struct MuxTreeContext { RTLIL::Module* mod; SnippetSwCache& swcache; + const SnippetSourceMapper& source_mapper; dict &swpara; RTLIL::CaseRule *cs; const RTLIL::SigSpec &sig; @@ -351,6 +388,7 @@ RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx) // detect groups of parallel cases std::vector pgroups(sw->cases.size()); + pool case_sources; bool is_simple_parallel_case = true; if (!sw->get_bool_attribute(ID::parallel_case)) { @@ -402,7 +440,13 @@ RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx) pool.take(pat); } } - + // Create sources for default cases + for (auto cs2 : sw -> cases) { + if (cs2->compare.empty()) { + int sn = ctx.swcache.current_snippet; + ctx.source_mapper.try_map_into(case_sources, sn, cs2); + } + } // mask default bits that are irrelevant because the output is driven by a full case const pool &full_case_bits = get_full_case_bits(ctx.swcache, sw); for (int i = 0; i < GetSize(ctx.sig); i++) @@ -416,7 +460,10 @@ RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx) nullptr, sw, nullptr, - ctx.ifxmode + ctx.ifxmode, + ctx.source_mapper, + ctx.swcache.current_snippet, + case_sources }; // evaluate in reverse order to give the first entry the top priority for (size_t i = 0; i < sw->cases.size(); i++) { @@ -433,6 +480,9 @@ RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx) result = mux_gen_ctx.gen_mux(value, result); } } + if (mux_gen_ctx.last_mux_cell) { + mux_gen_ctx.last_mux_cell->set_strpool_attribute(ID::src, case_sources); + } } return result; @@ -459,9 +509,11 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) log_debug("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); + const SnippetSourceMapper mapper{sigsnip.source_builder.map}; RTLIL::SigSpec value = signal_to_mux_tree({ mod, swcache, + mapper, swpara, &proc->root_case, sig, From 2db4208ca5038156872e75216feb77cdf9331c8f Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 11:09:01 +0100 Subject: [PATCH 6/9] proc_mux: refactor --- passes/proc/proc_mux.cc | 57 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 9eae29bbd..7d8b2ed57 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -37,7 +37,7 @@ struct SnippetSourceMapBuilder { }; struct SnippetSourceMapper { - SnippetSourceMap map; + const SnippetSourceMap map; void try_map_into(pool& sources, int snippet, const RTLIL::CaseRule* cs) const { auto src_it = map.find(std::make_pair(snippet, cs)); if (src_it != map.end()) { @@ -298,8 +298,10 @@ struct MuxGenCtx { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort(ID::A).size()); - if (when_signal == last_mux_cell->getPort(ID::A)) + if (when_signal == last_mux_cell->getPort(ID::A)) { + // when_signal already covered by the default value at port A return; + } RTLIL::SigSpec ctrl_sig = gen_cmp(); log_assert(ctrl_sig.size() == 1); @@ -372,6 +374,31 @@ struct MuxTreeContext { const bool ifxmode; }; +bool is_simple_parallel_case(RTLIL::SwitchRule* sw, dict &swpara) +{ + bool ret = true; + if (!sw->get_bool_attribute(ID::parallel_case)) { + if (!swpara.count(sw)) { + pool case_values; + for (size_t i = 0; i < sw->cases.size(); i++) { + RTLIL::CaseRule *cs2 = sw->cases[i]; + for (auto pat : cs2->compare) { + if (!pat.is_fully_def()) + return false; + Const cpat = pat.as_const(); + if (case_values.count(cpat)) + return false; + case_values.insert(cpat); + } + } + swpara[sw] = ret; + } else { + return swpara.at(sw); + } + } + return ret; +} + RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx) { RTLIL::SigSpec result = ctx.defval; @@ -389,32 +416,8 @@ RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx) // detect groups of parallel cases std::vector pgroups(sw->cases.size()); pool case_sources; - bool is_simple_parallel_case = true; - if (!sw->get_bool_attribute(ID::parallel_case)) { - if (!ctx.swpara.count(sw)) { - pool case_values; - for (size_t i = 0; i < sw->cases.size(); i++) { - RTLIL::CaseRule *cs2 = sw->cases[i]; - for (auto pat : cs2->compare) { - if (!pat.is_fully_def()) - goto not_simple_parallel_case; - Const cpat = pat.as_const(); - if (case_values.count(cpat)) - goto not_simple_parallel_case; - case_values.insert(cpat); - } - } - if (0) - not_simple_parallel_case: - is_simple_parallel_case = false; - ctx.swpara[sw] = is_simple_parallel_case; - } else { - is_simple_parallel_case = ctx.swpara.at(sw); - } - } - - if (!is_simple_parallel_case) { + if (!is_simple_parallel_case(sw, ctx.swpara)) { BitPatternPool pool(sw->signal.size()); bool extra_group_for_next_case = false; for (size_t i = 0; i < sw->cases.size(); i++) { From f9c528e981e4afcaba437e793df686a3796ee4a2 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 11:09:14 +0100 Subject: [PATCH 7/9] docs: word_mux grammar --- docs/source/cell/word_mux.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/cell/word_mux.rst b/docs/source/cell/word_mux.rst index 234d1016b..52d1372d3 100644 --- a/docs/source/cell/word_mux.rst +++ b/docs/source/cell/word_mux.rst @@ -16,7 +16,7 @@ value from the ``B`` input is sent to the output. So the `$mux` cell implements the function :verilog:`Y = S ? B : A`. The `$pmux` cell is used to multiplex between many inputs using a one-hot select -signal. Cells of this type have a ``WIDTH`` and a ``S_WIDTH`` parameter and +signal. Cells of this type have a ``WIDTH`` and an ``S_WIDTH`` parameter and inputs ``A``, ``B``, and ``S`` and an output ``Y``. The ``S`` input is ``S_WIDTH`` bits wide. The ``A`` input and the output are both ``WIDTH`` bits wide and the ``B`` input is ``WIDTH*S_WIDTH`` bits wide. When all bits of ``S`` From 0c8e008ce7497c4c891832764e4bc6d9232c6205 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 11:10:08 +0100 Subject: [PATCH 8/9] proc_mux: add src test --- tests/proc/proc_mux_src.v | 92 ++++++++++++++++++++++++++++++++++++++ tests/proc/proc_mux_src.ys | 33 ++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 tests/proc/proc_mux_src.v create mode 100644 tests/proc/proc_mux_src.ys diff --git a/tests/proc/proc_mux_src.v b/tests/proc/proc_mux_src.v new file mode 100644 index 000000000..17d6da104 --- /dev/null +++ b/tests/proc/proc_mux_src.v @@ -0,0 +1,92 @@ +module nested( + input clk, + input [7:0] A, + input [7:0] B, + input [3:0] mode1, + input [3:0] mode2, + output reg [7:0] result1, + output reg [7:0] result2, + output reg [1:0] arith +); + + localparam OP_A = 4'b0000; + localparam OP_BA = 4'b0001; + localparam OP_BB = 4'b0010; + localparam OP_C = 4'b0011; + + always @(posedge clk) + begin + case (mode1) + OP_A: begin + result1 = A + B; + result2 = A - B; + arith = 2'b01; + end + OP_BA , OP_BB : begin + result1 = A * B; + result2 = A / B; + arith = 2'b00; + end + OP_C : begin + arith = 2'b10; + case (mode2) + OP_A: begin + result1 = ~B; + result2 = B; + end + OP_C: begin + result1 = A ^ B; + result2 = A == B; + end + default: begin + result1 = 1'b0; + // result2 omitted + end + endcase + end + default: begin + result1 = 8'b0; + result2 = 8'b0; + arith = 2'b11; + end + endcase + end +endmodule + +module tiny( + input clk, + input ya, + input [7:0] in, + output reg [7:0] out, +); + always @(posedge clk) + begin + case (ya) + 1'b1: begin + out = in; + end + endcase + end +endmodule + +module tiny2( + input clk, + input [1:0] ya, + input [7:0] in, + output reg [7:0] out, +); + always @(posedge clk) + begin + case (ya) + 2'b01: begin + out = in; + end + 2'b10: begin + out = 1'b1; + end + default begin + out = 1'b0; + end + endcase + end +endmodule diff --git a/tests/proc/proc_mux_src.ys b/tests/proc/proc_mux_src.ys new file mode 100644 index 000000000..e25858e26 --- /dev/null +++ b/tests/proc/proc_mux_src.ys @@ -0,0 +1,33 @@ +read_verilog proc_mux_src.v +proc -noopt +check -assert +# eq refer to the values compared against +select -assert-count 2 tiny2/t:$eq +select -assert-count 1 tiny2/t:$eq a:src=proc_mux_src.v:81.4-81.10 %i +select -assert-count 1 tiny2/t:$eq a:src=proc_mux_src.v:84.4-84.10 %i +# Flops cover the whole process +select -assert-count 1 tiny2/t:$dff +select -assert-count 1 tiny2/t:$dff a:src=proc_mux_src.v:78.2-91.5 %i +# Muxes are marked to the exact assignment statements they represent including the explicit default case +select -assert-count 1 tiny2/t:$pmux +select -assert-count 1 tiny2/t:$pmux a:src=proc_mux_src.v:80.5-80.13|proc_mux_src.v:83.5-83.15|proc_mux_src.v:86.5-86.15 +select -assert-count 0 tiny/t:$reduce_or + +# Implicit default cases add src attributes to muxes that cover the whole switch +select -assert-count 1 tiny/t:$mux +select -assert-count 1 tiny/t:$mux a:proc_mux_src.v:65.5-65.13|proc_mux_src.v:63.3-67.10 +select -assert-count 0 tiny/t:$reduce_or + +dump nested +#dump nested/t:$pmux +# $reduce_or src covers the entire list of comparison RHSs +# Each snippet is treated separately so it gets its own $eq and $reduce_or etc +select -assert-count 3 nested/t:$reduce_or +select -assert-count 3 nested/t:$reduce_or a:src=proc_mux_src.v:25.4-25.19 %i +# When switches are nested, the top mux considers the inner switch the entire source +# for one of its inputs. Here, that's proc_mux_src.v:32.5-45.12 +select -assert-count 5 nested/t:$pmux +select -assert-count 1 nested/t:$pmux a:src=proc_mux_src.v:21.5-21.20|proc_mux_src.v:26.5-26.20|proc_mux_src.v:32.5-45.12|proc_mux_src.v:48.5-48.19 %i +# No nesting for output reg arith +select -assert-count 1 nested/t:$pmux a:src=proc_mux_src.v:23.5-23.18|proc_mux_src.v:28.5-28.18|proc_mux_src.v:31.5-31.18|proc_mux_src.v:50.5-50.18 %i +dump nested/t:$pmux From 26e293d71f00a1174aad493f9c13357efbf275dc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Sun, 2 Nov 2025 12:41:55 +0100 Subject: [PATCH 9/9] proc_mux: default to case src when action src is missing --- passes/proc/proc_mux.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 7d8b2ed57..f0433d6b8 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -32,7 +32,8 @@ using SnippetSourceMap = dict, const Cons struct SnippetSourceMapBuilder { SnippetSourceMap map; void insert(int snippet, const RTLIL::CaseRule* cs, const RTLIL::SyncAction& action) { - map[std::make_pair(snippet, cs)] = &action.src; + if (action.src.size()) + map[std::make_pair(snippet, cs)] = &action.src; } }; @@ -42,6 +43,11 @@ struct SnippetSourceMapper { auto src_it = map.find(std::make_pair(snippet, cs)); if (src_it != map.end()) { sources.insert(src_it->second->decode_string()); + } else { + auto cs_src = cs->get_src_attribute(); + if (cs_src.size()) { + sources.insert(cs_src); + } } } @@ -168,7 +174,7 @@ struct SnippetSwCache } }; -void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs) +void apply_attrs(RTLIL::Cell *cell, const RTLIL::CaseRule *cs) { Const old_src; if (cell->attributes.count(ID::src)) { @@ -220,7 +226,7 @@ struct MuxGenCtx { { // create compare cell RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str(), cmp_wire->width), ifxmode ? ID($eqx) : ID($eq)); - apply_attrs(eq_cell, sw, cs); + apply_attrs(eq_cell, cs); eq_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0); eq_cell->parameters[ID::B_SIGNED] = RTLIL::Const(0); @@ -246,7 +252,7 @@ struct MuxGenCtx { // reduce cmp vector to one logic signal RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", ID($reduce_or)); - apply_attrs(any_cell, sw, cs); + apply_attrs(any_cell, cs); any_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0); any_cell->parameters[ID::A_WIDTH] = RTLIL::Const(cmp_wire->width); @@ -280,7 +286,6 @@ struct MuxGenCtx { // create the multiplexer itself RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), ID($mux)); - apply_attrs(mux_cell, sw, cs); mux_cell->parameters[ID::WIDTH] = RTLIL::Const(when_signal.size()); mux_cell->setPort(ID::A, else_signal);