opt_lut: Allow more than one -dlogic per cell type.
authorMarcelina Kościelnicka <mwk@0x04.net>
Thu, 29 Jul 2021 14:55:15 +0000 (16:55 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Thu, 29 Jul 2021 15:30:07 +0000 (17:30 +0200)
Fixes #2061.

passes/opt/opt_lut.cc
techlibs/ice40/synth_ice40.cc
tests/arch/ice40/bug2061.ys [new file with mode: 0644]

index 62310101645edb288682e6b9456b2eae6afe194b..3b079d964f4e5d8704607775320f753f71339a61 100644 (file)
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
+struct dlogic_t {
+       IdString cell_type;
+       // LUT input idx -> hard cell's port name
+       dict<int, IdString> lut_input_port;
+};
+
 struct OptLutWorker
 {
-       dict<IdString, dict<int, IdString>> &dlogic;
+       const std::vector<dlogic_t> &dlogic;
        RTLIL::Module *module;
        ModIndex index;
        SigMap sigmap;
 
        pool<RTLIL::Cell*> luts;
        dict<RTLIL::Cell*, int> luts_arity;
-       dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics;
+       dict<RTLIL::Cell*, pool<std::pair<int, RTLIL::Cell*>>> luts_dlogics;
        dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs;
 
        int eliminated_count = 0, combined_count = 0;
@@ -64,7 +70,7 @@ struct OptLutWorker
        void show_stats_by_arity()
        {
                dict<int, int> arity_counts;
-               dict<IdString, int> dlogic_counts;
+               std::vector<int> dlogic_counts(dlogic.size());
                int max_arity = 0;
 
                for (auto lut_arity : luts_arity)
@@ -77,7 +83,7 @@ struct OptLutWorker
                {
                        for (auto &lut_dlogic : lut_dlogics.second)
                        {
-                               dlogic_counts[lut_dlogic->type]++;
+                               dlogic_counts[lut_dlogic.first]++;
                        }
                }
 
@@ -87,13 +93,13 @@ struct OptLutWorker
                        if (arity_counts[arity])
                                log("  %d-LUT %16d\n", arity, arity_counts[arity]);
                }
-               for (auto &dlogic_count : dlogic_counts)
+               for (int i = 0; i < GetSize(dlogic); i++)
                {
-                       log("  with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second);
+                       log("  with %-12s (#%d) %4d\n", dlogic[i].cell_type.c_str(), i, dlogic_counts[i]);
                }
        }
 
-       OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) :
+       OptLutWorker(const std::vector<dlogic_t> &dlogic, RTLIL::Module *module, int limit) :
                dlogic(dlogic), module(module), index(module), sigmap(module)
        {
                log("Discovering LUTs.\n");
@@ -116,20 +122,19 @@ struct OptLutWorker
 
                                // First, find all dedicated logic we're connected to. This results in an overapproximation
                                // of such connections.
-                               pool<RTLIL::Cell*> lut_all_dlogics;
+                               pool<std::pair<int, RTLIL::Cell*>> lut_all_dlogics;
                                for (int i = 0; i < lut_width; i++)
                                {
                                        SigBit bit = lut_input[i];
                                        for (auto &port : index.query_ports(bit))
                                        {
-                                               if (dlogic.count(port.cell->type))
+                                               for (int j = 0; j < GetSize(dlogic); j++)
                                                {
-                                                       auto &dlogic_map = dlogic[port.cell->type];
-                                                       if (dlogic_map.count(i))
+                                                       if (dlogic[j].cell_type == port.cell->type)
                                                        {
-                                                               if (port.port == dlogic_map[i])
+                                                               if (port.port == dlogic[j].lut_input_port.at(i, IdString()))
                                                                {
-                                                                       lut_all_dlogics.insert(port.cell);
+                                                                       lut_all_dlogics.insert({j, port.cell});
                                                                }
                                                        }
                                                }
@@ -143,25 +148,25 @@ struct OptLutWorker
                                //   * The connection is illegal.
                                // In either of these cases, we don't need to concern ourselves with preserving the connection
                                // between this LUT and this dedicated logic cell.
-                               pool<RTLIL::Cell*> lut_legal_dlogics;
+                               pool<std::pair<int, RTLIL::Cell*>> lut_legal_dlogics;
                                pool<int> lut_dlogic_inputs;
                                for (auto lut_dlogic : lut_all_dlogics)
                                {
-                                       auto &dlogic_map = dlogic[lut_dlogic->type];
+                                       auto &dlogic_map = dlogic[lut_dlogic.first].lut_input_port;
                                        bool legal = true;
                                        for (auto &dlogic_conn : dlogic_map)
                                        {
                                                if (lut_width <= dlogic_conn.first)
                                                {
-                                                       log_debug("  LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+                                                       log_debug("  LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
                                                        log_debug("    LUT input A[%d] not present.\n", dlogic_conn.first);
                                                        legal = false;
                                                        break;
                                                }
-                                               if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second)))
+                                               if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)))
                                                {
-                                                       log_debug("  LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
-                                                       log_debug("    LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second)));
+                                                       log_debug("  LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
+                                                       log_debug("    LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second)));
                                                        legal = false;
                                                        break;
                                                }
@@ -169,7 +174,7 @@ struct OptLutWorker
 
                                        if (legal)
                                        {
-                                               log_debug("  LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+                                               log_debug("  LUT has legal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
                                                lut_legal_dlogics.insert(lut_dlogic);
                                                for (auto &dlogic_conn : dlogic_map)
                                                        lut_dlogic_inputs.insert(dlogic_conn.first);
@@ -544,7 +549,7 @@ struct OptLutPass : public Pass {
        {
                log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
 
-               dict<IdString, dict<int, IdString>> dlogic;
+               std::vector<dlogic_t> dlogic;
                int limit = -1;
 
                size_t argidx;
@@ -556,7 +561,8 @@ struct OptLutPass : public Pass {
                                split(tokens, args[++argidx], ':');
                                if (tokens.size() < 2)
                                        log_cmd_error("The -dlogic option requires at least one connection.\n");
-                               IdString type = "\\" + tokens[0];
+                               dlogic_t entry;
+                               entry.cell_type = "\\" + tokens[0];
                                for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
                                        std::vector<std::string> conn_tokens;
                                        split(conn_tokens, *it, '=');
@@ -564,8 +570,9 @@ struct OptLutPass : public Pass {
                                                log_cmd_error("Invalid format of -dlogic signal mapping.\n");
                                        IdString logic_port = "\\" + conn_tokens[0];
                                        int lut_input = atoi(conn_tokens[1].c_str());
-                                       dlogic[type][lut_input] = logic_port;
+                                       entry.lut_input_port[lut_input] = logic_port;
                                }
+                               dlogic.push_back(entry);
                                continue;
                        }
                        if (args[argidx] == "-limit" && argidx + 1 < args.size())
index 3c84ea42620775458c6d4d8be0c8f5a55d67df5d..421ec3b4e2edc36256ec12c3f7b319aeba44a89a 100644 (file)
@@ -399,7 +399,7 @@ struct SynthIce40Pass : public ScriptPass
                        run("ice40_wrapcarry -unwrap");
                        run("techmap -map +/ice40/ff_map.v");
                        run("clean");
-                       run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3");
+                       run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3 -dlogic SB_CARRY:CO=3");
                }
 
                if (check_label("map_cells"))
diff --git a/tests/arch/ice40/bug2061.ys b/tests/arch/ice40/bug2061.ys
new file mode 100644 (file)
index 0000000..7dd7ee6
--- /dev/null
@@ -0,0 +1,24 @@
+read_verilog <<EOT
+module top #(
+    parameter integer WIDTH = 12
+)(
+    output reg  [WIDTH:0] cnt,
+    input  wire clk,
+    input  wire rst
+);
+    wire last_n;
+
+    assign last_n = cnt[WIDTH];
+
+    always @(posedge clk or posedge rst)
+        if (rst)
+            cnt <= 0;
+        else
+            cnt <= last_n ? ( cnt + { (WIDTH+1){last_n} } ) : 13'h1aaa;
+
+endmodule
+EOT
+
+synth_ice40
+splitnets
+select -assert-count 12 t:SB_CARRY %co:+[CO] t:SB_LUT4 %ci:+[I3] %i