fsm_detect: Add a cache to avoid excessive CPU usage for big mux networks.
authorMarcin Kościelnicki <mwk@0x04.net>
Tue, 14 Jan 2020 21:48:40 +0000 (22:48 +0100)
committerMarcin Kościelnicki <mwk@0x04.net>
Tue, 14 Jan 2020 21:49:20 +0000 (22:49 +0100)
Fixes #1634.

passes/fsm/fsm_detect.cc

index fb3896669734545bd1d40d23bed05479b70320f6..a1c8067b47b6f8b7a35a95af48372d8ad89af827 100644 (file)
@@ -34,13 +34,20 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
 static std::set<RTLIL::Cell*> muxtree_cells;
 static SigPool sig_at_port;
 
-static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor)
+static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor, dict<RTLIL::SigSpec, bool> &mux_tree_cache)
 {
+       if (mux_tree_cache.find(sig) != mux_tree_cache.end())
+               return mux_tree_cache.at(sig);
+
        if (sig.is_fully_const() || old_sig == sig) {
+ret_true:
+               mux_tree_cache[sig] = true;
                return true;
        }
 
        if (sig_at_port.check_any(assign_map(sig))) {
+ret_false:
+               mux_tree_cache[sig] = false;
                return false;
        }
 
@@ -49,13 +56,13 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
        for (auto &cellport : cellport_list)
        {
                if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
-                       return false;
+                       goto ret_false;
                }
 
                if (recursion_monitor.count(cellport.first)) {
                        log_warning("logic loop in mux tree at signal %s in module %s.\n",
                                        log_signal(sig), RTLIL::id2cstr(module->name));
-                       return false;
+                       goto ret_false;
                }
 
                recursion_monitor.insert(cellport.first);
@@ -63,22 +70,22 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
                RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
                RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
 
-               if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) {
+               if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor, mux_tree_cache)) {
                        recursion_monitor.erase(cellport.first);
-                       return false;
+                       goto ret_false;
                }
 
                for (int i = 0; i < sig_b.size(); i += sig_a.size())
-                       if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) {
+                       if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor, mux_tree_cache)) {
                                recursion_monitor.erase(cellport.first);
-                               return false;
+                               goto ret_false;
                        }
 
                recursion_monitor.erase(cellport.first);
                muxtree_cells.insert(cellport.first);
        }
 
-       return true;
+       goto ret_true;
 }
 
 static bool check_state_users(RTLIL::SigSpec sig)
@@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire)
                pool<Cell*> recursion_monitor;
                RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
                RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
+               dict<RTLIL::SigSpec, bool> mux_tree_cache;
 
                if (sig_q != assign_map(wire))
                        continue;
 
-               looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor);
+               looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor, mux_tree_cache);
                looks_like_good_state_reg = check_state_users(sig_q);
 
                if (!looks_like_state_reg)