Add doc
[yosys.git] / passes / opt / muxpack.cc
index cb13a45b0f9f9fe9ce7d4a3d6c9a216dcabaaed5..c8b226c78e916a8ecc76582437facc570069de5e 100644 (file)
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
+struct ExclusiveDatabase
+{
+       Module *module;
+       const SigMap &sigmap;
+
+       dict<SigBit, std::pair<SigSpec,Const>> sig_cmp_prev;
+
+       ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+       {
+               SigSpec const_sig, nonconst_sig, y_port;
+               for (auto cell : module->cells()) {
+                       if (cell->type == "$eq") {
+                               nonconst_sig = sigmap(cell->getPort("\\A"));
+                               const_sig = sigmap(cell->getPort("\\B"));
+                               if (!const_sig.is_fully_const()) {
+                                       if (!nonconst_sig.is_fully_const())
+                                               continue;
+                                       std::swap(nonconst_sig, const_sig);
+                               }
+                               y_port = sigmap(cell->getPort("\\Y"));
+                       }
+                       else if (cell->type == "$logic_not") {
+                               nonconst_sig = sigmap(cell->getPort("\\A"));
+                               const_sig = Const(RTLIL::S0, GetSize(nonconst_sig));
+                               y_port = sigmap(cell->getPort("\\Y"));
+                       }
+                       else continue;
+
+                        log_assert(!nonconst_sig.empty());
+                        log_assert(!const_sig.empty());
+                        sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,const_sig.as_const());
+                }
+        }
+
+        bool query(const SigSpec &sig) const
+        {
+                SigSpec nonconst_sig;
+                pool<Const> const_values;
+
+                for (auto bit : sig.bits()) {
+                        auto it = sig_cmp_prev.find(bit);
+                        if (it == sig_cmp_prev.end())
+                                return false;
+
+                        if (nonconst_sig.empty())
+                                nonconst_sig = it->second.first;
+                        else if (nonconst_sig != it->second.first)
+                                return false;
+
+                        if (!const_values.insert(it->second.second).second)
+                                return false;
+                }
+
+               return true;
+       }
+};
+
+
 struct MuxpackWorker
 {
        Module *module;
@@ -37,6 +95,9 @@ struct MuxpackWorker
        dict<SigSpec, Cell*> sig_chain_prev;
        pool<SigBit> sigbit_with_non_chain_users;
        pool<Cell*> chain_start_cells;
+       pool<Cell*> candidate_cells;
+
+       ExclusiveDatabase excl_db;
 
        void make_sig_chain_next_prev()
        {
@@ -50,23 +111,31 @@ struct MuxpackWorker
 
                for (auto cell : module->cells())
                {
-                       if (cell->type.in("$mux") && !cell->get_bool_attribute("\\keep"))
+                       if (cell->type.in("$mux", "$pmux") && !cell->get_bool_attribute("\\keep"))
                        {
                                SigSpec a_sig = sigmap(cell->getPort("\\A"));
-                               SigSpec b_sig = sigmap(cell->getPort("\\B"));
+                               SigSpec b_sig;
+                               if (cell->type == "$mux")
+                                       b_sig = sigmap(cell->getPort("\\B"));
                                SigSpec y_sig = sigmap(cell->getPort("\\Y"));
    
                                if (sig_chain_next.count(a_sig))
                                        for (auto a_bit : a_sig.bits())
                                                sigbit_with_non_chain_users.insert(a_bit);
-                               else
+                               else {
                                        sig_chain_next[a_sig] = cell;
+                                       candidate_cells.insert(cell);
+                               }
 
-                               if (sig_chain_next.count(b_sig))
-                                       for (auto b_bit : b_sig.bits())
-                                               sigbit_with_non_chain_users.insert(b_bit);
-                               else
-                                       sig_chain_next[b_sig] = cell;
+                               if (!b_sig.empty()) {
+                                       if (sig_chain_next.count(b_sig))
+                                               for (auto b_bit : b_sig.bits())
+                                                       sigbit_with_non_chain_users.insert(b_bit);
+                                       else {
+                                               sig_chain_next[b_sig] = cell;
+                                               candidate_cells.insert(cell);
+                                       }
+                               }
 
                                sig_chain_prev[y_sig] = cell;
                                continue;
@@ -81,35 +150,42 @@ struct MuxpackWorker
 
        void find_chain_start_cells()
        {
-               for (auto it : sig_chain_next)
+               for (auto cell : candidate_cells)
                {
-                       SigSpec next_sig = it.second->getPort("\\A");
-                       if (sig_chain_prev.count(next_sig) == 0) {
-                               next_sig = it.second->getPort("\\B");
-                               if (sig_chain_prev.count(next_sig) == 0)
-                                       next_sig = SigSpec();
-                       }
+                       log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type));
 
-                       for (auto bit : next_sig.bits())
-                               if (sigbit_with_non_chain_users.count(bit))
+                       SigSpec a_sig = sigmap(cell->getPort("\\A"));
+                       if (cell->type == "$mux") {
+                               SigSpec b_sig = sigmap(cell->getPort("\\B"));
+                               if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1)
                                        goto start_cell;
 
-                       if (!next_sig.empty())
-                       {
-                               Cell *c1 = sig_chain_prev.at(next_sig);
-                               Cell *c2 = it.second;
-
-                               if (c1->type != c2->type)
+                               if (!sig_chain_prev.count(a_sig))
+                                       a_sig = b_sig;
+                       }
+                       else if (cell->type == "$pmux") {
+                               if (!sig_chain_prev.count(a_sig))
                                        goto start_cell;
+                       }
+                       else log_abort();
 
-                               if (c1->parameters != c2->parameters)
+                       for (auto bit : a_sig.bits())
+                               if (sigbit_with_non_chain_users.count(bit))
                                        goto start_cell;
 
-                               continue;
+                       {
+                               Cell *prev_cell = sig_chain_prev.at(a_sig);
+                               log_assert(prev_cell);
+                               SigSpec s_sig = sigmap(cell->getPort("\\S"));
+                               s_sig.append(sigmap(prev_cell->getPort("\\S")));
+                               if (!excl_db.query(s_sig))
+                                       goto start_cell;
                        }
 
+                       continue;
+
                start_cell:
-                       chain_start_cells.insert(it.second);
+                       chain_start_cells.insert(cell);
                }
        }
 
@@ -173,6 +249,7 @@ struct MuxpackWorker
                                        s_sig.append(cursor_cell->getPort("\\S"));
                                }
                                else {
+                                       log_assert(cursor_cell->type == "$mux");
                                        b_sig.append(cursor_cell->getPort("\\A"));
                                        s_sig.append(module->LogicNot(NEW_ID, cursor_cell->getPort("\\S")));
                                }
@@ -197,10 +274,11 @@ struct MuxpackWorker
                sig_chain_next.clear();
                sig_chain_prev.clear();
                chain_start_cells.clear();
+               candidate_cells.clear();
        }
 
        MuxpackWorker(Module *module) :
-                       module(module), sigmap(module), mux_count(0), pmux_count(0)
+                       module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap)
        {
                make_sig_chain_next_prev();
                find_chain_start_cells();
@@ -215,15 +293,20 @@ struct MuxpackWorker
 };
 
 struct MuxpackPass : public Pass {
-       MuxpackPass() : Pass("muxpack", "$mux cell cascades to $pmux") { }
+       MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { }
        void help() YS_OVERRIDE
        {
                //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
                log("\n");
                log("    muxpack [selection]\n");
                log("\n");
-               log("This pass converts cascaded chains of $mux cells (e.g. those created by if-else\n");
-               log("constructs) into $pmux cells.\n");
+               log("This pass converts cascaded chains of $pmux cells (e.g. those create from case\n");
+               log("constructs) and $mux cells (e.g. those created by if-else constructs) into\n");
+               log("$pmux cells.\n");
+               log("\n");
+               log("This optimisation is conservative --- it will only pack $mux or $pmux cells\n");
+               log("whose select lines are driven by '$eq' cells with other such cells if it can be\n");
+               log("certain that their select inputs are mutually exclusive.\n");
                log("\n");
        }
        void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE